nextauthjs / next-auth

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

Encountering OAuthAccountNotLinked Error on Subsequent Google Sign-Ins [Drizzle ORM] #10062

Open alexmartinezm opened 7 months ago

alexmartinezm commented 7 months ago

Adapter type

@auth/drizzle-adapter

Environment

  System:
    OS: macOS 14.3.1
    CPU: (11) arm64 Apple M3 Pro
    Memory: 178.80 MB / 18.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.11.1 - /usr/local/bin/node
    npm: 10.2.4 - /usr/local/bin/npm
    pnpm: 8.15.3 - /usr/local/bin/pnpm
    Watchman: 2024.01.22.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome: 121.0.6167.184
    Edge: 121.0.2277.128
    Safari: 17.3.1
  npmPackages:
    @auth/drizzle-adapter: ^0.7.0 => 0.7.0 
    next: 14.1.0 => 14.1.0 
    next-auth: 5.0.0-beta.11 => 5.0.0-beta.11 
    react: ^18.2.0 => 18.2.0 

Reproduction URL

https://github.com/wpcodevo/nextauth-nextjs14-drizzle/tree/main

Describe the issue

We're experiencing an issue where users receive an OAuthAccountNotLinked error when they attempt to sign in with Google after their initial account creation and sign-out. Despite implementing allowDangerousEmailAccountLinking: true, our database shows duplicate entries in the accounts table for the same user upon a second and subsequent sign-in attempt.

The repo link uses beta-8 but it also happens with beta-11.

How to reproduce

  1. User signs up with Google OAuth provider.
  2. User signs out.
  3. User attempts to sign in again with the same Google account.
  4. The OAuthAccountNotLinked error occurs.

Expected behavior

The expected behavior is that a user should be able to sign in with the same Google account multiple times without encountering linking issues, assuming allowDangerousEmailAccountLinking: true is set to allow email-based account linking.

jankaifer commented 7 months ago

I'm hitting the same issue with Drizzle and Google. Have you tried to debug this? Like if using different database adapters solves it?

I want to dig into this so I'd be glad for any helpful observations that can help me fix it.

EDIT: I'll be using only google and I need better control over refresh tokens so I'll just roll my own solution instead.

EDIT2: actually I can still use auth.js with dangerous linking (I only support google so it's safe). Then when generating a session I just look for some account in the db that has a refresh token (only the first one has it when using google).

LeoSM-07 commented 7 months ago

I'm also facing this issue using the exact same config for DrizzleAdapter (SQLite) and GoogleProvider listed here. Users can log in the first time, but after logging out, subsequent login attempts return the OAuthAccountNotLinked error when trying to sign in. To be clear, I don't want to allow dangerous email linking, I just want users to be able to sign into their single Google OAuth account, but at this point, they can't do that more than once. Is there anything I can do to fix this in the meantime? Thanks

alexmartinezm commented 7 months ago

I'm also facing this issue using the exact same config for DrizzleAdapter (SQLite) and GoogleProvider listed here. Users can log in the first time, but after logging out, subsequent login attempts return the OAuthAccountNotLinked error when trying to sign in. To be clear, I don't want to allow dangerous email linking, I just want users to be able to sign into their single Google OAuth account, but at this point, they can't do that more than once. Is there anything I can do to fix this in the meantime? Thanks

Unfortunately, just allow the allowDangerousEmailAccountLinking. You can also try the workaround explained by @jankaifer in his 2nd edit.

ThangHuuVu commented 7 months ago

allowDangerousEmailAccountLinking should not be necessary because you're using only the Google provider. This bug is supposed to be fixed in https://github.com/nextauthjs/next-auth/pull/10008, let us check your reproduction and get back to you. @ndom91 maybe you could help with this? 🙏

ndom91 commented 7 months ago

Hmm yeah i think it might be because of the providerAccountId thing we fixed recently.

Did you initially create your users in your DB with beta.8? There was a bug in how the providerAccountIds were set in that version which is fixed now.

So users logging in with beta.13 now will not match against what was saved for them originally.

More details here: https://github.com/nextauthjs/next-auth/issues/9992#issuecomment-1944062556

Long story short, if the users were created in your DB while you were using beta.7/8/9 you'll need to either:

DeveloperOskar commented 7 months ago

I've encountered the same issue within SvelteKit Auth using the Drizzle adapter. When signing up for the first time with either GitHub or Google, and then subsequently signing out, attempting to log in again results in an "OAuthAccountNotLinked" error.

In the DB it looks like my "account" table gets 2 records with the same email and provider

ndom91 commented 7 months ago

@DeveloperOskar which versions of @auth/sveltekit are you using? Are these newly created users?

DeveloperOskar commented 7 months ago

It is just a side project, and yes they are newly created, then I log out and can not log back in because i get "OAuthAccountNotLinked" and see 2 accounts in the "account" table with the same email and provider.

"@auth/core": "^0.25.1",
"@auth/sveltekit": "^0.11.1",

hooks.server.js:

export const handle = sequence(
    SvelteKitAuth({
        providers: [
            GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET }),
            Google({
                clientId: GOOGLE_ID,
                clientSecret: GOOGLE_SECRET
            })
        ],
        pages: {
            signIn: 'sign-in'
        },
        callbacks: {
            session: ({ session, user }) => ({
                ...session,
                user: {
                    ...session.user,
                    id: user.id
                }
            })
        },
        adapter: DrizzleAdapter(db, mysqlTable)
    }),
    authorization
);
ndom91 commented 7 months ago
"@auth/core": "^0.25.1",
"@auth/sveltekit": "^0.11.1",

