[GH-ISSUE #8906] Link-social callback should look up existing accounts by providerId #11232

Closed
opened 2026-04-13 07:34:40 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @skirdey on GitHub (Apr 2, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8906

What happened?

In better-auth@1.5.6, the social account link callback does a provider-agnostic lookup:

const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id));

That lives in dist/api/routes/callback.mjs in the if (link) { ... } branch.

The internal adapter already exposes a provider-aware helper:

findAccountByProviderId(accountId, providerId)

Using findAccount(accountId) here means the link flow can match an existing account row from the wrong provider if two providers produce the same external subject/account id string. In that case the callback can update the wrong linked account row instead of the provider-specific one.

Expected behavior

The link-social callback should look up the existing linked account by both accountId and providerId.

Proposed fix

Replace:

const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id));

with:

const existingAccount = await c.context.internalAdapter.findAccountByProviderId(String(userInfo.id), provider.id);

Additional context

We verified this is still present in the latest npm release (better-auth@1.5.6) and are carrying the above one-line patch downstream to avoid provider-agnostic updates during account linking.

If helpful, I can open a PR with the exact change.

Originally created by @skirdey on GitHub (Apr 2, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8906 ### What happened? In `better-auth@1.5.6`, the social account link callback does a provider-agnostic lookup: ```js const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id)); ``` That lives in `dist/api/routes/callback.mjs` in the `if (link) { ... }` branch. The internal adapter already exposes a provider-aware helper: ```js findAccountByProviderId(accountId, providerId) ``` Using `findAccount(accountId)` here means the link flow can match an existing account row from the wrong provider if two providers produce the same external subject/account id string. In that case the callback can update the wrong linked account row instead of the provider-specific one. ### Expected behavior The link-social callback should look up the existing linked account by both `accountId` and `providerId`. ### Proposed fix Replace: ```js const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id)); ``` with: ```js const existingAccount = await c.context.internalAdapter.findAccountByProviderId(String(userInfo.id), provider.id); ``` ### Additional context We verified this is still present in the latest npm release (`better-auth@1.5.6`) and are carrying the above one-line patch downstream to avoid provider-agnostic updates during account linking. If helpful, I can open a PR with the exact change.
GiteaMirror added the bug label 2026-04-13 07:34:40 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Apr 2, 2026):

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

I can confirm this bug is real. The callback route at line 196-198 of callback.ts indeed uses findAccount(String(userInfo.id)) which only looks up by accountId without filtering by provider [1].

Notably, the generic OAuth plugin already uses the correct, provider-aware lookup:

const existingAccount =
    await ctx.context.internalAdapter.findAccountByProviderId(
        String(userInfo.id),
        providerConfig.providerId,
    );

[2]

So there's a clear inconsistency — the generic OAuth plugin does the right thing, but the main social callback does not.

For additional context, there have been several related fixes in this area recently:

  • PR #7331 refactored findOAuthUser to do proper DB-level filtering by both accountId and providerId [3]
  • PR #7216 fixed an incorrect field comparison (acc.id vs acc.accountId) in the callback route [4]

