Reputation: 25
I'm using NextAuth in a Next.js application with a custom credentials provider. Everything works perfectly on my local development environment, but in production (deployed on Vercel), the middleware fails to retrieve the token, causing users to get stuck on the login page.
Here's my nextauth configuration file:
import connectMongoDB from "@/libs/mongodb";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import User from "@/models/User";
import bcryptjs from "bcryptjs";
export const authOptions = {
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
siteID: { label: "Site ID", type: "text" },
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const { siteID, email, password } = credentials;
try {
await connectMongoDB();
const user = await User.findOne({ siteID, email });
if (!user) {
console.error("No user found with the provided credentials");
return null;
}
const validPassword = await bcryptjs.compare(password, user.password);
if (!validPassword) {
console.error("Invalid password");
return null;
}
return user;
} catch (error) {
console.error("Error in authorization:", error);
return null;
}
},
}),
],
callbacks: {
async jwt({ token, user, session, trigger }) {
if (trigger === "update" && session?.name) {
token.name = session.name;
}
if (user) {
return {
...token,
id: user._id,
name: user.name,
siteID: user.siteID,
};
}
return token;
},
async session({ session, token }) {
return {
...session,
user: {
...session.user,
id: token.id,
name: token.name,
siteID: token.siteID,
},
};
},
},
secret: process.env.NEXTAUTH_SECRET || "consultancyproject",
session: {
strategy: "jwt",
maxAge: 15 * 60,
updateAge: 10 * 60,
},
pages: {
signIn: "/Login",
},
events: {
async signOut(message) {
console.log("User signed out", message);
},
async sessionExpired(message) {
console.log("Session expired", message);
},
},
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Here is my Middleware file:
import { NextResponse } from "next/server";
import { getToken } from "next-auth/jwt";
export async function middleware(request) {
console.log("Middleware called. Request URL:", request.url);
console.log("NEXTAUTH_SECRET:", process.env.NEXTAUTH_SECRET);
let token;
try {
token = await getToken({
req: request,
secret: process.env.NEXTAUTH_SECRET,
});
console.log("Token retrieved:", token);
} catch (error) {
console.error("Error retrieving token:", error);
return NextResponse.redirect(new URL("/Login", request.url));
}
const path = request.nextUrl.pathname;
console.log("Requested path:", path);
const publicPaths = [
"/",
"/Signup",
"/Login",
"/ForgotPassword",
"/ResetPassword",
"/ChoosePlan",
];
const isPublicPath = publicPaths.includes(path);
console.log("Is public path:", isPublicPath);
// Redirect authenticated users away from public paths to the dashboard
if (isPublicPath && token) {
console.log("Authenticated user trying to access public path. Redirecting to /Dashboard");
return NextResponse.redirect(new URL("/Dashboard", request.url));
}
// Redirect unauthenticated users trying to access protected paths to the login page
if (!isPublicPath && !token) {
console.log("Unauthenticated user trying to access protected path. Redirecting to /Login");
return NextResponse.redirect(new URL("/Login", request.url));
}
// Continue with the request if the user is authenticated or accessing a public path
console.log("User is authenticated or accessing public path. Continuing with request.");
return NextResponse.next();
}
export const config = {
matcher: [
"/",
"/Admin",
"/Cases",
"/Dashboard",
"/ForgotPassword",
"/Home",
"/NotAuthorized",
"/PaymentSuccess",
"/Reports",
"/Signup",
"/ChoosePlan",
"/Emails",
"/Form",
"/ResetPassword",
], };
In production, the logs indicate that the token is not being retrieved:
***User is authenticated or accessing public path. Continuing with request.
GET /Login
Is public path: true
GET /Login
Requested path: /Login
Token retrieved: null***
These are the logs from nextauth file:
Session callback token: {
email: '[email protected]',
sub: '668e24f188a5984c9650b971',
id: '668e24f188a5984c9650b971',
siteID: 'GC7U35TAFX',
iat: 1721071538,
exp: 1723663538,
jti: '63301835-155f-4ef0-8eb9-7cff5f93d203'
}
JWT callback token: {
email: '[email protected]',
sub: '668e24f188a5984c9650b971',
id: '668e24f188a5984c9650b971',
siteID: 'GC7U35TAFX',
iat: 1721071538,
exp: 1723663538,
jti: '63301835-155f-4ef0-8eb9-7cff5f93d203'
}
What I've checked so far:
Questions:
Upvotes: 0
Views: 147
Reputation: 1603
you can try this implementation
import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
// Retrieve token from the request
const secureSessionToken = cookies().get('__Secure-authjs.session-token')?.name;
const unsecureSessionToken = cookies().get('authjs.session-token')?.name;
const secret = process.env.NEXTAUTH_SECRET ?? process.env.AUTH_SECRET ?? '';
const salt = secureSessionToken ?? unsecureSessionToken ?? '';
const isVercelProd = process.env.VERCEL_ENV === 'production';
const cookieName = !isVercelProd ? unsecureSessionToken : secureSessionToken;
const token = await getToken({ req, secret, salt, cookieName, raw: true });
Upvotes: 0
Reputation: 4055
Don't forget to add environment variables in your Dockerfile as well (if you use any)
E.x.
//...
ENV API_BASE_URL=https://api.example.com
ENV API_KEY=i175e99e-99x1-5e18-52bb-1u98c30ef83c
ENV AUTH_SECRET=an2af0596604344x3g015a789ba447uj
ENV AUTH_URL=https://www.example.com/api/auth
//...
Upvotes: 0