typescriptnext.jsmiddlewarenext-auth

How to extend NextRequest (to add an "auth" property)


I'm using nextjs14 (App Router) with stacked middleware for localization and authentication. For auth, I'm using next-auth v5, and I'm unable to access auth in the Request object.

The same code works fine in middleware.ts, with no other middleware for localization, but once stacked, request.auth is undefined and I also get the ts error "Property 'auth' does not exist on type 'NextRequest'.ts(2339)".

code that works (middleware.ts):

import NextAuth from "next-auth";

import authConfig from "@/auth.config";

import {
  DEFAULT_LOGIN_REDIRECT,
  apiAuthPrefix,
  authRoutes,
  publicRoutes
} from "@/routes";

const { auth } = NextAuth(authConfig);

// import { auth } from "./auth";

export default auth((req) => {
    // middleware code goes here
      
      const { nextUrl } = req;
      const isLoggedIn = !!req.auth;

      console.log("req.auth: ", req.auth);

      const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
      const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
      const isAuthRoute = authRoutes.includes(nextUrl.pathname);
      
      console.log({
        url: nextUrl.pathname,
        isApiAuthRoute: isApiAuthRoute,
        isAuthRoute: isAuthRoute,
        isPublicRoute: isPublicRoute,
        isLoggedIn: isLoggedIn,
      });

      if (isApiAuthRoute) {
        return null;
      }

      if (isAuthRoute) {
        if (isLoggedIn) {
          return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl));
        }
        return null;
      }


      if (!isLoggedIn && !isPublicRoute && !isAuthRoute) {
        return Response.redirect(new URL("/en/login", nextUrl));
        // return null;
      }

      return null;
})

export const config = {
    matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};

But the following code doesnt (middlewares/authMiddleware.ts):

import {
  NextFetchEvent,
  NextRequest,
  NextResponse
} from "next/server";
import { MiddlewareFactory } from "@/lib/definitions";

import NextAuth from "next-auth";

import authConfig from "@/auth.config";

import {
  DEFAULT_LOGIN_REDIRECT,
  apiAuthPrefix,
  authRoutes,
  publicRoutes
} from "@/routes";

const { auth } = NextAuth(authConfig);

function getSearchParam(param: string, url: any) {
  return url.searchParams.get(param);
}

export const authMiddleware: MiddlewareFactory = (next) => {
  return async(request: NextRequest, _next: NextFetchEvent) => {
    const pathname = request.nextUrl.pathname;

    if (["/"]?.some((path) => pathname.startsWith(path))) {
      // middleware code goes here
      
      const { nextUrl } = request;
      const isLoggedIn = !!request.auth;

      console.log("request.auth: ", request);

      const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
      const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
      const isAuthRoute = authRoutes.includes(nextUrl.pathname);
      
      console.log({
        url: nextUrl.pathname,
        isApiAuthRoute: isApiAuthRoute,
        isAuthRoute: isAuthRoute,
        isPublicRoute: isPublicRoute,
        isLoggedIn: isLoggedIn,
      });

      if (isApiAuthRoute) {
        return null;
      }

      if (isAuthRoute) {
        if (isLoggedIn) {
          return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl));
        }
        return null;
      }


      if (!isLoggedIn && !isPublicRoute && !isAuthRoute) {
        return Response.redirect(new URL("/en/login", nextUrl));
        // return null;
      }

      return null;

    // end
    }
    return next(request, _next);
  };
};

I have also tried adding this:

import { NextRequest as OriginalNextRequest } from "next/server";

declare global {
    declare interface NextRequest extends OriginalNextRequest {
        auth: any
    }
}

to global.d.ts in the root of my project, using it instead of calling NextRequest from next/server and I get the following error:

Type '(next: NextMiddleware) => (request: NextRequest, _next: NextFetchEvent) => Promise<NextMiddlewareResult>' is not assignable to type 'MiddlewareFactory'.
  Type '(request: NextRequest, _next: NextFetchEvent) => Promise<NextMiddlewareResult>' is not assignable to type 'NextMiddleware'.
    Types of parameters 'request' and 'request' are incompatible.
      Property 'auth' is missing in type 'import("p:/cts-final/frontend/node_modules/next/dist/server/web/spec-extension/request").NextRequest' but required in type 'NextRequest'.ts(2322)
global.d.ts(5, 9): 'auth' is declared here.

How do I extend NextRequest to add the auth property, if that's even the solution?


Solution

  • You can create a .d.ts file with any name (Eg: types.d.ts)

    And then add your custom attribute/s

    import { NextRequest as DefaultNextRequest } from 'next/server'
    
    
    declare global {
      declare interface NextRequest extends DefaultNextRequest {
        // add more attributes here
        auth: {
          user: User
        }
      }
    }