
RFC 9700: The Mandatory Guardrails for OAuth 2.0
An examination of RFC 9700, detailing OAuth 2.0 security best current practices, including mitigation of mix-up attacks and redirect URI validation.
RFC 9700: The Mandatory Guardrails for OAuth 2.0
OAuth 2.0 was designed as a flexible authorization framework, not a comprehensive authentication protocol. This flexibility allowed for rapid adoption but created significant ambiguity in implementation. Developers often interpreted the specification differently, leading to interoperability failures and security vulnerabilities. RFC 9700, titled "OAuth 2.0 Security Best Current Practice," resolves this by establishing a baseline of secure configurations that all compliant implementations must follow. It does not introduce new protocol endpoints; instead, it mandates how existing endpoints must be secured to prevent common attack vectors.
As Part 7 of the OAuth 2.0 RFCs Every Engineer Should Read series, this article examines how RFC 9700 closes the gaps left by the original RFC 6749, focusing on token confusion, redirect URI validation, and public client security.
The Problem of Ambiguity in OAuth 2.0
The original OAuth 2.0 specification (RFC 6749) provided broad guidelines. For example, it allowed various client types and token formats without strictly defining how they should be validated. This ambiguity meant that a developer could implement a "secure" flow that was technically compliant with RFC 6749 but vulnerable to modern attack techniques that did not exist at the time of writing.
RFC 9700 acts as a corrective layer. It clarifies that while the protocol is flexible, the security posture must be rigid. The document explicitly states that implementations must follow these best practices to ensure security across diverse environments, from web servers to mobile devices. Without these guidelines, the security of an OAuth 2.0 system depends entirely on the individual developer’s knowledge, which is an unacceptable risk for authentication infrastructure.
Mix-Up Attacks and Token Binding
One of the most critical vulnerabilities addressed in RFC 9700 is the "mix-up" attack. This occurs when an attacker tricks a client or server into accepting a token intended for a different purpose or client. For instance, an authorization code meant for Client A might be intercepted and used by Client B, or an access token issued for one resource server might be accepted by another.
The mechanism of prevention relies on strict validation of the iss (Issuer) and aud (Audience) claims within the ID Token or Access Token. RFC 9700 mandates that clients must validate the issuer of the token to ensure it comes from the expected authorization server. Furthermore, clients must verify that the audience claim includes their own client ID. This ensures that a token issued for one service cannot be reused in another context.
Consider a scenario where an attacker crafts a malicious redirect URL. If the client does not validate the issuer, it might accept a token from an untrusted authorization server. By enforcing issuer validation, the client ensures that the token’s origin is trustworthy. Similarly, audience validation prevents token reuse across different services. If a token’s audience claim does not match the client’s identifier, the token is rejected. These measures represent key oauth security best practices defined in RFC 9700, effectively isolating tokens to their intended contexts and preventing mix-up attacks.
Redirect URI Validation Mechanics
Redirect URI validation is another area where ambiguity led to security flaws. Early implementations often allowed wildcards or partial matches in redirect URIs, making it easy for attackers to redirect authentication responses to malicious domains. RFC 9700 tightens these requirements significantly.
The specification mandates that redirect URIs must be registered exactly as they will be used. Wildcards are prohibited because they create ambiguity in matching. For example, a registered URI of https://example.com/* could match https://evil.com/path, allowing an attacker to capture authorization codes. Instead, RFC 9700 requires exact string matching for the scheme, host, and port. Case sensitivity is also addressed: the scheme must be case-insensitive (as per RFC 3986), but the rest of the URI must be case-sensitive.
This strict validation is a core bcp requirement. It ensures that the authorization server only redirects to pre-approved endpoints. The mechanism works by comparing the redirect URI in the authorization request against the registered list. If there is any mismatch, the server rejects the request. This prevents open redirect vulnerabilities that could lead to authorization code interception.
Public Client Security: PKCE as Mandatory
Public clients, such as single-page applications (SPAs) and native mobile apps, cannot securely store client secrets. This makes them vulnerable to authorization code interception attacks, where an attacker steals the code from the redirect URI and exchanges it for an access token.
RFC 9700 mandates the use of Proof Key for Code Exchange (PKCE, defined in RFC 7636) for all public clients. PKCE is now an oauth security best practice for public clients. It adds a layer of security by introducing a code verifier and a code challenge. The client generates a random code verifier and hashes it to create a code challenge, which is sent during the initial authorization request. When exchanging the authorization code for a token, the client sends the original code verifier. The authorization server verifies that the verifier matches the challenge.
This mechanism ensures that even if an attacker intercepts the authorization code, they cannot exchange it for a token without the code verifier. The verifier is generated and stored securely by the client, making it inaccessible to attackers. This eliminates the need for client secrets in public clients while maintaining security.
Implementation Checklist for Developers
To align with the BCP guidelines outlined in RFC 9700, backend developers and security engineers should audit their OAuth implementations against the following criteria:
- Validate Issuer and Audience: Ensure that all tokens are validated against the expected issuer and that the audience claim includes the client’s identifier.
- Exact Redirect URI Matching: Register redirect URIs with exact matches and reject any requests that do not match precisely.
- Enforce PKCE: Require PKCE for all public clients, ensuring that the code verifier is generated and validated correctly.
- Use State Parameters: Always include and validate the
stateparameter to prevent CSRF attacks during the authorization flow. - Secure Token Storage: Store access tokens securely, avoiding local storage in browsers and using httpOnly cookies or secure storage mechanisms in native apps. Validate against BCP standards to ensure compliance.
By adhering to these oauth security best practices, developers can mitigate the most common OAuth 2.0 vulnerabilities and ensure a secure authentication experience for their users.
Conclusion
RFC 9700 transforms OAuth 2.0 from a flexible framework with ambiguous security implications into a robust standard with clear, mandatory guardrails. By enforcing strict issuer and audience validation, eliminating wildcard redirect URIs, and mandating PKCE for public clients, it addresses the most prevalent attack vectors in modern authentication flows. For backend developers and security engineers, adopting these best current practices is no longer optional—it is essential for building secure, interoperable systems.
Related posts
Spring Security Password Encoding: BCrypt, Argon2, and PBKDF2
An examination of password encoding strategies in Spring Security using BCrypt, Argon2, and PBKDF2 to ensure secure password storage.
RFC 8693: Token Exchange, Delegation, and Impersonation
RFC 8693 defines token exchange, delegation, and impersonation mechanisms for OAuth 2.0, enabling secure identity propagation across service boundaries.
Decoding client_secret_basic Default in RFC 8414
An examination of the token_endpoint_auth_methods_supported metadata field in RFC 8414 and why client_secret_basic remains the default authentication method.