Not logged in

Fixed: Explicit Consent Gate Secure

Before linking a new sign-in method to an existing account, the service requires re-authentication via an already-linked method. An attacker with only an OAuth token cannot pass this gate.

/auth/sso/callback
// /auth/sso/callback router.get('/auth/sso/callback', async (req, res) => { const { email, provider } = parseOAuthToken(req.query.code) const user = await db.users.findByEmail(email) if (user && user.emailVerified) { if (user.ssoProviders.includes(provider)) { return createSession(res, user.id) } // FIX: do not merge silently. Store the pending link in the session // and require the user to authenticate with an already-linked method // before the new provider is trusted. req.session.pendingLink = { email, provider } return res.redirect('/sso/fixed/link-confirm') } const newUser = await db.users.create({ email, emailVerified: true, ssoProviders: [provider] }) createSession(res, newUser.id) })

1

Victim: registers victim

Password will be set to Password123

2

Attacker: tries SSO merge attacker

The attacker tries to sign in via SSO with the victim's email. The service detects an existing account and requires re-auth before linking.