Skip to content
Docs
Usage guide
Middleware

Authentication Middleware

authMiddleware works together with getTokens function to share valid user credentials between Next.js Middleware (opens in a new tab) and Server Components (opens in a new tab).

Features

  1. It sets up /api/login and /api/logout endpoints to allow the client to manage browser authentication cookies. Please note that you don't have to setup dedicated api routes youself, as middleware does it for you. You can change the name of those endpoints by changing loginPath and logoutPath middleware options
  2. Automatically refreshes browser authentication cookies when token expires, and let's developer act accordingly
  3. Signs user cookies with rotating keys to mitigate the risk of cryptanalysis attacks
  4. Validates user cookies on each request
  5. Allows to customize behaviour by defining handleValidToken, handleInvalidToken and handleError callbacks

Advanced usage

Advanced usage of authMiddleware in middleware.ts, based on starter example:

middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { authMiddleware, redirectToHome, redirectToLogin } from "next-firebase-auth-edge";
 
const PUBLIC_PATHS = ["/register", "/login", "/reset-password"];
 
export async function middleware(request: NextRequest) {
  return authMiddleware(request, {
    loginPath: "/api/login",
    logoutPath: "/api/logout",
    apiKey: "XXxxXxXXXxXxxxxx_XxxxXxxxxxXxxxXXXxxXxX",
    cookieName: "AuthToken",
    cookieSignatureKeys: ["secret1", "secret2"],
    cookieSerializeOptions: {
      path: "/",
      httpOnly: true,
      secure: false, // Set this to true on HTTPS environments
      sameSite: "lax" as const,
      maxAge: 12 * 60 * 60 * 24, // twelve days
    },
    serviceAccount: {
      projectId: "your-firebase-project-id",
      clientEmail: "firebase-adminsdk-nnw48@your-firebase-project-id.iam.gserviceaccount.com",
      privateKey: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
    },
    debug: true,
    tenantId: "your-tenant-id",
    checkRevoked: true,
    handleValidToken: async ({ token, decodedToken }, headers) => {
      // Authenticated user should not be able to access /login, /register and /reset-password routes
      if (PUBLIC_PATHS.includes(request.nextUrl.pathname)) {
        return redirectToHome(request);
      }
 
      return NextResponse.next({
        request: {
          headers,
        },
      });
    },
    handleInvalidToken: async (reason) => {
      console.info('Missing or malformed credentials', {reason});
 
      return redirectToLogin(request, {
        path: "/login",
        publicPaths: PUBLIC_PATHS,
      });
    },
    handleError: async (error) => {
      console.error("Unhandled authentication error", { error });
 
      return redirectToLogin(request, {
        path: "/login",
        publicPaths: PUBLIC_PATHS,
      });
    },
  });
}
 
export const config = {
  matcher: ["/api/login", "/api/logout", "/", "/((?!_next|favicon.ico|api|.*\\.).*)"],
};

Middleware token verification caching

Since v0.9.0 handleValidToken is called with modified request headers as a second parameter.

You can pass this object to NextResponse.next({ request: { headers } }) to enable token verification caching.

See Modifying Request Headers in Middleware (opens in a new tab) for more information on how modified headers work

handleValidToken: async ({ token, decodedToken }, headers) => {
  return NextResponse.next({
    request: {
      headers, // Pass modified request headers to skip token verification in subsequent getTokens and getTokensFromObject calls
    },
  });
};

Usage with next-intl and other middlewares

import { NextRequest } from "next/server";
import createIntlMiddleware from "next-intl/middleware";
import { authMiddleware } from "next-firebase-auth-edge";
 
const intlMiddleware = createIntlMiddleware({
  locales: ["en", "pl"],
  defaultLocale: "en",
});
 
export async function middleware(request: NextRequest) {
  return authMiddleware(request, {
    // ...
    handleValidToken: async (tokens) => {
      return intlMiddleware(request);
    },
    handleInvalidToken: async (reason) => {
      return intlMiddleware(request);
    },
    handleError: async (error) => {
      return intlMiddleware(request);
    },
  });
}

Please note that you don't need to pass headers as in middleware token verification caching (opens in a new tab) when using next-intl middleware. request is already updated with modified headers by the time we call intlMiddleware. next-intl will pass modified headers for us.

Options

NameDescription
loginPathRequiredDefines API login endpoint. When called with auth firebase token from the client (see examples below), responds with Set-Cookie headers containing signed id and refresh tokens.
logoutPathRequiredDefines API logout endpoint. When called from the client (see examples below), returns empty Set-Cookie headers that remove previously set credentials
apiKeyRequiredFirebase project API key used to fetch firebase id and refresh tokens
cookieNameRequiredThe name for cookie set by loginPath api route.
cookieSignatureKeysRequiredRotating keys (opens in a new tab) the cookie is validated against
cookieSerializeOptionsRequiredDefines additional cookie options sent along Set-Cookie headers
serviceAccountOptional in authenticated Google Cloud Run (opens in a new tab) environment. Otherwise requiredFirebase project service account.
tenantIdOptionalstring By default undefinedGoogle Cloud Platform tenant identifier. Specify if your project supports multi-tenancy (opens in a new tab)
checkRevokedOptionalboolean By default falseIf true, validates the token against firebase server on each request. Unless you have a good reason, it's better not to use it.
handleValidTokenOptional(tokens: { token: string, decodedToken: DecodedIdToken }, headers: Headers) => Promise<NextResponse> By default returns NextResponse.next()Receives id and decoded tokens and should return a promise that resolves with NextResponse. Function is called with modified request headers as a second parameter. By passing this parameters down to NextResponse.next({ request: { headers } }) library won't verify the token in subsequent calls to getTokens or getTokensFromObject, which can improve response times.
handleInvalidTokenOptional(reason: InvalidTokenReason) => Promise<NextResponse> By default returns NextResponse.next()If passed, is called and returned if request has not been authenticated (either does not have credentials attached or credentials are malformed). Can be used to redirect unauthenticated users to specific page or pages. Called with reason as first argument, which can be one of MISSING_CREDENTIALS, MISSING_REFRESH_TOKEN, MALFORMED_CREDENTIALS, INVALID_SIGNATURE, INVALID_CREDENTIALS. See handleInvalidToken to read description of each reason.
handleErrorOptional(error: AuthError) => Promise<NextResponse> By default returns NextResponse.next()Receives an unhandled error that happened during authentication and should resolve with NextResponse. By default, in case of unhandled error during authentication, the library just allows application to render. This allows you to customize error handling. See handleError to read about possible errors
debugOptionalboolean By default falseProvides helpful logs than can help understand authentication flow and debug issues