auth0 / nextjs-auth0

Next.js SDK for signing in with Auth0
MIT License
2.01k stars 381 forks source link

withMiddlewareAuthRequired doesn't check if access token has expired #1723

Open jln13x opened 5 months ago

jln13x commented 5 months ago

Checklist

Description

The withMiddlewareAuthRequired doesn't seem to check if the access token is actually still valid

Reproduction

Additional context

No response

nextjs-auth0 version

3.5.0

Next.js version

14.1.3

Node.js version

v20.11.1

jln13x commented 5 months ago

Had to add custom code to check for it. Isn't there a better way to do that?

import { logger } from "@/server/logger";
import { routes } from "@/shared/routes";
import { safeTryAsync } from "@/shared/safe-try";
import { getAccessToken, withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge";
import type { NextMiddleware } from "next/server";
import { NextResponse } from "next/server";
import { pathToRegexp } from "path-to-regexp";

const publicRoutes: string[] = ["/api/auth/(.*)"];

const middleware: NextMiddleware = async (req, event) => {
  const publicRoutesAsRegex = publicRoutes.map((route) => pathToRegexp(route));

  const pathname = req.nextUrl.pathname;
  const isPublicRoute = publicRoutesAsRegex.some((route) => route.test(pathname));

  const tag = isPublicRoute ? "🌍" : "🔓";
  console.log(`[${tag}] Middleware hit for ${pathname}`);

  if (isPublicRoute) {
    return NextResponse.next();
  }

  // Only seems to check if a session exists, not if it's valid - But atleast it extends the Cookie expiration
  return withMiddlewareAuthRequired({
    middleware: async (req) => {
      const res = NextResponse.next();
      const [, error] = await safeTryAsync(() => getAccessToken(req, res));

      if (error) {
        logger.error("Error getting access token", error);
        return NextResponse.redirect(new URL(routes.logout(), req.nextUrl));
      }

      return res;
    },
  })(req, event);
};

export default middleware;

export const config = {
  matcher: ["/((?!.+\\.[\\w]+$|_next).*)"],
};
bashaus commented 4 months ago

Out of curiosity, do you also get the following error message:

nextjs-auth0 is attempting to set cookies from a server component,see https://github.com/auth0/nextjs-auth0#using-this-sdk-with-react-server-components
jln13x commented 4 months ago

Out of curiosity, do you also get the following error message:

nextjs-auth0 is attempting to set cookies from a server component,see https://github.com/auth0/nextjs-auth0#using-this-sdk-with-react-server-components

ye but shouldnt be related

bashaus commented 4 months ago

@jln13x – I implemented the same logic as your code, and it worked until I received the AccessTokenErrorCode ERR_EXPIRED_ACCESS_TOKEN. The problem was that the redirect was stuck in an infinite loop.

I solved it by updating the session to expire. This may not be the right way to do it (I know the documentation recommend against it), but I redirect to the logout URL afterwards to clean up the session:

import {
  AccessTokenError,
  getAccessToken,
  updateSession,
  withMiddlewareAuthRequired,
} from "@auth0/nextjs-auth0/edge";
import { NextResponse } from "next/server";

export default withMiddlewareAuthRequired({
  middleware: async function middleware(req) {
    try {
      await getAccessToken();
      return NextResponse.next();
    } catch (err) {
      if (err instanceof AccessTokenError) {
        const res = NextResponse.redirect(
          "https://www.example.com/api/auth/logout",
        );

        return updateSession(req, res, {
          user: [],
          accessToken: undefined,
          idToken: undefined,
          refreshToken: undefined,
          accessTokenExpiresAt: 0,
        });
      }

      /* Fallback: if you don't know how to handle the error */
      throw err;
    }
  },
});