next-auth: Getting token in middleware doesn't work with authorization header request

Environment

System: OS: macOS 11.6 CPU: (4) x64 Intel® Core™ i5-7360U CPU @ 2.30GHz Memory: 169.62 MB / 16.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 16.13.0 - /usr/local/bin/node Yarn: 1.22.15 - ~/.yarn/bin/yarn npm: 8.1.0 - /usr/local/bin/npm Browsers: Brave Browser: 72.0.59.35 Chrome: 98.0.4758.102 Edge: 98.0.1108.56 Safari: 14.1.2 npmPackages: next: ^12.0.7 => 12.0.9 next-auth: ^4.2.1 => 4.2.1 react: 17.0.2 => 17.0.2

Reproduction URL

None

Describe the issue

When using just the authorization header, the getToken(req) method returns correctly in the server with NextApiRequest, but not in the middleware with NextRequest. When I dug through the code, it seems it’s because NextApiRequest authorization header is in the list, whereas getToken function is trying to split the authorization header by space, which shouldn’t work for this case.

How to reproduce

For any OAuth provider, get the raw token.

Use it from a REST client with the Authorization: Bearer <token> and send an API request.

In the middleware, try getToken({req}). It returns null, whereas in the actual API route code, it returns the token info correctly. Also, it works correctly when a user actually logs in in the browser and there’s cookies and other jazz from the browser.

The middleware code is very simple:

import { getToken } from "next-auth/jwt"
import { NextRequest, NextResponse } from "next/server"

export async function middleware(req: NextRequest) {
  const token = await getToken({ req })
  console.log("Middleware token", token)
  NextResponse.next()
}

My API request is:

GET http://localhost:3000/api/<api_path> HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{token}} 

Expected behavior

I should get the token info in the middleware with just the auth header with bearer token (not only with browser session).

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 30 (5 by maintainers)

Most upvoted comments

Why is this closed? The original issue has not been resolved yet

Hmm thanks ! Buts still not working for me even with next@12.2.0 and the middleware in the root it keep asking for authentification

Update : I comfirm that downgrading to @12.2.0 make all working for now

Any update on this one by any chance? Just an acknowledgement that this is indeed an issue, and that you’re prioritizing/deprioritizing this would be helpful to structure my project accordingly.

same issue using next@latest[12.2.3].

I downgraded from next 12.2.3 to next 12.2.0 and that fixed the issue for me.

I tried this method you recommended as well, but still gives me null. I have not been able to get the token info in middleware by any means so far. Also, I pass secret in the environment variable correctly, and that shouldn’t be the issue (unless I need to explicitly pass it somewhere else). Also the reason I can say it’s probably not the issue is that it works correctly in the API code (see below description).

Here’s my middleware code (pages/api/_middleware.ts). This gives type errors, but that’s another issue.:

import { withAuth } from "next-auth/middleware"
import { NextRequest, NextResponse } from "next/server"

export default withAuth(
  function middleware(req: NextRequest & { nextauth: { token: JWT } }) {
    console.log("Middleware token", req.nextauth.token)
    return NextResponse.next()
  },
  {
    callbacks: {
      authorized: ({ token }) => true,
    },
  }
)

Here’s my API code (pages/api/<my_api_path>)

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const token = await getToken({ req })
  console.log("Server side token: ", token)
...
}

My vscode rest client request is this:

GET {{host}}/api/<my_api> HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{secretApiKey}}

The console output is:

Middleware token null
..
..
Server side token:  {
  name: '..',
  email: '...',
  picture: '...',
  sub: '1330646',
  roles: [ 'admin' ],
  iat: 1645512745,
  exp: 1648104745,
  jti: '...'
}

Just for sake of completeness, console logging the token from the authorize callback also results in the same issue, i.e. following middleware code also gives same output:

import { withAuth } from "next-auth/middleware"

export default withAuth({
  callbacks: {
    authorized: ({ token }) => {
      console.log("Middleware token", token)
      return true
    },
  },
})

Please reopen this issue! I’m facing the same problem

For anyone working with next version >= 13, this solution worked for me. You can either set the urls as absolute, or you can create an URL object, both can work, but URL gave me a little bit of issues so I fallbacked to absolutes.

import { NextRequest, NextResponse } from "next/server";
import { getSession } from "next-auth/react";
import { env } from "./env.mjs";

async function middleware(request: NextRequest) {
  const requestForNextAuth = {
    headers: {
      cookie: request.headers.get("cookie"),
    },
  };

  const session = await getSession({ req: requestForNextAuth });

  const isAuthPage =
    request.nextUrl.pathname.startsWith("/login") ||
    request.nextUrl.pathname.startsWith("/register");

  if (!session) {
    let from = request.nextUrl.pathname;
    if (request.nextUrl.search) {
      from += request.nextUrl.search;
    }
    const absoluteFrom = `${env.NEXT_PUBLIC_APP_URL}/${from}`;
    return NextResponse.redirect(
      `${env.NEXT_PUBLIC_APP_URL}/login?from=${encodeURIComponent(
        absoluteFrom,
      )}`,
    );
  } else if (isAuthPage) {
    return NextResponse.redirect(`${env.NEXT_PUBLIC_APP_URL}/dashboard`);
  }
  return null;
}

export default middleware;

export const config = {
  matcher: ["/dashboard", "/org/:path*"],
};

Having the same issue. Just a wild guess: “req.headers.authorization” does not work (anymore) as it is used e.g. here: https://github.com/nextauthjs/next-auth/blob/3d0c68d9e00c707f254d17a511c54af4ebb9eeb0/packages/next-auth/src/jwt/index.ts#L93-L106

What we actually need to do is (or at least that gets me the actual token): req.headers.get("Authorization")

You are a life saver! Ive been struggling with this problem all day, with no hope.

Failed with next@12.2.4. Works with:

"next": "^12.3.0",
"next-auth": "^4.10.3",

I confirm too, the only way to work is downgrading nextjs… I’ve been struggling all day, no hope.

@90PabloRomero which version of next-auth are you using? I have the same issue even with the v12.0.0

I confirm too, the only way to work is downgrading nextjs… I’ve been struggling all day, no hope.

I faced the same issue while using next@latest[12.2.3]. Downgrading to next@12.2.0 fixed it for me.

Actually, this seems to have been fixed in #4472 already, we just need a new release. See the code here:

https://github.com/nextauthjs/next-auth/blob/e4ee520b4aba55fbc123801291bcba404f578d3f/packages/next-auth/src/jwt/index.ts#L94-L97

Haven’t had the time to confirm yet. Kept open with the triage label until then. 👍

If you are certain this is a bug, you are welcome to open a PR. Here is the relevant code: https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/next/middleware.ts