Prompt
How do I let users sign in with different providers but keep one account per person?
Latest observation
Use a single internal user account and attach multiple sign-in identities to it.
Core idea
Don’t make the external provider account your user account. Instead:
- Create one
Userrecord in your app - Create a separate
AuthIdentity/UserLogin/AccountLinkrecord for each provider login - Each identity stores:
- provider name (
google,github,apple, etc.) - provider user ID / subject
- the internal
user_idit belongs to
- provider name (
That way, the same person can sign in with Google today and GitHub tomorrow, but your app still sees one user.
Recommended data model
Users
users
- id
- email
- name
- ...
Linked identities
auth_identities
- id
- user_id
- provider
- provider_subject // unique ID from the provider
- email_at_provider // optional
- created_at
Add a uniqueness constraint:
UNIQUE(provider, provider_subject)
If you allow account linking by email:
UNIQUE(user_id, provider)if you only want one Google, one GitHub, etc. per user
Sign-in flow
1) User signs in with a provider
Example: Google returns:
- provider =
google - provider_subject =
1234567890 - email =
alice@example.com
2) Check if that provider identity already exists
- If yes: sign in the linked
user_id - If no: decide whether to:
- create a new user, or
- link to an existing user, usually by verified email
How to keep one account per person
Best practice: link by verified email
If Google/GitHub/Apple gives a verified email matching an existing user:
- show a “We found an existing account” step, or
- automatically link if your security policy allows it
But be careful:
- not all providers verify email equally well
- some providers may return no email
- email matching alone can be risky if the email isn’t verified
Safer account-linking options
Option A: User-initiated linking
When the user is already signed in:
- They go to “Connected accounts”
- Click “Connect Google”
- Complete OAuth
- You add the new identity to the same
user_id
This is the safest approach.
Option B: Email-based auto-linking
If provider email is verified and matches an existing account:
- Prompt the user to confirm linking
- Or auto-link if your product accepts that risk
Option C: Require re-authentication
Before linking a new provider to an existing account:
- ask the user to sign in to the current account again
- then link the new identity
Important security rules
- Never merge accounts automatically just because names match
- Prefer provider subject ID over email for identity lookup
- Treat email as a secondary matching signal only if verified
- Make account linking explicit when possible
- Allow users to see/remove connected providers
- Ensure at least one login method remains attached, or provide recovery flow
Practical example
A user has one account in your app:
users.id = 42
They sign in with:
- Google identity:
google|123 - GitHub identity:
github|abc
Your link table might look like:
auth_identities
1 | 42 | google | 123
2 | 42 | github | abc
No matter which provider they use, they end up at users.id = 42.
Common implementation pattern
When OAuth completes:
identity = find auth_identity where provider = X and provider_subject = Y
if identity exists:
log user in as identity.user_id
else if user is already signed in:
create auth_identity linked to current user
else if verified email matches existing user and policy allows:
create auth_identity linked to existing user
else:
create new user + auth_identity
If you want, I can also show:
- a database schema for this,
- an OAuth callback flow,
- or how to do this in a specific framework like Django, Rails, Laravel, or Node/Passport.