But neither of those addressed this specific findAccountfindAccountByProviderId change in the link branch. A PR with the one-line fix you described would be welcome.

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:4174111973 --> @dosubot[bot] commented on GitHub (Apr 2, 2026): <!-- Greeting --> Hi @skirdey! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> I can confirm this bug is real. The callback route at [line 196-198 of `callback.ts`](https://github.com/better-auth/better-auth/blob/51518c372dd328041f772db3fd5d7f2b3cb4f3de/packages/better-auth/src/api/routes/callback.ts#L196-L198) indeed uses `findAccount(String(userInfo.id))` which only looks up by `accountId` without filtering by provider [[1]](https://github.com/better-auth/better-auth/blob/51518c372dd328041f772db3fd5d7f2b3cb4f3de/packages/core/src/types/context.ts#L192). Notably, the **generic OAuth plugin** already uses the correct, provider-aware lookup: ```typescript const existingAccount = await ctx.context.internalAdapter.findAccountByProviderId( String(userInfo.id), providerConfig.providerId, ); ``` [[2]](https://github.com/better-auth/better-auth/blob/51518c372dd328041f772db3fd5d7f2b3cb4f3de/packages/better-auth/src/plugins/generic-oauth/routes.ts#L461-L465) So there's a clear inconsistency — the generic OAuth plugin does the right thing, but the main social callback does not. For additional context, there have been several related fixes in this area recently: - **PR #7331** refactored `findOAuthUser` to do proper DB-level filtering by both `accountId` and `providerId` [[3]](https://github.com/better-auth/better-auth/pull/7331) - **PR #7216** fixed an incorrect field comparison (`acc.id` vs `acc.accountId`) in the callback route [[4]](https://github.com/better-auth/better-auth/pull/7216) But neither of those addressed this specific `findAccount` → `findAccountByProviderId` change in the link branch. A PR with the one-line fix you described would be welcome. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=ad5ebb77-2895-4c2f-a319-70a6f90de01e&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&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=I%20can%20confirm%20this%20bug%20is%20real.%20The%20callback%20route%20at%20%5Bline%20196-198%20of%20%60callback.ts%60%5D%28https%3A//github.com/better-auth/better-auth/blob/51518c372dd328041f772db3fd5d7f2b3cb4f3de/packages/better-auth/src/api/routes/callback.ts%23L196-L198%29%20indeed%20uses%20%60findAccount%28String%28userInfo.id%29%29%60%20which%20only%20looks%20up%20by%20%60accountId%60%20without%20filtering%20by%20provider%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/51518c372dd328041f772db3fd5d7f2b3cb4f3de/packages/core/src/types/context.ts%23L192%29.%0A%0ANotably%2C%20the%20%2A%2Ageneric%20OAuth%20plugin%2A%2A%20already%20uses%20the%20correct%2C%20provider-aware%20lookup%3A%0A%0A%60%60%60typescript%0Aconst%20existingAccount%20%3D%0A%20%20%20%20await%20ctx.context.internalAdapter.findAccountByProviderId%28%0A%20%20%20%20%20%20%20%20String%28userInfo.id%29%2C%0A%20%20%20%20%20%20%20%20providerConfig.providerId%2C%0A%20%20%20%20%29%3B%0A%60%60%60%0A%0A%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/51518c372dd328041f772db3fd5d7f2b3cb4f3de/packages/better-auth/src/plugins/generic-oauth/routes.ts%23L461-L465%29%0A%0ASo%20there%27s%20a%20clear%20inconsistency%20%E2%80%94%20the%20generic%20OAuth%20plugin%20does%20the%20right%20thing%2C%20but%20the%20main%20social%20callback%20does%20not.%0A%0AFor%20additional%20context%2C%20there%20have%20been%20several%20related%20fixes%20in%20this%20area%20recently%3A%0A%0A-%20%2A%2APR%20%237331%2A%2A%20refactored%20%60findOAuthUser%60%20to%20do%20proper%20DB-level%20filtering%20by%20both%20%60accountId%60%20and%20%60providerId%60%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/pull/7331%29%0A-%20%2A%2APR%20%237216%2A%2A%20fixed%20an%20incorrect%20field%20comparison%20%28%60acc.id%60%20vs%20%60acc.accountId%60%29%20in%20the%20callback%20route%20%5B%5B4%5D%5D%28https%3A//github.com/better-auth/better-auth/pull/7216%29%0A%0ABut%20neither%20of%20those%20addressed%20this%20specific%20%60findAccount%60%20%E2%86%92%20%60findAccountByProviderId%60%20change%20in%20the%20link%20branch.%20A%20PR%20with%20the%20one-line%20fix%20you%20described%20would%20be%20welcome.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&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/8906)
Author
Owner

@jaydeep-pipaliya commented on GitHub (Apr 6, 2026):

Hi, I'd like to work on this — could you assign it to me? I've already reproduced the bug and have a fix with a regression test ready.

<!-- gh-comment-id:4191746636 --> @jaydeep-pipaliya commented on GitHub (Apr 6, 2026): Hi, I'd like to work on this — could you assign it to me? I've already reproduced the bug and have a fix with a regression test ready.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#11232