OAuth Security Best Practices

OAuth 2.0 has been deployed for over a decade, and attackers have probed every corner of it. The original 2012 specification left many options open, some of which turned out to be dangerous: the implicit flow, the password grant, loose redirect URI matching, and bearer tokens usable by anyone who steals them. The IETF's OAuth 2.0 Security Best Current Practice (RFC 9700) and the consolidating OAuth 2.1 draft capture the lessons, and profiles like FAPI 2.0 define strict configurations for high-risk sectors such as banking.

This page summarizes what modern OAuth deployments should do, the attacks those rules prevent, and newer mechanisms (DPoP, mTLS-bound tokens, PAR) that make stolen tokens far less useful.

TL;DR

Quick Example

A DPoP-bound token request and API call (conceptual HTTP):

The access token is bound to the client's key (the cnf.jkt thumbprint). An attacker who steals it can't produce valid DPoP proofs, so the token is useless to them.

Core Concepts

OAuth 2.1 in Brief

OAuth 2.1 consolidates OAuth 2.0 with its security extensions:

Most providers already support these behaviors, so new deployments should follow them today.

Common Attacks and Mitigations

Sender-Constrained Tokens

Bearer tokens work for whoever holds them. Sender-constrained tokens require proof of possession of a key:

Pushed Authorization Requests (PAR)

With PAR (RFC 9126), the client POSTs the authorization parameters directly to the authorization server and receives a request_uri. The browser redirect then carries only that reference. Parameters can't be tampered with or leaked via the URL, and the server can authenticate the client before the user interaction begins. JAR (signed request objects, RFC 9101) provides integrity for request parameters.

FAPI 2.0

The Financial-grade API security profile (from the OpenID Foundation) mandates a hardened configuration: authorization code with PKCE, PAR, sender-constrained tokens (DPoP or mTLS), strong client authentication (private_key_jwt or mTLS), and strict validation. Open banking regimes and other high-risk APIs use it, and it's a useful blueprint even outside finance.

Operational Security

Best Practices

Follow RFC 9700 Checklist-Style

Treat the Security BCP as a checklist during design and review: PKCE, exact redirects, no implicit or password grants, audience-restricted tokens, refresh token protection, and issuer validation.

Constrain Tokens in High-Risk Contexts

For APIs handling payments, health data, or admin operations, deploy DPoP or mTLS so token theft alone isn't enough to act.

Minimize Token Exposure in Browsers

Use a backend-for-frontend so tokens never reach JavaScript. If tokens must live in the browser, keep them short-lived and in memory, use DPoP with non-extractable WebCrypto keys, and harden against XSS.

Least Privilege Everywhere

Narrow scopes, per-API audiences, short lifetimes, and per-client permissions. Assume any single token or client will eventually leak, and limit what it can do.

Common Mistakes

Still Using the Password Grant

Apps collecting user passwords and exchanging them for tokens (ROPC) bypass MFA, train users to type passwords into third-party UIs, and break federation. Use the authorization code flow.

Wildcard Redirect URIs

https://*.example.com/callback lets any subdomain, including a compromised or user-controlled one, receive codes. Register exact URIs.

Not Validating the Issuer

Clients integrating multiple identity providers without checking the iss parameter are vulnerable to mix-up attacks that route codes to an attacker-controlled provider.

FAQ

What is OAuth 2.1?

A consolidation of OAuth 2.0 with its most important security extensions and best practices: PKCE required, implicit and password grants removed, exact redirect URI matching, and protected refresh tokens. It simplifies OAuth by removing insecure options, and most modern providers already support its requirements.

What is DPoP?

Demonstrating Proof of Possession (RFC 9449): a mechanism binding access and refresh tokens to a client-held key pair. Each request includes a signed proof, so stolen tokens can't be used without the private key. It works at the HTTP layer, which makes it suitable for browsers, mobile apps, and services.

Why is the implicit flow deprecated?

It returned access tokens directly in the browser URL fragment, exposing them to history, logs, referrers, and scripts, with no way to bind them to the client. Authorization code with PKCE provides the same browser compatibility securely.

Do I need FAPI?

Regulated financial APIs (open banking) often require it. For other high-risk APIs, adopting FAPI 2.0's core measures (PAR, sender-constrained tokens, strong client authentication) is a solid hardening path, even without formal certification.

Related Topics

References