nextauthjs / next-auth

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

callbackUrl and middleware issues #8252

Open tmackness opened 11 months ago

tmackness commented 11 months ago

Environment

System: OS: macOS 13.4.1 CPU: (10) arm64 Apple M1 Max Memory: 160.19 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.16.0 - /usr/local/bin/node npm: 9.5.1 - /usr/local/bin/npm Browsers: Brave Browser: 114.1.52.129 Safari: 16.5.2

Next 13.4.12 next-auth 4.22.3 & 4.22.4

Reproduction URL

https://gist.github.com/tmackness/7c89f9668d386fc125c5cf9c67bde1f5

Describe the issue

I am having two issues:

  1. CallbackUrl

When you navigate or get redirected to the login page (via protected route middleware) the correct callbackUrl is added as a query param. However, in the case of the email provider the verify url has 2 callbackUrl query strings attached.

http://localhost:3000/api/auth/callback/email?callbackUrl=http://localhost:3000/login?callbackUrl=http://localhost:3000/login&token=391a921d57bb707128941a9786e381a9901a0b78d45a590bc9b823edd1ffa993&email=me@gmail.com

  1. Once logged in correctly when navigating to a protected route you get redirected to the login page again. Never able to access the protected route.

How to reproduce

As per gist in link.

Expected behavior

  1. CallbackUrl - should only contain one in the verify url.

  2. Once user is logged in, user should be able to view a protected route without redirect to the login page.

msalahz commented 11 months ago

I'm having the second issue.

Once logged in correctly when navigating to a protected route you get redirected to the login page again. Never able to access the protected route.

dearcoco commented 11 months ago

I'm having the same issue with the second one. It works fine locally, but when I deploy to vercel, I get this issue.

// middleware.ts
...
export const config = {
    matcher: ["/profile/:path*", "/protected/:path*", "/dashboard/:path*"],
};
msalahz commented 10 months ago

any updates here?

tmackness commented 10 months ago

I'm having the same issue with the second one.

It works fine locally, but when I deploy to vercel, I get this issue.


// middleware.ts

...

export const config = {

  matcher: ["/profile/:path*", "/protected/:path*", "/dashboard/:path*"],

};

Are you using --turbo in dev mode?

tmackness commented 10 months ago

any updates here?

Still nothing.

mikekellyio commented 10 months ago

I'm having the 2nd issue when using next 13.4.12, going back to 13.4.11 seems to have fixed the issue.

tmackness commented 10 months ago

I'm having the 2nd issue when using next 13.4.12, going back to 13.4.11 seems to have fixed the issue.

That didn't work for me.

Laecherlich commented 10 months ago

I am having the same issue. Any updates?

msalahz commented 10 months ago

I managed to work around this issue in v4.23.1 by using withAuth in the middleware as you can see in the following code example.

It was inspired by next.js example in next-auth Github repository

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

// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
export default withAuth({
  callbacks: {
    authorized({ req, token }) {
      // matcher array will be only requires the user to be logged in
      return !!token
    },
  },
})

export const config = { matcher: ['/protected'] }
msalahz commented 10 months ago

I managed to work around this issue in v4.23.1 by using withAuth in the middleware as you can see in the following code example.

It was inspired by next.js example in next-auth Github repository

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

// More on how NextAuth.js middleware works: https://next-auth.js.org/configuration/nextjs#middleware
export default withAuth({
  callbacks: {
    authorized({ req, token }) {
      // matcher array will be only requires the user to be logged in
      return !!token
    },
  },
})

export const config = { matcher: ['/protected'] }

Currently this is not working due to null token 😞

kyen99 commented 10 months ago

I believe the reason for issue 2 in the original post and @msalahz issue is withAuth middleware ONLY supports JWT and not database sessions. This page buried in the docs notes this: https://next-auth.js.org/tutorials/securing-pages-and-api-routes#nextjs-middleware

netgfx commented 8 months ago

trying to get this to work with the following:

export default withAuth(
  // `withAuth` augments your `Request` with the user's token.
  function middleware(req, res) {
    console.log("middleware check: ", req.url, req.nextauth.token);
  },
  {
    callbacks: {
      authorized: ({ req, token }) => {
        const res = NextResponse.next();

        if (token) {
          return res;
        } else {
          return NextResponse.redirect(new URL("/login", req.nextUrl.origin));
        }
      },
    },
  }
);

which doesn't do anything if the token is null, however if I return false it redirects to login, but with the callbackUrl= parameter, is there a way to omit the callback parameter from the url? Honestly it should be more straightforward to check the token and simply redirect "somewhere"

yesl-kim commented 7 months ago

I believe the reason for issue 2 in the original post and @msalahz issue is withAuth middleware ONLY supports JWT and not database sessions. This page buried in the docs notes this: https://next-auth.js.org/tutorials/securing-pages-and-api-routes#nextjs-middleware

I had the same issue with next.js 13, next auth@beta, and Prisma adapter.

And just adding { session: { strategy: 'jwt' } } to options, it worked! I think it's because of what it said above.

SarthakSKumar commented 5 months ago

A work around for the problem is here!!

Unfortunately those of us who are using alternate authentication methods such as session-based authentication don't have anything out of the box, and implementing a middleware like that yourself is trickier than you would expect, because you cannot simply pass the request in your middleware to getSession

This seems to be because the next-auth accesses headers via req.headers.cookie, but the type of the headers inside middleware is not an object, but a Headers object which must be accessed through req.headers.get("cookie")

I have implemented a middleware that works for session-based authentication. It does this by converting the relevant part of the request headers to an object

import type { NextFetchEvent, NextRequest } from 'next/server';
import { getSession } from 'next-auth/react';
import { NextResponse } from 'next/server';

export async function middleware(req: NextRequest, ev: NextFetchEvent) {
  const requestForNextAuth = {
    headers: {
      cookie: req.headers.get('cookie'),
    },
  };

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

  if (session) {
    console.log(session);

    // validate your session here

    return NextResponse.next();
  } else {
    // the user is not logged in, redirect to the sign-in page
    const signInPage = '/auth/signin';
    const signInUrl = new URL(signInPage, req.nextUrl.origin);
    signInUrl.searchParams.append('callbackUrl', req.url);
    return NextResponse.redirect(signInUrl);
  }
}

However I think this means that an extra fetch call will be made to the next-auth backend. One in the middleware, and one later on if you want to access the session in API calls.

theMillenniumFalcon commented 3 weeks ago
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
import { getSession } from "next-auth/react"

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

    const session = await getSession({ request: requestForNextAuth })

    if (session) {
        console.log('Session is: ', session)

        return NextResponse.next()
    } else {
        console.log("There is no session")
    }
}

export const config = {
  matcher: '/',
}

can someone help me with the type error: Screenshot (101) looks like a skill issue to me...