OAuth Hacking: The Complete Guide to OAuth and OIDC Attacks

OAuth Grant Types and Their Attack Surface

The OAuth grant types explained for testers: authorization code with PKCE, the legacy implicit flow, client credentials, device code, and what each one exposes.

Published

Each OAuth grant type is a different way to obtain a token, and each exposes a different attack surface, so knowing which flow a target uses tells you where to look. This page covers the grants you will meet and what each one risks. Practice on a free live target.

Authorization code (the default)

The standard interactive flow. The client sends the user to the authorization server, the user approves, and the server redirects back with a short-lived authorization code in the query string. The client then exchanges that code for tokens at the token endpoint, server to server. The code in the browser is useless without that back-channel exchange, which is what makes it safer than handing a token straight to the browser.

GET /authorize?response_type=code&client_id=app123&redirect_uri=https://app.tld/callback&scope=openid%20email&state=xyz HTTP/1.1
Host: auth.example.com

The redirect returns ?code=...&state=..., and the attack surface is redirect_uri validation, state, and the code's single-use handling.

PKCE and why it exists

PKCE adds two parameters to the code flow so a stolen code cannot be redeemed by anyone else:

code_verifier     a high-entropy random string the client keeps
code_challenge    base64url(SHA-256(code_verifier)), the S256 challenge sent on the authz request

At redemption the client must present the matching code_verifier. PKCE defeats code interception only, so it is required on public clients but does not replace exact redirect_uri matching or state.

Implicit flow (legacy)

The implicit flow returned the access token directly in the URL fragment (response_type=token), with no code exchange:

response_type=token   ->   https://app.tld/callback#access_token=...&token_type=Bearer

A token in the fragment leaks through browser history, any JavaScript that reads location.hash, and analytics scripts that log the full URL. (It is NOT sent in the Referer header, which strips the fragment.) RFC 9700 says clients SHOULD NOT use the implicit grant (and OAuth 2.1 removes it) in favor of authorization code with PKCE. Finding it in use is usually a finding. See implicit flow token leakage.

Client credentials (machine-to-machine)

No user is involved. A service authenticates with its own client ID and secret to get a token for itself:

grant_type=client_credentials&client_id=svc&client_secret=...&scope=reports.read

The attack surface is leaked client secrets and over-broad scopes, not browser redirects.

Resource owner password (avoid)

The user types their password directly into the client, which forwards it to the token endpoint:

grant_type=password&username=alice&password=hunter2&client_id=app

This defeats the entire point of delegation (the client sees the password). RFC 9700 says it MUST NOT be used, and OAuth 2.1 removes it. Treat any use of it as a finding.

Device code

For devices with no browser or keyboard (a TV, a CLI). The device shows a short user_code and a verification_uri. The user approves on their phone, and the device polls the token endpoint. The attack surface is phishing the user into approving the attacker's code, covered in device flow abuse.

Which grant each app uses

Web app with a backend     authorization code (+ PKCE)
Single-page app            authorization code + PKCE (NOT implicit anymore)
Mobile app                 authorization code + PKCE
Service to service         client credentials
TV / CLI / IoT             device code
Legacy / lazy integration  implicit or password (a finding)

Where to go next

Next, see how OIDC layers identity on top of these flows in OpenID Connect, then attack the most common flaw in redirect_uri manipulation. Or jump straight to a free live target.

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