Topic guide · 3 min read
OAuth security: what to get right
Short answer
Secure OAuth means using the authorization code flow with PKCE for every client, exact redirect URI matching, state or PKCE for CSRF protection, short-lived and audience-restricted access tokens, protected refresh tokens, and APIs that validate every token and still check object-level authorization; RFC 9700 (January 2025) is the reference.
Key takeaways
- Drop the implicit and resource owner password grants; RFC 9700 and the OAuth 2.1 draft retire them.
- Redirect URI validation must be exact string matching; most OAuth account takeovers start with a loose redirect check.
- Access tokens should be short-lived, audience-restricted and, for high-value APIs, sender-constrained.
OAuth 2.0 is a framework with many options, and most real-world vulnerabilities come from choosing a weak option or skipping a check. The IETF collected a decade of lessons into RFC 9700, the OAuth 2.0 Security Best Current Practice, published in January 2025. The OAuth 2.1 draft rolls the same rules into the core framework. This guide is a practical summary.
Which flows are still acceptable?
Authorization code with PKCE, client credentials, device authorization and refresh tokens; not implicit, not password.
| Grant | Status | Use for |
|---|---|---|
| Authorization code + PKCE | Recommended | All user-facing clients |
| Client credentials | Fine | Service to service |
| Device authorization (RFC 8628) | Fine | Input-constrained devices |
| Refresh token | Fine, protected | Long sessions without re-login |
| Implicit | Do not use | (Tokens in URL fragments) |
| Resource owner password | Do not use | (Exposes passwords, blocks MFA) |
What protects the authorization request and response?
PKCE, state, exact redirect URIs and, with multiple servers, the iss parameter.
- PKCE (RFC 7636) on every code flow. Generate pairs with the PKCE Generator.
- state for CSRF protection and per-request context, explained in detail in the state parameter article.
- Exact redirect URI matching. No wildcards, no partial paths. Loose matching plus an open redirect on the client is a classic token theft chain.
- iss check (RFC 9207) for clients using more than one server, against mix-up.
- PAR (RFC 9126) for high-value clients, so parameters never travel through the browser.
Build and audit a request in the Authorization URL Builder.
How should tokens be shaped?
Short-lived, narrowly scoped, audience-restricted and, where it matters, bound to the client.
- Access tokens of minutes to an hour.
audnaming one API (use theresourceparameter, RFC 8707) rather than a catch-all.- Minimum scopes.
- Sender-constrained via DPoP (RFC 9449) or mTLS (RFC 8705) for sensitive APIs.
- Refresh tokens for public clients rotated on use or sender-constrained.
What must an API check?
Signature with a pinned algorithm, issuer, audience, expiry, scope, and then whether this user may act on this object.
The token check is necessary but not sufficient. Broken object level authorization is the top risk in the OWASP API Security Top 10; see the OWASP API Top 10 in practice. Decode a sample token in the JWT Decoder to review what your APIs receive.
What else does RFC 9700 cover?
Leakage through Referer headers and browser history, open redirectors, 307 redirects, TLS-terminating proxies, clickjacking and attacks on in-browser communication.
Practical takeaways: send Referrer-Policy on pages that handle codes, use 303 rather than 307 when redirecting after a POST that carried credentials, frame-protect authorization pages, and validate postMessage origins in browser-based flows. Downgrade-style attacks have their own guide: OAuth downgrade attacks.
Questions
Which OAuth grant should I use?
Authorization code with PKCE for anything involving a user, including web apps, SPAs and mobile apps. Client credentials for machine-to-machine. Device authorization grant (RFC 8628) for TVs and devices without a browser.
Should a single-page app hold tokens?
It can, using the code flow with PKCE, short-lived access tokens and rotated refresh tokens. Many teams prefer a backend-for-frontend pattern where a small server holds the tokens and the browser only has a session cookie.
Are JWT access tokens better than opaque ones?
They let APIs validate locally without calling the authorization server, which helps at scale. Opaque tokens with introspection (RFC 7662) make revocation immediate and keep claims private. Both are fine when validated correctly.