I am currently working on developing an admin dashboard using nextjs 13. I have encountered a specific issue where the user is redirected to the login page every time they reload the page. Upon inspecting in developer mode, I noticed that cookies are still present in the Cookies section, but both Session Storage and Local Storage appear empty, and the token changes with each login attempt. At this point, I have not yet implemented any middleware.
api/auth/[...nextauth]/route.js
import {authOptions} from '@/lib/auth'
import NextAuth from 'next-auth/next'
const handler = NextAuth(authOptions)
export {handler as GET, handler as POST}
/lib/auth.js
import { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { db } from "./db";
import { compare } from "bcryptjs";
export const authOptions = {
adapter: PrismaAdapter(db),
secret: process.env.NEXTAUTH_SECRET,
session: {
strategy: "jwt",
maxAge: 60 * 60 * 24 * 30,
},
pages: {
signIn: "/sign-in",
},
rememberMe: {
label: "Remember Me",
type: "checkbox",
},
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: {
label: "Email",
type: "email",
placeholder: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="27433e51424967404a464e4b0944484a">[email protected]</a>",
validation: {
required: true,
pattern:
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
},
},
password: { label: "Password", type: "password", placeholder: "****" },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
return null;
}
const existingUser = await db.user.findUnique({
where: { email: credentials?.email },
});
if (
(existingUser &&
existingUser.passwordExpirationDate &&
existingUser.passwordExpirationDate < new Date()) ||
(existingUser &&
existingUser.verificationCodeExpiresAt &&
existingUser.verificationCodeExpiresAt < new Date())
) {
return null; // Trigger password reset
}
if (!existingUser) {
return null;
}
const passwordMatch = await compare(
credentials.password,
existingUser.password
);
if (!passwordMatch) {
return null;
}
const passwordAgeInDays = existingUser.lastPasswordResetDate
? Math.floor(
(new Date().getTime() -
existingUser.lastPasswordResetDate.getTime()) /
(1000 * 60 * 60 * 24)
)
: 0;
if (passwordAgeInDays > 30) {
await db.user.update({
where: { id: existingUser.id },
data: { IsExpired: true },
});
return null;
}
const maxAge = credentials.rememberMe ? 60 * 60 * 24 * 30 : 60 * 60 * 24;
return {
id: `${existingUser.id}`,
name: existingUser.name,
username: existingUser.username,
email: existingUser.email,
role: existingUser.role,
exp: Math.floor(Date.now() / 1000) + maxAge,
};
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) {
return {
...token,
id: user.id,
username: user.username,
role: user.role,
};
}
return token;
},
async session({ session, token }) {
const maxAge = token.exp - Math.floor(Date.now() / 1000);
return {
...session,
user: {
...session.user,
id: token.id,
username: token.username,
role: token.role,
},
expires: Math.floor(Date.now() / 1000) + maxAge,
};
},
},
};
The main goal is to maintain the user's logged-in status and effectively manage their session.