[GH-ISSUE #6392] Allow explicit 'linkSocial' while disabling implicit auto-linking on Sign In #10502

Closed
opened 2026-04-13 06:41:33 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @Dayql on GitHub (Nov 28, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6392

Originally assigned to: @Paola3stefania on GitHub.

Is this suited for github?

  • Yes, this is suited for github

I am trying to implement a strict security flow regarding OAuth account linking (OWASP recommendation).

My requirements are:

  1. Strict Sign In: If a user tries to sign in with any social provider (Google, GitHub, etc.) but already has a Password account with the same email, I want to BLOCK the automatic login/merge. The user should be prompted to login with their password first.
  2. Manual Linking: Once the user is authenticated (logged in via password), they should be able to explicitly link that provider from their settings page using authClient.linkSocial.

The Conflict:
Currently, the accountLinking configuration seems to control both behaviors globally, creating a deadlock:

  • Scenario A: If I set accountLinking: { enabled: false }:

    • Sign In: It correctly blocks the implicit login (Good).
    • Settings: authClient.linkSocial fails with an error (Unable to link account), even though the user is explicitly requesting the link.
  • Scenario B: If I set accountLinking: { enabled: true }:

    • Settings: authClient.linkSocial works perfectly.
    • Sign In: It automatically merges the account on sign-in if emails match, which I want to avoid for security reasons.

Describe the solution you'd like

I believe authClient.linkSocial should be allowed to proceed if the user is already authenticated, regardless of the accountLinking.enabled global setting.

Alternatively, a more granular configuration would be great, for example:

accountLinking: {
    enabled: true, 
    implicitLinking: false, // New option? To prevent auto-merge on sign-in
    trustedProviders: ["google", "github", "discord"]
}

Describe alternatives you've considered

I have considered trying to intercept the sign-in flow manually using hooks to check for existing users in the database before allowing the OAuth flow to complete, but this feels like a workaround. A native configuration option to separate "implicit merging" from "explicit linking" would be much cleaner and safer.

Additional context

Here is my current config attempting to achieve this. It fails for any provider listed:

export const auth = betterAuth({
    accountLinking: {
        enabled: false, // I need this to prevent auto-merge
        trustedProviders: ["google", "github"] // Fails for both when attempting manual link
    }
});

When I call authClient.linkSocial with this config, I get the following error: "Unable to link account"

Originally created by @Dayql on GitHub (Nov 28, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6392 Originally assigned to: @Paola3stefania on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. I am trying to implement a strict security flow regarding OAuth account linking (OWASP recommendation). My requirements are: 1. **Strict Sign In:** If a user tries to sign in with **any social provider** (Google, GitHub, etc.) but already has a Password account with the same email, I want to **BLOCK** the automatic login/merge. The user should be prompted to login with their password first. 2. **Manual Linking:** Once the user is authenticated (logged in via password), they should be able to explicitly link **that provider** from their settings page using `authClient.linkSocial`. **The Conflict:** Currently, the `accountLinking` configuration seems to control both behaviors globally, creating a deadlock: * **Scenario A:** If I set `accountLinking: { enabled: false }`: * ✅ **Sign In:** It correctly blocks the implicit login (Good). * ❌ **Settings:** `authClient.linkSocial` fails with an error (`Unable to link account`), even though the user is explicitly requesting the link. * **Scenario B:** If I set `accountLinking: { enabled: true }`: * ✅ **Settings:** `authClient.linkSocial` works perfectly. * ❌ **Sign In:** It automatically merges the account on sign-in if emails match, which I want to avoid for security reasons. ### Describe the solution you'd like I believe `authClient.linkSocial` should be allowed to proceed if the user is **already authenticated**, regardless of the `accountLinking.enabled` global setting. Alternatively, a more granular configuration would be great, for example: ```typescript accountLinking: { enabled: true, implicitLinking: false, // New option? To prevent auto-merge on sign-in trustedProviders: ["google", "github", "discord"] } ``` ### Describe alternatives you've considered I have considered trying to intercept the sign-in flow manually using hooks to check for existing users in the database before allowing the OAuth flow to complete, but this feels like a workaround. A native configuration option to separate "implicit merging" from "explicit linking" would be much cleaner and safer. ### Additional context Here is my current config attempting to achieve this. It fails for any provider listed: ```typescript export const auth = betterAuth({ accountLinking: { enabled: false, // I need this to prevent auto-merge trustedProviders: ["google", "github"] // Fails for both when attempting manual link } }); ``` When I call authClient.linkSocial with this config, I get the following error: "Unable to link account"
GiteaMirror added the enhancementlocked labels 2026-04-13 06:41:33 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Nov 28, 2025):

(tag @better-auth-agent if you would like an answer from the Agent)

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3590084588 --> @better-auth-agent[bot] commented on GitHub (Nov 28, 2025): (tag @better-auth-agent if you would like an answer from the Agent) <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dosubot[bot] commented on GitHub (Nov 28, 2025):

Hi @Dayql! I'm Dosu and I’m helping the better-auth team.

Your understanding is correct: in better-auth, the accountLinking.enabled flag controls both implicit (auto-linking on sign-in) and explicit (manual linking via authClient.linkSocial) account linking globally. If you set enabled: false, both implicit and explicit linking are blocked; if true, both are allowed. There is currently no built-in way to allow explicit linking while blocking implicit auto-linking—both behaviors are tied to the same flag in the code and API logic, regardless of whether the user is authenticated or not (source, source).

There is no implicitLinking or similar granular option available at this time. Some plugin-specific options (like disableImplicitSignUp for Steam) exist, but they do not solve this for all providers or for the core logic (source).

Workarounds, such as intercepting the sign-in flow with hooks, have been discussed but are not natively supported or recommended as a clean solution (discussion). A feature request for more granular control (e.g., an implicitLinking flag) would be new and is not currently planned or documented.

References for further reading:

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3590088986 --> @dosubot[bot] commented on GitHub (Nov 28, 2025): <!-- Greeting --> Hi @Dayql! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> Your understanding is correct: in better-auth, the accountLinking.enabled flag controls both implicit (auto-linking on sign-in) and explicit (manual linking via authClient.linkSocial) account linking globally. If you set enabled: false, both implicit and explicit linking are blocked; if true, both are allowed. There is currently no built-in way to allow explicit linking while blocking implicit auto-linking—both behaviors are tied to the same flag in the code and API logic, regardless of whether the user is authenticated or not ([source](https://github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/api/routes/account.ts#L100-L376), [source](https://github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/oauth2/link-account.ts#L7-L248)). There is no implicitLinking or similar granular option available at this time. Some plugin-specific options (like disableImplicitSignUp for Steam) exist, but they do not solve this for all providers or for the core logic ([source](https://github.com/better-auth/better-auth/pull/4877)). Workarounds, such as intercepting the sign-in flow with hooks, have been discussed but are not natively supported or recommended as a clean solution ([discussion](https://github.com/better-auth/better-auth/issues/1745)). A feature request for more granular control (e.g., an implicitLinking flag) would be new and is not currently planned or documented. References for further reading: - [OAuth linking logic in code](https://github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/api/routes/account.ts#L100-L376) - [Related issue: global flag controls both behaviors](https://github.com/better-auth/better-auth/issues/1745) - [Discussion on linking flows and config](https://github.com/better-auth/better-auth/discussions/6385) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/66c340bf-2864-407c-8883-3d8d9d7238c8?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Your%20understanding%20is%20correct%3A%20in%20better-auth%2C%20the%20accountLinking.enabled%20flag%20controls%20both%20implicit%20%28auto-linking%20on%20sign-in%29%20and%20explicit%20%28manual%20linking%20via%20authClient.linkSocial%29%20account%20linking%20globally.%20If%20you%20set%20enabled%3A%20false%2C%20both%20implicit%20and%20explicit%20linking%20are%20blocked%3B%20if%20true%2C%20both%20are%20allowed.%20There%20is%20currently%20no%20built-in%20way%20to%20allow%20explicit%20linking%20while%20blocking%20implicit%20auto-linking%E2%80%94both%20behaviors%20are%20tied%20to%20the%20same%20flag%20in%20the%20code%20and%20API%20logic%2C%20regardless%20of%20whether%20the%20user%20is%20authenticated%20or%20not%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/api/routes/account.ts%23L100-L376%29%2C%20%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/oauth2/link-account.ts%23L7-L248%29%29.%0A%0AThere%20is%20no%20implicitLinking%20or%20similar%20granular%20option%20available%20at%20this%20time.%20Some%20plugin-specific%20options%20%28like%20disableImplicitSignUp%20for%20Steam%29%20exist%2C%20but%20they%20do%20not%20solve%20this%20for%20all%20providers%20or%20for%20the%20core%20logic%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/pull/4877%29%29.%0A%0AWorkarounds%2C%20such%20as%20intercepting%20the%20sign-in%20flow%20with%20hooks%2C%20have%20been%20discussed%20but%20are%20not%20natively%20supported%20or%20recommended%20as%20a%20clean%20solution%20%28%5Bdiscussion%5D%28https%3A//github.com/better-auth/better-auth/issues/1745%29%29.%20A%20feature%20request%20for%20more%20granular%20control%20%28e.g.%2C%20an%20implicitLinking%20flag%29%20would%20be%20new%20and%20is%20not%20currently%20planned%20or%20documented.%0A%0AReferences%20for%20further%20reading%3A%0A-%20%5BOAuth%20linking%20logic%20in%20code%5D%28https%3A//github.com/better-auth/better-auth/blob/8248dad72bac5685d1b9e7c7dd58dd734914b46e/packages/better-auth/src/api/routes/account.ts%23L100-L376%29%0A-%20%5BRelated%20issue%3A%20global%20flag%20controls%20both%20behaviors%5D%28https%3A//github.com/better-auth/better-auth/issues/1745%29%0A-%20%5BDiscussion%20on%20linking%20flows%20and%20config%5D%28https%3A//github.com/better-auth/better-auth/discussions/6385%29)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6392)
Author
Owner

@Dayql commented on GitHub (Dec 4, 2025):

Hi @bekacru,

The bot confirmed my understanding above, but I am a bit confused regarding issue #1745.

In that thread, it was suggested that setting accountLinking: { enabled: false } would disable the automatic merging on sign-in, and a user claimed that manual linking via linkSocial was still working with that config.

I just tested it on the latest version, and setting enabled: false definitely blocks linkSocial as well (throwing "Unable to link account").

This feature request is essentially to find a way to allow Explicit Linking (Manual) while blocking Implicit Linking (Auto-merge on sign-in) to prevent Account Takeover (ATO) via Automatic OAuth Account Linking.

Currently, it seems we can only have "All or Nothing", which forces us to choose between UX (manual linking) and Security (preventing auto-merge).

<!-- gh-comment-id:3611597361 --> @Dayql commented on GitHub (Dec 4, 2025): Hi @bekacru, The bot confirmed my understanding above, but I am a bit confused regarding issue #1745. In that thread, it was suggested that setting `accountLinking: { enabled: false }` would disable the automatic merging on sign-in, and a user claimed that manual linking via `linkSocial` was still working with that config. I just tested it on the latest version, and setting `enabled: false` definitely blocks `linkSocial` as well (throwing "Unable to link account"). This feature request is essentially to find a way to allow **Explicit Linking** (Manual) while blocking **Implicit Linking** (Auto-merge on sign-in) to prevent **Account Takeover (ATO) via Automatic OAuth Account Linking**. Currently, it seems we can only have "All or Nothing", which forces us to choose between UX (manual linking) and Security (preventing auto-merge).
Author
Owner

@itschip commented on GitHub (Jan 11, 2026):

@Dayql the intended behaviour according to docs, seem to be:

  1. You enable account linking as a whole
  2. Any provider in trustedProviders are allowed to auto-link. The rest must be manually linked.

However, like you say, even with accountLinking enabled and a empty list for trustedProviders it still auto-links.

bump @Bekacru , and let me know where to start looking if you need a hand.

<!-- gh-comment-id:3734888063 --> @itschip commented on GitHub (Jan 11, 2026): @Dayql the intended behaviour according to docs, seem to be: 1. You enable account linking as a whole 2. Any provider in trustedProviders are allowed to auto-link. The rest must be manually linked. However, like you say, even with accountLinking enabled and a empty list for `trustedProviders` it still auto-links. bump @Bekacru , and let me know where to start looking if you need a hand.
Author
Owner

@Bekacru commented on GitHub (Jan 11, 2026):

@itschip @Dayql Looking into this.

@Paola3stefania will report back once we decide on a solution

<!-- gh-comment-id:3735079526 --> @Bekacru commented on GitHub (Jan 11, 2026): @itschip @Dayql Looking into this. @Paola3stefania will report back once we decide on a solution
Author
Owner

@Paola3stefania commented on GitHub (Jan 11, 2026):

New disableImplicitLinking option - when enable blocks automatic account linking on sign-in while still allowing explicit linking via linkSocial()

<!-- gh-comment-id:3735422219 --> @Paola3stefania commented on GitHub (Jan 11, 2026): New **disableImplicitLinking** option - when enable blocks automatic account linking on sign-in while still allowing explicit linking via linkSocial()
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10502