The Error Iam getting is : data: { error: 'jwt must be provided' }
When User logIn by providing the correct email and password then the user is redirected to "/" page the logic is written in middleware.ts you can see in this code.But using react query is showing error why like normal axios.get will work but this doesnot work. I need tanstack query in my project so tried to use it in this UserDetails.jsx to show the user information that is stored in login/route.ts
UserDetails.tsx (This code doesnot work):
"use client";
interface Data {
id: string;
username: string;
email: string;
}
export default function UserDetails() {
const { data } = useQuery({
queryFn: async () => {
const { data } = await axios.get("http://localhost:3000/api/me");
return data as Data;
},
});
return (
<div>
<h1>{JSON.stringify(data)}</h1>
</div>
);
}
This is login api that takes email and password and validates and after validating it generates jwt token and stores it in httponly cookie
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const schema = z.object({
email: z.string().email({ message: "Invalid Email !!" }),
password: z.string(),
});
if (body.email === "" && body.password === "") {
return NextResponse.json(
{ error: "Please fill all the fields !!" },
{ status: 400 }
);
}
if (body.email === "") {
return NextResponse.json(
{ error: "Email is required !!" },
{ status: 400 }
);
}
if (body.password === "") {
return NextResponse.json(
{ error: "Password is required !!" },
{ status: 400 }
);
}
const validatedData = schema.parse(body);
const { email, password } = validatedData;
const user = await FindByEmail(email);
if (!user) {
return NextResponse.json(
{ error: "User doesn't Exists !!" },
{ status: 400 }
);
}
const passwordsMatched = await user.matchPasswords(password);
if (!passwordsMatched) {
return NextResponse.json(
{ error: "Password is incorrect !!" },
{ status: 400 }
);
}
const tokenData = {
id: user._id,
username: user.username,
email: user.email,
};
const token = await jwt.sign(tokenData, process.env.JWT_SECRET!, {
expiresIn: "1d",
});
const response = NextResponse.json(
{
message: "Login Successful",
},
{ status: 200, statusText: "set cookie" }
);
response.cookies.set("jwt", token, {
secure: true,
httpOnly: process.env.NODE_ENV !== "development",
sameSite: "strict",
maxAge: 1 * 24 * 60 * 60 * 1000,
});
return response;
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ error: error.flatten() }, { status: 400 });
} else {
return NextResponse.json(
{
error: "Something went wrong !!" + error,
},
{ status: 400 }
);
}
}
}
This api is used to get the userInformation
api/me/route.ts :
connect();
export async function GET(request: NextRequest) {
try {
const userId = await getDataFromToken(request);
const user = await User.findOne({ _id: userId }).select("-password");
return NextResponse.json({
message: "User Found",
data: user,
});
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 400 });
}
}
This is used to get the id from jwt token as id is stored in jwt along with username and email
getDataFromToken.ts :
import { NextRequest } from "next/server";
import jwt from "jsonwebtoken";
export const getDataFromToken = (request: NextRequest) => {
try {
const token = request.cookies.get("jwt")?.value || "";
const decodedToken: any = jwt.verify(token, process.env.JWT_SECRET!);
return decodedToken.id;
} catch (error: any) {
throw new Error(error.message);
}
};
This is the middleware.ts provided by NextJs which checks the cookies have jwt token or not and if that token is valid then redirects to "/" page otherwise not
middleware.ts:
import { verifyJwtToken } from "@/libs/verifyToken";
import { NextResponse } from "next/server";
import { NextRequest } from "next/server";
export async function middleware(request: NextRequest) {
const token = request.cookies.get("jwt")?.value;
const path = request.nextUrl.pathname;
const PublicPath = path === "/login" || path === "/register";
try {
const verifiedToken =
token &&
(await verifyJwtToken(token).catch((e) => {
throw new Error(e);
}));
if (PublicPath && token && verifiedToken) {
return NextResponse.redirect(new URL("/", request.nextUrl));
}
if (!PublicPath && (!token || !verifiedToken)) {
return NextResponse.redirect(new URL("/login", request.nextUrl));
}
} catch (error) {
return NextResponse.redirect(new URL("/login", request.nextUrl));
}
}
export const config = {
matcher: ["/login", "/register", "/"],
};
This code is used to verify the jwt token if it is valid or not using jose and if jwt is valid then send the payload associated with it.
verifyToken.ts:
import { jwtVerify } from "jose";
export const getJwtSecretKey = () => {
const secret = process.env.JWT_SECRET!;
if (!secret || secret.length === 0) {
throw new Error("The environment variable JWT_SECRET is not set.");
}
return secret;
};
export async function verifyJwtToken(token: string) {
try {
if (!token) {
return;
}
const verified = await jwtVerify(
token,
new TextEncoder().encode(getJwtSecretKey())
);
return verified.payload;
} catch (error: any) {
throw new Error("Your token is expired");
}
}
But This code works :
"use client";
import axios from "axios";
import { useEffect, useState } from "react";
export default function UserDetails() {
const [userData, setUserData] = useState({
name: "",
email: "",
});
useEffect(() => {
const getUserDetails = async () => {
const res = await axios.get("/api/me");
if (res.status === 200) {
setUserData({
name: res.data.data.username,
email: res.data.data.email,
});
}
};
getUserDetails();
}, []);
return (
<div>
<h1>{userData.name}</h1>
<h1>{userData.email}</h1>
</div>
);
}
In the login api, you set the expired time to 1d
const token = await jwt.sign(tokenData, process.env.JWT_SECRET!, {expiresIn: "1d",});
it means,if after one day you try to verify it; gives error
To solve this;
Erase the cookie from your developement or localhost server {go to profile and logout} login again and then you will not get any error