I’m working on a Next.js project using NextAuth (v5) for authentication. I want to implement a 10-second token expiry for testing purposes. The goal is that after logging in, users can access protected routes for 10 seconds. Once the token expires, refreshing the page or navigating should redirect them to the login page.
Here’s my current implementation:
auth.ts
import NextAuth from "next-auth"
import authConfig from "@/auth.config"
export const { handlers, signIn, signOut, auth } = NextAuth({
...authConfig,
pages: {
signIn: '/sign-in'
},
session: {
strategy: "jwt",
// maxAge: <number>,
// updateAge: <number>,
},
callbacks: {
async jwt({ token, user }) {
console.log('\n hello from jwt callback. \n'); // DEBUG
if (user){
token.firstName = user.firstName;
token.lastName = user.lastName;
token.email = user.email;
}
return token;
},
async session({ session, token }) {
console.log('\n hello from session callback. \n'); // DEBUG
session.user.firstName = token.firstName as string;
session.user.lastName = token.lastName as string;
session.user.email = token.email as string;
return session;
},
},
});
middleware.ts
import authConfig from "@/auth.config"
import NextAuth from "next-auth"
import { NextResponse } from "next/server";
const { auth: middleware } = NextAuth(authConfig);
export default middleware( async (req) => {
const url = req.nextUrl.clone();
if(url.pathname.startsWith('/api/auth')) return NextResponse.next();
if(url.pathname.startsWith('/api/users')) return NextResponse.next();
const isLoggedIn = !!req.auth;
if(!isLoggedIn){
if(url.pathname.startsWith('/api')){
return NextResponse.json(
{ message: "Unauthorized" },
{ status: 401 }
);
}else{
url.pathname = '/sign-in'
return NextResponse.redirect(url);
}
}
return NextResponse.next();
})
export const config = {
matcher: [
"/((?!.*\\.[\\w]+$|_next|sign-in|sign-up).*)", // Match all paths except static files, `_next`, `sign-in` and `sign-up` paths
"/", // Root route
"/api/:path*", // API routes
"/trpc/:path*", // TRPC routes
],
};
My Attempts:
session: { strategy: "jwt", ...}
, expecting the token to expire after 10 seconds. However, it seems that the token expiry isn’t being enforced as expected, and client can still access protected routes beyond the 10-second limit.Expected Behavior:
Since this implementation is based on NextAuth v5 (the latest version at the time), it may feel unfamiliar or different to developers who are used to older stable versions of NextAuth (i.e., v4). Some concepts and APIs, such as token handling and session management, may have changed.
You can update like this.
session: {
strategy: "jwt",
maxAge: 10, // Token will expire after 10 seconds
},
callbacks: {
async jwt({ token, user }) {
const now = Math.floor(Date.now() / 1000);
const maxAge = 10;
if (user) {
token.issuedAt = now;
}
if (now - (token.issuedAt ?? 0) > maxAge) {
return null;
}
return token;
},
async session({ session, token }) {
if (!token) {
return null;
}
session.user = {
firstName: token.firstName,
lastName: token.lastName,
email: token.email,
};
return session;
},
},
export default async function middleware(req) {
const { auth } = req;
const url = req.nextUrl.clone();
if (!auth) {
if (url.pathname.startsWith('/api')) {
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
}
url.pathname = '/sign-in';
return NextResponse.redirect(url);
}
const now = Math.floor(Date.now() / 1000);
if (auth.token?.issuedAt && now - auth.token.issuedAt > 10) {
url.pathname = '/sign-in';
return NextResponse.redirect(url);
}
return NextResponse.next();
}