iron-session
iron-session copied to clipboard
Next.js middleware token rotation issue
Hey, thanks for all your hard work around iron-session!
We're implementing authentication, and checks via Next.js 14 middleware. An issue we are running into is that if we detect that a session has expired, and we want to refresh it during the middleware run, the first response back to the client/page usually still has the expired session.
We've tested using both getIronSession with req,res and using the Next.js cookies.
I, believe that for iron-session to fully work within the middleware there needs to be a similar functionality as how Supabase sets up its client:
https://supabase.com/docs/guides/auth/server-side/nextjs?router=app
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return request.cookies.get(name)?.value
},
set(name: string, value: string, options: CookieOptions) {
request.cookies.set({
name,
value,
...options,
})
response = NextResponse.next({
request: {
headers: request.headers,
},
})
response.cookies.set({
name,
value,
...options,
})
},
remove(name: string, options: CookieOptions) {
request.cookies.set({
name,
value: '',
...options,
})
response = NextResponse.next({
request: {
headers: request.headers,
},
})
response.cookies.set({
name,
value: '',
...options,
})
},
},
}
)
Excuse, did you solve this problem?
I am also observing the bug where after updating the session in the middleware, the first response in a server component is stale.
Edit: This was an issue with next but it was fixed in v14.3.0-canary.24 The issue is still present in iron session however.
@headironc and everyone else that is trying to deal with this.
What we basically did was to create a next.js api route, in our case: /api/refresh-token
.
In the middleware check, if a user has a authentication session, but it has "expired", we want to attempt to refresh the authentication the user has.
middleware.tsx
....
if (session && session?.isLoggedIn) {
const tokenValid = (await tokenValidation(session)).valid;
/**
* Instead of doing a refresh of the token here, we can bounce the user
* to the auth portal, where the refresh logic is attempted, and if successful the user just gets
* redirected here
*/
if (!tokenValid) {
return NextResponse.redirect(`${req.nextUrl.origin}/api/refresh-token`);
}
}
....
the /api/refresh-token
endpoint doesn't do much else than checking if there is a session, and attempt to refresh the session authentication.
export async function GET(req: NextRequest) {
const session = await getSession();
if (!session.isLoggedIn) {
return NextResponse.redirect(authRedirectUrl());
}
const correlationId = getCorrelationId(req.headers);
log.info({
msg: 'Refreshing token',
user: session?.username,
correlationId,
});
const newSession = await refreshAuthentication();
if (!newSession) {
log.info({
msg: 'Failed to refresh token',
user: session?.username,
correlationId,
});
return NextResponse.redirect(authRedirectUrl(), {
headers: {
'x-correlation-id': correlationId,
},
});
}
return NextResponse.redirect(env.PUBLIC_URL, {
headers: {
'x-correlation-id': correlationId,
},
});
}
export const dynamic = 'force-dynamic';
So basically by having the middleware do a redirect to a next.js api route or route handler, the route handler does the updating and then redirects back to where the user wanted to go.
This has at least worked for us.
the refreshAuthentication
function basically does the following:
- gets the current session
- attempts to get new credentials from our database
- updates the session and calls sesion.save() and returns the session
@olafurns7 @Emrin thanks for all the explanations, in the end, is there a bug to fix in iron-session? If yes I would love a very minimal GH repo from you so I can try to fix that.
I'll close the issue, you can still comment, and I will reopen if there's a GH repository with a reproduction we can work on, thanks!
@olafurns7 @Emrin thanks for all the explanations, in the end, is there a bug to fix in iron-session? If yes I would love a very minimal GH repo from you so I can try to fix that.
I'll close the issue, you can still comment, and I will reopen if there's a GH repository with a reproduction we can work on, thanks!
I isn't really a fault with iron-session, just how iron-session works internally and how the Next.js middleware works.
However it might be a useful/good addition to iron-session, specifically for Next.js AppDir middleware to have some out of the box functionality to deal with this case. But that's just an idea :)
Thank you for all your hard work on this library!