guest@geearr:~/guides$ less ~/guides/oauth-oidc-review.md
-rw-r--r-- 1 george sec 3min oauth-oidc-review.md
OAuth 2.0 & OpenID Connect security review checklist
An OAuth 2.0 / OIDC security checklist based on RFC 9700: PKCE everywhere, exact redirect URI matching, strict JWT validation, token storage, refresh token rotation and sender-constrained tokens.
by George Rios · filed under geearr-appsec(1) · raw .md
Short answer: use the authorization code flow with PKCE for every client, match redirect URIs exactly, validate every token strictly (algorithm, signature, issuer, audience, expiry), keep tokens out of browser storage where you can, rotate refresh tokens with reuse detection, and scope everything to least privilege. RFC 9700 — Best Current Practice for OAuth 2.0 Security — is the reference this checklist follows.
Flows
- Authorization code + PKCE for all clients: SPAs, mobile, server-side web apps and CLIs. PKCE (RFC 7636) with the
S256method, neverplain. - No implicit flow. Tokens in URL fragments leak through history, referrers and logs.
- No resource owner password credentials grant. It trains users to type passwords into third-party apps and breaks MFA.
- Client credentials only for genuine machine-to-machine calls, with short-lived tokens.
- Device authorization grant for input-constrained devices only.
Redirect URIs and request integrity
- Redirect URIs are registered and compared by exact string match — no wildcards, no prefix matching, no open redirects on the redirect target.
-
state(or PKCE) prevents CSRF on the callback;noncebinds the ID token to the session in OIDC. - Clients that talk to more than one authorization server check the
issparameter in the authorization response (RFC 9207) to prevent mix-up attacks. - Consider pushed authorization requests (PAR, RFC 9126) for high-value clients so parameters cannot be tampered with in the browser.
Token validation
Every resource server and client that consumes a JWT must:
- Accept only an explicit allowlist of algorithms (for example
RS256,ES256). Rejectnone. Never let the token’s header choose an HMAC algorithm keyed with a public key. - Verify the signature against keys fetched from the issuer’s JWKS, selected by
kid, with caching and rotation handled. - Check
iss,aud(this API, not just any API from the same issuer),exp,nbfandiat, with small clock skew. - Treat ID tokens as login proof for the client only — never as API access tokens.
- Keep access tokens short-lived (minutes, not days).
Token storage and transport
- For browser apps, prefer a backend-for-frontend (BFF): tokens stay on the server, the browser gets an
HttpOnly,Secure,SameSitesession cookie. - If tokens must live in the browser, keep them in memory, not
localStorage, and enforce a strict Content Security Policy. - Refresh token rotation with reuse detection: each use issues a new refresh token, and reuse of an old one revokes the whole family.
- Sender-constrained tokens — DPoP (RFC 9449) or mutual TLS (RFC 8705) — for high-risk APIs, so a stolen token is useless elsewhere.
- No client secrets in mobile apps, SPAs or anything shipped to users. Those are public clients.
Scopes, consent and authorization
- Scopes are narrow and purpose-specific; tokens are requested with the minimum needed.
- The API enforces authorization on every request and every object — a valid token does not mean access to every record (BOLA).
- Consent screens accurately describe what is granted; users and admins can review and revoke grants.
Keys and the authorization server
- Signing keys are at least RSA-2048 or P-256, stored in an HSM or KMS where possible, and rotated with overlap published in JWKS.
- Discovery (
/.well-known/openid-configuration) advertises only the flows and algorithms you actually support. - Login endpoints are rate-limited and protected against credential stuffing; MFA and passkeys are available.
Session lifecycle
- Logout ends the local session and the identity-provider session where intended (RP-initiated, front-channel or back-channel logout).
- Revocation (RFC 7009) works for refresh tokens, and introspection is available where opaque tokens are used.
- Account deletion and offboarding revoke all outstanding grants.
Test it like an attacker
Swap the aud to a different API. Replay an old refresh token. Change the redirect URI by one character. Change alg to none. Use user A’s token against user B’s resource. If any of those work, you have found your first fix.
geearr reviews OAuth and OIDC implementations as part of application security engagements — informed by building AuthnzNet, an OAuth 2.0 + OIDC server, and Identizen, an accountless OIDC identity provider.
Want this done for you, properly? Application Security · book a free scoping call ↗
more guides: