nextauthjs / next-auth

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

Next Auth [v5.0.0-beta.4] Middleware and Scope not working #9448

Open siinghd opened 9 months ago

siinghd commented 9 months ago

Environment

System: OS: Windows 11 10.0.22621 CPU: (8) x64 Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz Memory: 2.78 GB / 11.92 GB Binaries: Node: 18.18.2 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD npm: 10.2.5 - C:\Program Files\nodejs\npm.CMD pnpm: 8.10.3 - C:\Program Files\nodejs\pnpm.CMD Browsers: Edge: Chromium (120.0.2210.77) Internet Explorer: 11.0.22621.1 npmPackages: @auth/core: ^0.19.0 => 0.19.0 @auth/prisma-adapter: ^1.0.12 => 1.0.12 next: 14.0.4 => 14.0.4 next-auth: 5.0.0-beta.4 => 5.0.0-beta.4 react: ^18.2.0 => 18.2.0

Reproduction URL

https://github.com/siinghd/question-tracker

Describe the issue

I am using the NextAuth beta to implement a login feature using the Discord provider and Prisma adapter. I am encountering two primary issues:

Issue 1: Scope Not Working as Expected

The specified scope in auth.config.ts doesn't seem to be applied. Instead, the default scope is used. Here's the relevant code snippet:

const scopes = ['identify', 'guilds'];
export default {
  providers: [
    DiscordProvider({
      clientId: process.env.DISCORD_CLIENT_ID || '',
      clientSecret: process.env.DISCORD_CLIENT_SECRET || '',
      authorization: { params: { scope: scopes.join(' ') } },
    }),
  ],
};

Issue 2: Middleware Not Redirecting Properly

The middleware intended to protect the root path / and redirect unauthenticated users to /login is not functioning as expected. The .env file includes AUTH_SECRET and AUTH_URL.

How to reproduce

  1. Set up the NextAuth configuration with the Discord provider and Prisma adapter.
  2. Define scopes as ['identify', 'guilds'].
  3. Implement middleware to protect the root path and redirect to /login.
  4. Observe that the specified scopes are not applied and the middleware does not redirect as intended.

Expected behavior

  1. The Discord authentication should use the specified scopes (identify, guilds).
  2. Unauthenticated users trying to access the root path should be redirected to the /login page.

Actual Behavior:

  1. The default scope is used instead of the specified custom scopes.
  2. The middleware does not redirect unauthenticated users to /login.
siinghd commented 9 months ago

Update:

fixed the middleware problem by using:

 authorized({ auth, request: { nextUrl } }) {
      const isLoggedIn = !!auth?.user;

      const unprotectedPaths = ['/login'];

      const isProtected = !unprotectedPaths.some((path) =>
        nextUrl.pathname.startsWith(path)
      );

      if (isProtected && !isLoggedIn) {
        const redirectUrl = new URL('api/auth/signin', nextUrl.origin);
        redirectUrl.searchParams.append('callbackUrl', nextUrl.href);
        return Response.redirect(redirectUrl);
      }

      return true;
    },

The scope one still remains

siinghd commented 9 months ago

Update 2:

Solved the scope issue by doing

await signIn('discord', {}, 'scope=identify guilds');

I do not know if this is the correct way to do it but seems working.

Let me know if this is ok or not.

me-imfhd commented 9 months ago

It's great to see you here, @siinghd. Would it be alright if I share an issue I'm experiencing with the next-auth beta version? Specifically, the auth() function doesn't seem to be working in pages routers, even when I pass req and res as arguments. Interestingly, it works fine in the app router, but I have a dependency on pages routers and need to use auth() there. Any insights or assistance would be appreciated.

me-imfhd commented 9 months ago

Update: The issue has already been raised here, my apologies. https://github.com/nextauthjs/next-auth/issues/9307

rikurainio commented 9 months ago

@siinghd

I used this method:

providers: [
    DiscordProvider({
      clientId: process.env.DISCORD_CLIENT_ID as string,
      clientSecret: process.env.DISCORD_CLIENT_SECRET as string,
      authorization: "https://discord.com/api/oauth2/authorize?scope=identify+guilds+guilds.members.read",

Checked the discordProvider in 5.0.0-beta.4 image

Just seems to take authorization as this string. Don't know how it should be passed. Many unofficial examples show the params: { scope: ... } way. At least in discord provider's case.

balazsorban44 commented 8 months ago

The provided code should work. Likely a bug in our merging of the default config with the user provided one:

https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/lib/utils/providers.ts

https://github.com/nextauthjs/next-auth/blob/d85269d1169d068448b831d2ee9517e4673c78b8/packages/core/src/providers/discord.ts#L143

nic-vo commented 3 months ago

Reviving this old issue because I may have found the source of the bug: merge assumes that arg target is always an object.

Attempting to override this authorization default fails: example provider

The merge function in question: function

1) merge gets called recursively if source[key] is an object 2) But if the target value for that key is NOT an object, merge skips the override silently and returns the default. (if condition checks isObject(target) but target[key] is technically any, potentially ignoring supplied config for merging)

The Spotify provider above provides a string default for authorization, so it will not be overridden even if a config is provided as specified in the docs.

Possible solutions:

  1. Any provider that allows configuration must have fixes like #9866 to expose the desired values for overriding via a config object. I.e. provider default values must be objects to support deep merge, not strings or otherwise
  2. Mark this is as intentional? Any non-object default values are not supposed to be overridden by config supplied to provider function?
  3. Take a look at merge so it can override any default