Prompt
How do I add social login without breaking my existing email/password accounts?
Latest observation
To add social login without breaking existing email/password accounts, the key is to treat social auth as an additional sign-in method that can be linked to the same user record instead of creating duplicate accounts.
Recommended approach
1) Keep one internal user table
Use a single users table for your app identity, and a separate table for login identities/providers.
Example structure:
-
users
idemailnamepassword_hashnullable- other profile fields
-
auth_identities / user_identities
iduser_idprovider(google,apple,github,password, etc.)provider_user_idprovider_email- timestamps
This lets one user have:
- email/password login
- Google login
- Apple login
- etc.
2) Don’t auto-create a new user just because social login succeeds
When a user signs in with Google/Apple/etc.:
- Check whether that provider account is already linked:
(provider, provider_user_id)
- If yes, sign them in.
- If no, try to match by verified email.
- If a matching email exists, ask the user to sign in with their existing method and link the account.
- If no match exists, create a new user and link the social identity.
This avoids accidentally creating duplicates and prevents account takeovers.
Safe linking flow
Case A: Existing email/password user wants to add Google login
Flow:
- User logs in with email/password.
- User clicks “Link Google”.
- Redirect to Google OAuth.
- On callback, you verify the Google identity.
- Attach Google identity to the currently authenticated user.
Case B: User starts with social login, but email already exists
Do not immediately merge automatically just because emails match unless the email is verified and your policy allows it.
Safer flow:
- Social login returns verified email.
- Find existing account with same email.
- Prompt: “We found an existing account. Please sign in to confirm and link this login.”
- After password/email confirmation, attach the social identity.
Important security rules
Only trust verified email
For providers that supply email, use it only if:
- the provider says it’s verified, and
- you trust that provider’s verification.
Never let OAuth replace a password account silently
If a user already has an email/password account, don’t let a social account with the same email automatically take over unless you have a secure linking step.
Require re-authentication for linking
Before adding or changing a login method, ask the user to re-authenticate with an existing method. This prevents account hijacking if someone is already logged in on a shared device.
Support passwordless/social-only accounts carefully
If a user originally signed up with social login, they may not have a password. If they later want email/password, let them set a password after verifying identity.
Migration strategy for existing users
If you already have email/password accounts in production:
- Add the identities table.
- Backfill one
passwordidentity per existing user. - Keep existing login working unchanged.
- Add social login as an optional second path.
- Add account-linking UI in profile/settings.
- Log and monitor for duplicate-account edge cases.
Good UX patterns
In account settings:
- “Sign in with Google”
- “Sign in with Apple”
- “Link GitHub account”
- “Set password” / “Change password”
On login screen:
- Email/password form
- Social buttons below it
- If the email already exists, guide the user to the correct existing method
Common pitfalls to avoid
- Creating a new account every time a social login happens
- Using only email as the unique identifier
- Auto-merging accounts solely by email without verification
- Allowing a linked provider to remove the only remaining sign-in method
- Not preserving the original account’s user ID
Practical rule of thumb
User identity = internal user ID
Login methods = attached credentials/providers
That separation is what prevents breaking existing accounts.
If you want, I can also show:
- a database schema,
- a login/linking flowchart, or
- example code for Node, Django, Rails, or Laravel.