Social Login Security
Cross-cutting security concerns when integrating OAuth 2.0 / OpenID Connect: CSRF (
state), replay (nonce), token validation, and the most common implementation mistakes.
CSRF Protection — the state Parameter
Threat: an attacker initiates an OAuth flow themselves, then tricks a victim into completing it, causing the victim’s account to be linked to the attacker’s identity at your app — or vice versa.
Mitigation:
- On the authorize request, generate a high-entropy random
statevalue. - Bind it to the user’s session (server-side store keyed by session, or signed cookie).
- On callback, verify the returned
statematches what was stored. If not, abort. - Rotate after use.
import secrets
state = secrets.token_urlsafe(32)
session["oauth_state"] = state
# ... build authorize URL with state=...
# in /callback:
if request.args["state"] != session.pop("oauth_state", None):
abort(400, "state mismatch")state should also encode the post-login redirect target if you have one — sign it (HMAC) or store it server-side keyed by the random value.
Replay Protection — the nonce Parameter
Threat: an attacker captures a valid ID Token (e.g., from an old log) and replays it to your app to impersonate the user.
Mitigation (OIDC-specific):
- Generate a random
nonceand include in the authorize request. - Bind it to the session like
state. - The provider includes
noncein the issued ID Token. - On token receipt, verify
id_token.nonce == sent_nonce. Mismatch → reject.
state and nonce solve overlapping but distinct problems. Use both.
Token Validation Checklist
Every time you receive an ID Token (full algorithm in ID Token):
- Signature verified with the provider’s public key (JWKS, by
kid). -
algpinned to a specific allow-list (RS256 / ES256), nevernone. -
issmatches the discoveryissuer. -
audincludes yourclient_id(andazp == client_idif multipleaud). -
expis in the future, with small clock-skew tolerance (~5 min). -
iatis sane (not far future). -
noncematches the value you sent.
For access tokens:
- If opaque, hit the introspection endpoint or treat as a bearer the resource server validates.
- If JWT, validate like an ID Token (different
aud— the resource server, not your client).
Replay Window for Codes
Authorization codes are single-use. A spec-compliant server invalidates a code on first redemption. If the same code is presented twice, the second request fails and the originally-issued tokens are revoked. Combined with PKCE, stolen codes are useless without the verifier.
Identifier Hygiene
- Federated user identity =
(iss, sub), neveremail. - Two providers can issue the same
sub; always includeiss. email_verified == falsemeans unverified. Don’t link accounts by email unless verified, and even then do an additional confirmation step.
Common Mistakes (Top 10)
- Using
emailas user ID. Emails change, get reassigned. Usesub. - Skipping
statevalidation. Account-linking CSRF. - Skipping ID Token signature verification. Accepting forged tokens.
- Not checking
aud. Accepting tokens issued to a different app. - Accepting
alg: none. Old library defaults; pin algorithms. - Storing tokens in localStorage in browsers. XSS reads them. Prefer httpOnly cookies (with SameSite) for session tokens.
- Using the implicit flow. Tokens leak via referrer / history.
- Treating an OAuth access token as identity (no
openidscope, no ID Token). - Doing the token exchange from the browser with a confidential client secret.
- Mishandling Apple’s first-login user data. See Sign in with Apple.
Refresh Token Hygiene
- Rotate on every use; the auth server should issue a new refresh token and invalidate the old.
- Detect reuse: if a previously-rotated refresh token is presented, treat as compromise — revoke the entire token family.
- Store securely: server-side encrypted at rest, never exposed to the browser for confidential clients.
Logout Considerations
- OAuth has no standard logout. OIDC defines RP-Initiated Logout and Back-Channel Logout.
- “Logging the user out of your app” doesn’t log them out of the IdP. They click “Sign in with Google” again → they’re back in immediately. This is usually desired UX; document it.