Skip to content

Upgrade to v4 causes logout for existing users #1894

Closed
@jdwitten

Description

@jdwitten

Checklist

Description

When upgrading to v4 I noticed the name of the session cookie changed from appSession -> __session and I believe this is requiring users to re-authenticate after upgrade. When trying to access a page that requires auth, the new v4 middleware is not able to find an active session for a user that was previously authenticated with v3 session and redirects to /auth/login.

We have not deployed to production, this observation is just from local testing, so want to raise this issue to make sure there isn't any technique to prevent this.

Reproduction

  1. Authenticate with a version of the application running v3
  2. Notice a valid session cookie in the user's browser. (note: I see the chunked appSession.0 and appSession.1)
  3. Switch branches to a version of the app running v4 (simulating an upgrade of our Next.js application)
  4. Refresh the same page and see that I am required to authenticate again.
  5. After authenticating I see both the appSession and __session cookies

Additional context

Here is our auth code in the middleware:

const authRes = await auth0.middleware(req);

  if (req.nextUrl.pathname.startsWith('/auth')) {
    // If the request is for the auth routes, short circuit the middleware chain and return the response
    return authRes;
  }

  const session = await auth0.getSession(req);
  /**
   * It's important to call getAccessToken here in the middleware with the req and res objects as inputs.
   * If the access token in the request is expired, getAccessToken will update it from Auth0 and set the new token in the response.
   * This allows the new token to be accessed from server components and other middleware.
   *
   * If getAccessToken is called only in the server component, then the token will be refreshed but Server components cannot set cookies,
   * so the updated token will not be persisted for subsequent requests.
   *
   * if getAccessToken is called without the req and res objects, the token will be updated but not persisted for other middleware or server components.
   * See this issue for more details: https://github.com/auth0/nextjs-auth0/issues/1841
   */
  const token = await getAccessToken({ req, res: authRes });
  const isUserActive = session && token;

  const isPublicRoute = PUBLIC_ROUTE_PREFIXES.find((path) =>
    req.nextUrl?.pathname?.startsWith(path),
  );
  const isPublicPage = PUBLIC_PAGES.find(
    (path) => req.nextUrl?.pathname === path,
  );
  if (!isPublicRoute && !isPublicPage && !isUserActive) {
    // User does not have an active session — short circuit middleware and redirect to login
    const { origin } = new URL(req.url);
    return NextResponse.redirect(
      `${origin}/auth/login?returnTo=${buildReturnTo(req)}`,
    );
  }

  const finalResponse = await next(req, authRes, event);

  // Auth0 middleware sets some headers that we need to pass along to the final response.
  // Other middlewares may have generated new responses without these headers (like the locale middleware), so we need to
  // copy the headers from the auth response to the final response.
  for (const [key, value] of authRes.headers.entries()) {
    finalResponse.headers.set(key, value);
  }
  return finalResponse;
}

export async function getAccessToken(
  requestVars: { req: NextRequest; res: NextResponse } | undefined = undefined,
) {
  try {
    const result =
      requestVars != null
        ? await auth0.getAccessToken(requestVars.req, requestVars.res)
        : await auth0.getAccessToken();
    return result.token;
  } catch (error) {
    authLogger.error(error, `Error retrieving access token`);
    authLogger.info(`Session details: ${getSessionDetails()};`);
    return undefined;
  }
}

async function getSessionDetails(): Promise<string> {
  const session = await auth0.getSession();
  return JSON.stringify({
    email: session?.user?.email,
    name: session?.user?.name,
    hasAccessToken: Boolean(session?.tokenSet.accessToken),
    accessTokenExpirationTime: String(session?.tokenSet.expiresAt),
    hasRefreshToken: Boolean(session?.refreshToken),
    hasSession: Boolean(session),
    runtime: process.env.NEXT_RUNTIME,
  });
}

nextjs-auth0 version

4.0.0

Next.js version

15.1.6

Node.js version

20.9.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions