nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.84k stars 3.5k forks source link

getServerSession not working in middleware using Next.js 13 app directory #7732

Closed thexpand closed 1 year ago

thexpand commented 1 year ago

Question 💬

I can't get the getServerSession to work in a middleware. I'm using the new Next 13 app directory and I placed the middleware.ts file in the root of my project. When I try to open a URL that matches the middleware and goes through it, I get the following error:

- error node_modules\oidc-token-hash\lib\shake256.js (3:0) @ <unknown>
- error Cannot read properties of undefined (reading 'substring')

How to reproduce ☕️

import type { NextRequest } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/utils/auth-options';

// This function can be marked `async` if using `await` inside
export async function middleware(request: NextRequest) {
  const session = await getServerSession(authOptions);
  console.log('session', session);
}

// See "Matching Paths" below to learn more
export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)',
  ],
};

Contributing 🙌🏽

Yes, I am willing to help answer this question in a PR

balazsorban44 commented 1 year ago

This is expected. getServerSession is not working in the Edge Runtime. You can follow #7443 for an upcoming solution.

Or for now, check out next-auth/middleware

JanThiel commented 1 year ago

With v5 the app and edge compatible middleware will look like this:

https://github.com/nextauthjs/next-auth/pull/7443/files#diff-dc71c2dbb7c476f01abb09660680b2cb82e843baa292adf0a804544449feec34

maykon-oliveira commented 11 months ago

How to deal with this until NextAuth 5 is released?

avarayr commented 8 months ago

If anyone stumbles upon this issue from Google - your only bet here is to make a HTTP Get Request to your app url. Works very well in my app, no issues with scaling.

/*
 * middleware.ts
 */
export function middleware(request: NextRequest) {
  const c = cookies();
  const allCookies = c
    .getAll()
    .map((c) => `${c.name}=${c.value}`)
    .join("; ");

  /**
   * If next-auth.session-token is not present, return 401
   * (!) IMPORTANT NOTE HERE:
   * next-auth likes to use different cookie name for prod (https) so make sure to set a consistent cookie name in your next-auth configuration file (see docs)
   */
  if (!c.get("next-auth.session-token")?.value?.trim()) {
    return new Response("Unauthorized, log in first", { status: 401 });
  }

  const headers = {
    "Content-Type": "application/json",
    Cookie: allCookies,
  };

  /**
   * Send a request to /api/auth/session to get the user session
   * process.LOOPBACK_URL can be set as localhost, or your website url
   */
  const url = new URL(`/api/auth/session`, process.env.LOOPBACK_URL);
  const response = await fetch(url.href, {
    headers,
    cache: "no-store",
  });

  if (response.ok) {
    // 🔥 check for relevant role/authorization (if necessarry)

    if (new Date(session.expires) < new Date()) {
      return new Response("Refresh session!", { status: 401 });
    }

    /**
     * Allow the request to continue
     */
    return NextResponse.next();
  }

  return new Response("Unauthorized", { status: 401 });
}
GabrielPedroza commented 7 months ago

Is there an implementation with Discord Provider?

nikolay-gipp-sibe commented 4 months ago

Isn't there some convenient way to get access token from the next-auth session in middleware?

dylanlanigansmith commented 2 months ago

for the people here from google maybe this helps

import { withAuth } from "next-auth/middleware";
import { NextRequest } from 'next/server' //actually next-auth is using a variation of this, see my response below in this thread!
export const config = {
  // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
  matcher: [ '/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|logo.png).*)',
  ],
};

export function middleware(request, event) {
    console.log("middleware + auth check without error", request.url)

   /*

        if you want to do your own stuff here first, without errors, but use nextAuth middleware for session check just call withAuth yourself! 

    otherwise do your thing, just check out the functions below in next-auth:
        in file: next-auth/middleware.ts
            //withAuth() is exported as middleware(request, event), internally it calls this:

            async function handleMiddleware(req: NextRequest,
            options: NextAuthMiddlewareOptions | undefined,
            onSuccess?: (token: JWT | null) => Promise<NextMiddlewareResult>) 

   */
    return withAuth(request, event)
  }
jonathangaldino commented 2 months ago

for the people here from google maybe this helps

import { withAuth } from "next-auth/middleware";
import { NextRequest } from 'next/server'
export const config = {
  // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
  matcher: [ '/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|logo.png).*)',
  ],
};

export function middleware(request, event) {
    console.log("middleware + auth check without error", request.url)

   /*

        if you want to do your own stuff here first, without errors, but use nextAuth middleware for session check just call withAuth yourself! 

    otherwise do your thing, just check out the functions below in next-auth:
        in file: next-auth/middleware.ts
            //withAuth() is exported as middleware(request, event), internally it calls this:

            async function handleMiddleware(req: NextRequest,
            options: NextAuthMiddlewareOptions | undefined,
            onSuccess?: (token: JWT | null) => Promise<NextMiddlewareResult>) 

   */
    return withAuth(request, event)
  }

withAuth doesn't accept NextRequest type for the request parameter (next-auth 4.24.7). They are different. I couldn't get it work here.

dylanlanigansmith commented 2 months ago

withAuth doesn't accept NextRequest type for the request parameter (next-auth 4.24.7). They are different. I couldn't get it work here.

yeah sorry that should be the NextAuth type, NextRequestWithAuth, look at the code in next-auth/src/next/middleware.ts from line 156-EOF

//from line 156 @ next-auth/src/next/middleware.ts
export interface NextRequestWithAuth extends NextRequest {
  nextauth: { token: JWT | null }
}

export type NextMiddlewareWithAuth = (
  request: NextRequestWithAuth,
  event: NextFetchEvent
) => NextMiddlewareResult | Promise<NextMiddlewareResult>

export type WithAuthArgs =
  | [NextRequestWithAuth]
  | [NextRequestWithAuth, NextFetchEvent]
  | [NextRequestWithAuth, NextAuthMiddlewareOptions]
  | [NextMiddlewareWithAuth]
  | [NextMiddlewareWithAuth, NextAuthMiddlewareOptions]
  | [NextAuthMiddlewareOptions]
  | []

//...
//from line 219
export function withAuth(
  ...args: WithAuthArgs
): ReturnType<NextMiddlewareWithAuth> | NextMiddlewareWithAuth {

hopefully this helps clarify my original response!