OAuth 2.0 and Third-Party AuthLesson 4.5
linking OAuth accounts to existing email-password accounts
account linking strategy, email deduplication, merging OAuth and local accounts, security risks of auto-linking, confirmation flow, multi-provider support
Linking OAuth to Existing Email-Password Accounts
Users often have an existing account with their email and later try to sign in with Google using the same address. Naive auto-linking is a security vulnerability.
The attack: Attacker creates a Google account with victim@example.com before the victim does. Your app auto-links on matching email. Attacker gains access to the victim's existing account.
Safe account linking approach:
- Check if an account with the OAuth email already exists.
- If the existing account has a verified email (email verification flow ran), prompt the user to link manually — require them to log in with their password first, then link the OAuth provider from account settings.
- If no account exists, create a new one via OAuth.
// In OAuth verify callback
const existingUser = await User.findOne({ email: profile.emails[0].value });
if (existingUser && !existingUser.googleId) {
// Do NOT auto-link — return an error prompting manual linking
return done(null, false, { message: 'Account exists. Log in with password to link Google.' });
}
if (existingUser && existingUser.googleId === profile.id) {
return done(null, existingUser); // normal login
}
const newUser = await User.create({ googleId: profile.id, email: profile.emails[0].value });
return done(null, newUser);Multi-provider support (Google + GitHub + local) requires tracking which providers each user has linked, with one canonical user record and multiple provider rows in a linked accounts table.
