From 3d805930b368e9a68ee1506484bf7997a8837c57 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Thu, 6 Oct 2022 17:44:50 -0700 Subject: [PATCH 1/4] Add opt-in for dangerous linking --- .../src/core/lib/callback-handler.ts | 43 +++++++++++-------- packages/next-auth/src/providers/oauth.ts | 1 + 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/next-auth/src/core/lib/callback-handler.ts b/packages/next-auth/src/core/lib/callback-handler.ts index 84bf95b80e..1f104183c6 100644 --- a/packages/next-auth/src/core/lib/callback-handler.ts +++ b/packages/next-auth/src/core/lib/callback-handler.ts @@ -6,6 +6,7 @@ import type { AdapterSession, AdapterUser } from "../../adapters" import type { JWT } from "../../jwt" import type { Account, User } from "../.." import type { SessionToken } from "./cookie" +import { OAuthConfig } from "src/providers" /** * This function handles the complex flow of signing users in, and either creating, @@ -180,25 +181,33 @@ export default async function callbackHandler(params: { ? await getUserByEmail(profile.email) : null if (userByEmail) { - // We end up here when we don't have an account with the same [provider].id *BUT* - // we do already have an account with the same email address as the one in the - // OAuth profile the user has just tried to sign in with. + const provider = options.provider as OAuthConfig; + if (provider?.allowDangerousEmailAccountLinking) { + // If you trust the oauth provider to correctly verify email addresses, you can opt-in to + // account linking even when the user is not signed-in. + user = userByEmail; + } else { + // We end up here when we don't have an account with the same [provider].id *BUT* + // we do already have an account with the same email address as the one in the + // OAuth profile the user has just tried to sign in with. + // + // We don't want to have two accounts with the same email address, and we don't + // want to link them in case it's not safe to do so, so instead we prompt the user + // to sign in via email to verify their identity and then link the accounts. + throw new AccountNotLinkedError( + "Another account already exists with the same e-mail address" + ) + } + } else { + // If the current user is not logged in and the profile isn't linked to any user + // accounts (by email or provider account id)... // - // We don't want to have two accounts with the same email address, and we don't - // want to link them in case it's not safe to do so, so instead we prompt the user - // to sign in via email to verify their identity and then link the accounts. - throw new AccountNotLinkedError( - "Another account already exists with the same e-mail address" - ) + // If no account matching the same [provider].id or .email exists, we can + // create a new account for the user, link it to the OAuth acccount and + // create a new session for them so they are signed in with it. + const { id: _, ...newUser } = { ...profile, emailVerified: null } + user = await createUser(newUser) } - // If the current user is not logged in and the profile isn't linked to any user - // accounts (by email or provider account id)... - // - // If no account matching the same [provider].id or .email exists, we can - // create a new account for the user, link it to the OAuth acccount and - // create a new session for them so they are signed in with it. - const { id: _, ...newUser } = { ...profile, emailVerified: null } - user = await createUser(newUser) await events.createUser?.({ user }) await linkAccount({ ...account, userId: user.id }) diff --git a/packages/next-auth/src/providers/oauth.ts b/packages/next-auth/src/providers/oauth.ts index 61e487b6cb..37f020d423 100644 --- a/packages/next-auth/src/providers/oauth.ts +++ b/packages/next-auth/src/providers/oauth.ts @@ -145,6 +145,7 @@ export interface OAuthConfig

extends CommonProviderOptions, PartialIssuer { requestTokenUrl?: string profileUrl?: string encoding?: string + allowDangerousEmailAccountLinking?: boolean; } /** @internal */ From 71e0f8f5fa4d8b0a1cc66c06f004307759129434 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Thu, 6 Oct 2022 18:52:56 -0700 Subject: [PATCH 2/4] Add documentation for allowDangerousEmailAccountLinking option. --- docs/docs/configuration/providers/oauth.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/docs/configuration/providers/oauth.md b/docs/docs/configuration/providers/oauth.md index 5dd7abfbfa..bd840a3d34 100644 --- a/docs/docs/configuration/providers/oauth.md +++ b/docs/docs/configuration/providers/oauth.md @@ -173,6 +173,7 @@ interface OAuthConfig { region?: string issuer?: string client?: Partial + allowDangerousEmailAccountLinking?: boolean } ``` @@ -278,6 +279,10 @@ If your Provider is OpenID Connect (OIDC) compliant, we recommend using the `wel An advanced option, hopefully you won't need it in most cases. `next-auth` uses `openid-client` under the hood, see the docs on this option [here](https://github.com/panva/node-openid-client/blob/main/docs/README.md#new-clientmetadata-jwks-options). +### `allowDangerousEmailAccountLinking` option + +Normally, when you sign in with an OAuth provider and another account with the same email address already exists, the accounts are not linked automatically. Automatic account linking on sign in is not secure between arbitrary providers and is disabled by default. However, it may be desireable to allow automatic account linking if you trust that the provider involved has securely verified the email address associated with the account. Just set `allowDangerousEmailAccountLinking: true` in your provider configuration to enable automatic account linking. + ## Using a custom provider You can use an OAuth provider that isn't built-in by using a custom object. @@ -404,6 +409,18 @@ GoogleProvider({ }) ``` +An example of how to enable automatic account linking: + +```js title=/api/auth/[...nextauth].js +import GoogleProvider from "next-auth/providers/google" + +GoogleProvider({ + clientId: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + allowDangerousEmailAccountLinking: true, +}) +``` + ### Adding a new built-in provider If you think your custom provider might be useful to others, we encourage you to open a PR and add it to the built-in list so others can discover it much more easily! From 5e56e15060a85e1d584146a9ba217d1ad740d584 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Thu, 20 Oct 2022 10:21:45 -0700 Subject: [PATCH 3/4] Add link to FAQ. --- docs/docs/configuration/providers/oauth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/configuration/providers/oauth.md b/docs/docs/configuration/providers/oauth.md index bd840a3d34..c8314b5f9f 100644 --- a/docs/docs/configuration/providers/oauth.md +++ b/docs/docs/configuration/providers/oauth.md @@ -281,7 +281,7 @@ An advanced option, hopefully you won't need it in most cases. `next-auth` uses ### `allowDangerousEmailAccountLinking` option -Normally, when you sign in with an OAuth provider and another account with the same email address already exists, the accounts are not linked automatically. Automatic account linking on sign in is not secure between arbitrary providers and is disabled by default. However, it may be desireable to allow automatic account linking if you trust that the provider involved has securely verified the email address associated with the account. Just set `allowDangerousEmailAccountLinking: true` in your provider configuration to enable automatic account linking. +Normally, when you sign in with an OAuth provider and another account with the same email address already exists, the accounts are not linked automatically. Automatic account linking on sign in is not secure between arbitrary providers and is disabled by default (see our [Security FAQ](https://next-auth.js.org/faq#security)). However, it may be desirable to allow automatic account linking if you trust that the provider involved has securely verified the email address associated with the account. Just set `allowDangerousEmailAccountLinking: true` in your provider configuration to enable automatic account linking. ## Using a custom provider From 60b1faff42c3258ca1039a7b5867224f907130c3 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Thu, 20 Oct 2022 10:42:54 -0700 Subject: [PATCH 4/4] Fix nullish coalescing operator linting error. --- packages/next-auth/src/utils/detect-host.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next-auth/src/utils/detect-host.ts b/packages/next-auth/src/utils/detect-host.ts index 2e4a1dda63..8c2d346492 100644 --- a/packages/next-auth/src/utils/detect-host.ts +++ b/packages/next-auth/src/utils/detect-host.ts @@ -1,7 +1,7 @@ /** Extract the host from the environment */ export function detectHost(forwardedHost: any) { // If we detect a Vercel environment, we can trust the host - if (process.env.VERCEL || process.env.AUTH_TRUST_HOST) + if (process.env.VERCEL ?? process.env.AUTH_TRUST_HOST) return forwardedHost // If `NEXTAUTH_URL` is `undefined` we fall back to "http://localhost:3000" return process.env.NEXTAUTH_URL