Prompt
account linking OAuth duplicate users
Latest observation
Oct 6, 2026
OpenAI APIWeb search: off
If you’re seeing duplicate users with OAuth account linking, the usual cause is that your app is creating a new local user record on each OAuth login instead of matching an existing user and attaching the provider identity to it.
Common causes
-
No stable linking key
- You’re using email alone, but:
- email may be missing from the provider,
- email may not be verified,
- the same person may use different emails on different providers.
- You’re using email alone, but:
-
Incorrect login flow
- “Sign in with Google” always creates a user if one doesn’t exist, even if the user already signed up with password.
- Then “Sign in with GitHub” creates another row.
-
Not storing provider account IDs
- You need to store something like:
provider=googleprovider_user_id=123456789
- That pair should map to a single internal user.
- You need to store something like:
-
Race conditions
- Two login/link requests happen at the same time and both create separate users.
- Missing unique constraints makes this worse.
Recommended model
Use two tables:
users
Internal user profile
idemailname- etc.
oauth_accounts
Linked identities
iduser_idproviderprovider_user_idaccess_token/refresh_tokenif needed
Important unique constraints
UNIQUE(provider, provider_user_id)- optionally
UNIQUE(email)if your business rules allow it - sometimes
UNIQUE(user_id, provider)if only one account per provider per user
Correct linking logic
When OAuth callback happens:
- Look up by
provider + provider_user_id- If found: log that user in.
- If not found:
- If user is already authenticated in your app, link the OAuth account to that existing user.
- Else, if you trust verified email and find a matching user, decide whether to auto-link or require confirmation.
- Else, create a new user and link the OAuth account.
Safe flow example
User is already signed in and adds Google
- Current session user:
user_42 - Google callback returns
provider_user_id = abc123 - Insert into
oauth_accounts(user_id=42, provider='google', provider_user_id='abc123')
User logs in with Google for the first time
- Check
oauth_accounts - If not found:
- Create user
- Insert linked OAuth account
How to prevent duplicates
- Put a unique index on
(provider, provider_user_id) - Handle “create or link” in a transaction
- If you auto-link by email, only do it when:
- email is verified by provider
- your security policy allows it
- Normalize emails if needed:
- lowercase
- trim whitespace
- but don’t over-normalize provider-specific email semantics unless you understand the impact
If duplicates already exist
You’ll need a merge process:
- Pick a canonical user.
- Reassign all linked OAuth accounts, sessions, and app data to the canonical user.
- Mark duplicates as merged/disabled.
- Add constraints to stop it happening again.
Quick fix checklist
- Store
(provider, provider_user_id)per account - Add unique constraint on that pair
- Link OAuth to existing signed-in user when applicable
- Don’t create a new user if the provider account is already linked
- Review email-based auto-linking rules
- Add transaction/locking for callback handling
If you want, I can also give you:
- a database schema
- a Node/Express example
- a NextAuth/Auth.js account linking setup
- a Django / Laravel / Rails implementation