vvo / iron-session

🛠 Secure, stateless, and cookie-based session library for JavaScript

Home Page:https://get-iron-session.vercel.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Next.js middleware token rotation issue

olafurns7 opened this issue · comments

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?

commented

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:

  1. gets the current session
  2. attempts to get new credentials from our database
  3. 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!