Hey thanks for all the info.

First of all, you shouldn't need to install core as well. Simply @auth/sveltekit should do the trick. What are you using core for exactly?

Also were up to 0.13.0 of the sveltekit package, could you try that?

The setup has changed slightly, see the instructions here: https://authjs.dev/reference/sveltekit#usage

The sveltekit setup is now like the next.js one, in that you want to have a src/auth.ts which exports a few methods returned from SvelteKitAuth() including a handle method which you'd import and use in your src/hooks.server.ts.

Thanks!

DeveloperOskar commented 7 months ago

Thank you @ndom91 for the help, looks like upgrading to 0.13.0 helped and using the updated instructions that you provided!

I uninstalled the CorePackage as well but I used it to get a typed type of the Account type in the "account" table image

ndom91 commented 7 months ago

Ah gotcha, yeah those are exported from the sveltekit package now as well 👍

We wanted to avoid having people have to install both packages

jdordoigne commented 6 months ago

Same issue here with beta15. providerAccountId from getUserByAccount keeps changing over each signin, it's not the one coming from the token sub

ndom91 commented 6 months ago

@jdordoigne can you provide some more details about your use-case? What version of next-auth, your config, what provider(s) are you trying to signin with and are you using a db or no, etc?

There were some previous verions where this was broken and providerAccountId was being returned as crypto.randomUUID() basically. But this should be fixed as of beta.10 or so. See packages/core/src/lib/actions/callback/oauth/callback.ts:213.

jpulec commented 6 months ago

If anyone else still has an issue with this, I discovered that in my case it was because I had a custom profile() function for my provider, and this working correctly requires that the default profile handler runs.

phil-w commented 5 months ago

There seem to be two separate issues in this thread.

1 Linked Accounts

I went around the loop on that issue myself, having forgotten to mark one of my providers as "safe".

I note only that the error page for this error... isn't the error page, it looks like it's probably the config.pages.signIn location with a query string etc.

I had expected my custom config.pages.error pages to be shown, with that same query string. I just wrote some code to navigate the user with this error away from config.pages.signIn to the config.pages.error.

2 Custom profiles cause multiple account table entries

If anyone else still has an issue with this, I discovered that in my case it was because I had a custom profile() function for my provider, and this working correctly requires that the default profile handler runs.

I thought the profile callback was for the user profile (so user table), not the accounts table, but looking at it the callbacks don't seem to map 1:1 onto the database tables anyway. I can't really not have user roles, so I have to put up with multiple account table entries for a single user/ account. The difference in the entries (eg for Azure AD) is just the provider ID, but that isn't in the account callback, so I can't see how to strip it out.

Anyway, I wrote a scheduled task which runs each night and zaps all those unwanted accounts collection entries, leaving only the latest of the set. It would be nice if "provider ID", which is useless to me, could be stripped out.

It's slightly confusing that the call backs don't map onto the database collections, if you see what I mean.

4rch1m3d35 commented 3 months ago

I have the same issue with OAuthAccountNotLinked with Google and the Drizzle Adapter(mysql2).

corepack@0.28.2 next@14.2.4 next-auth@5.0.0-beta.19

Edit: I went from next-auth 4 directly to beta.19 and the user was not made in next-auth 4