OAuth best practices: We read RFC 9700 so you don’t have to
In January 2025, the IETF published RFC 9700: Best Current Practice for OAuth 2.0 Security. We read it and summarized the best practices you should follow to keep your OAuth implementation safe.
The OAuth 2.0 standard is designed to provide secure access delegation, allowing third-party applications to access resources on behalf of a user without exposing their password. However, authentication and authorization are notoriously hard to get right, and a host of poor or inconsistent implementations has left us with web apps riddled with security flaws that expose systems and data.
The OAuth 2.0 Security Best Practices (RFC 9700) was written precisely to address common pitfalls and provide valuable insights into implementing OAuth correctly. However, reading RFCs is not everybody’s cup of tea. That’s why we read the RFC and summarized the best practices for you in this article.
TL;DR: Best practices summary
This is the high-level overview of the best practices included in the RFC 9700:
- Protect redirect-based flows: Ensure secure handling of authorization code and implicit grants.
- Prevent token replay: Use secure methods to prevent token replay for access and refresh tokens.
- Limit access token privileges: Limit token access to necessary permissions only.
- Avoid the Resource Owner Password Credentials Grant: Avoid using this grant due to security concerns.
- Avoid the Implicit Grant: Avoid using this grant due to security concerns.
- Client authentication: Use strong authentication methods for clients.
- Addressing new threats: Updates include new attacker models and vulnerabilities like redirect URI validation attacks, credential leakage, and mix-up attacks, with specific countermeasures provided.
Keep reading for details on each one.
Protect redirect-based flows
OAuth 2.0 typically uses redirect URIs to send users back to the application after they grant or deny access. However, attackers can manipulate redirect URIs to steal authorization codes or tokens. To prevent this:
- Always validate the redirect URI carefully to ensure it’s the correct one and matches the expected URL. Authorization servers must use exact string matching when comparing client redirection URIs against pre-registered URIs, except for port numbers in localhost redirection URIs of native apps.
- Use an allow list of approved redirect URIs to avoid sending data to unauthorized locations.
- No open redirectors: Clients and authorization servers must not use open redirectors (URLs that forward users to arbitrary URIs from a query parameter).
- CSRF Protection: Clients must prevent Cross-Site Request Forgery (CSRF). They can rely on PKCE if supported by the authorization server or use one-time CSRF tokens in the state parameter. In OpenID Connect flows, the nonce parameter provides CSRF protection.
- Mix-up attack defense: When a client interacts with multiple authorization servers, it's required to defend against mix-up attacks. Clients should use the iss parameter or distinct redirection URIs to identify authorization endpoints and token endpoints.
- Credential forwarding: Authorization servers must avoid accidentally forwarding user credentials when redirecting requests.
Prevent token replay attacks
In an OAuth 2.0 flow, tokens—whether access tokens or authorization codes—are meant to be used by authorized clients for specific purposes and for limited timeframes. However, if these tokens are intercepted or stolen, attackers may attempt to replay them, exploiting the system and potentially accessing protected resources without the consent of the user.
To secure tokens, it’s important to do the following:
- Use short-lived access tokens: This limits the window of time an attacker can use a stolen token.
- Use token binding: Token binding is a technique where tokens are tied to specific client attributes or communication sessions. This prevents tokens from being used in contexts where they weren’t originally issued. For example, tokens could be bound to a particular client instance (e.g., a specific device or IP address). If the token is replayed by a different device or client, the authorization server can recognize this inconsistency and reject the request.
- Ensure tokens are securely stored: Use secure methods to store tokens and prevent exposure.
- Check for token integrity: Ensure that tokens have not been altered during transit.
- Use state: It’s recommended that you use state parameters in OAuth 2.0 flows, especially for the Authorization Code flow. The state parameter helps protect against various attacks, including replay attacks, by ensuring that the authorization response is returned to the correct client. If a token response is replayed (e.g., from an attacker trying to steal or reuse the token), the state parameter ensures that the server can validate whether the response corresponds to the original client that initiated the request.
- Token revocation: It’s important that the authorization server supports token revocation. If an access token is stolen or misused, the ability to revoke the token immediately will limit an attacker's window of opportunity to exploit it. OAuth 2.0 allows the use of token revocation endpoints, and implementing such a mechanism ensures that tokens can be invalidated when necessary, such as when suspicious activity is detected or when a user explicitly logs out or revokes access.
- Sender-constrained access tokens: Authorization and resource servers should use mechanisms for sender-constraining access tokens, such as mutual TLS (mTLS) or DPoP (Demonstrating Proof-of-Possession), to prevent misuse of stolen and leaked access tokens.
- Refresh tokens security: Refresh tokens for public clients must be sender-constrained or use refresh token rotation. Refresh tokens for confidential clients can only be used by the client for which they were issued.
Limit the scope of access tokens
When an access token is granted, it should only allow access to the strictly necessary resources. Limiting access to just what’s needed minimizes potential damage; if a token is compromised, the attacker can only access a small part of the system.
- Use the principle of least privilege: Ensure that the application only asks for the minimum permissions required to function.
- Audience restriction: Access tokens should be audience-restricted to a specific resource server or a small set of resource servers. Every resource server must verify whether the access token was meant to be used for that particular resource server.
Don’t use password-based authentication
In the Resource Owner Password Credentials (ROPC) flow, the user has to provide their username and password directly to the application, which means that the application (and anyone who gains access to it) can potentially access sensitive credentials. This is a major security risk because:
- If the application is compromised, attackers can steal the user's username and password, which could be used for other malicious activities.
- It also makes the application a prime target for phishing attacks or data breaches.
OAuth 2.0 was designed to allow users to grant third-party applications limited access to their resources without sharing their credentials. The ROPC flow bypasses this by directly handing over the user’s username and password to the application, defeating OAuth’s purpose. Users no longer have control over what specific resources the application can access. With ROPC, the application can access anything the user has permission for, including sensitive information they may not want to share.
For all of the above, it’s recommended that you avoid using the ROPC flow and rely on more secure OAuth flows, like the Authorization Code flow, where user consent is required, or the Client Credentials flow for machine-to-machine communication.
Don’t use the implicit grant
The Implicit Grant was originally designed for clients that run in a user's browser (i.e., public clients like single-page web applications) and could not securely store client secrets. The problem with it is that it directly issues the access token as part of the URL fragment after the authorization step, making it more vulnerable to interception.
RFC 9700 recommends that developers use the Authorization Code Flow with PKCE (Proof Key for Code Exchange) for public clients (i.e., mobile and single-page web apps). PKCE mitigates the risks associated with Implicit Grant by using an authorization code (instead of an access token directly) to exchange for an access token, and PKCE further strengthens the security of this exchange.
Use strong client authentication
OAuth 2.0 relies on both the resource owner (the user) and the client (the application) to ensure proper access control. If the client is weak or vulnerable, the whole system can be compromised.
- Require strong client authentication: Make sure the client can prove its identity securely before receiving any access tokens. For public clients, that cannot securely hold client secrets, use PKCE. PKCE ensures that even if an attacker intercepts the authorization code during the process (e.g., through a man-in-the-middle attack), they cannot redeem the code for an access token because they do not have the code verifier. PKCE significantly improves security for clients who cannot securely store a client secret, preventing code interception and token theft.
- Use client certificates or other strong authentication methods, especially for high-risk applications. A client certificate is an SSL/TLS certificate that identifies the client in a cryptographically secure way. This method is used primarily for server-to-server authentication (i.e., the client credentials flow), where both the client and the authorization server are trusted entities that can authenticate each other without relying on passwords or secrets. Use mutual TLS (mTLS) to authenticate both the client and the server during the OAuth flow. This prevents man-in-the-middle attacks and ensures that both parties are legitimate.
Stay updated on threats and countermeasures
The world of cybersecurity is always evolving, with new threats emerging regularly. OAuth 2.0, while generally secure, is still susceptible to attacks like:
- Redirect URI manipulation: Redirect URI manipulation occurs when an attacker is able to change or influence the redirect URI used in an OAuth flow. This is a critical vulnerability, as the redirect URI is where sensitive authorization information (like tokens) is sent after a user successfully authorizes an application. If an attacker can manipulate the redirect URI, they can hijack the authorization code or access token meant for a legitimate user and redirect it to their own malicious server. To mitigate against redirect URI manipulation:
- Always validate the redirect URI against an allow list of pre-registered URIs on the authorization server.
- Ensure that the client application never allows arbitrary redirect URIs, which can be exploited by attackers.
- Use techniques like state parameters to prevent attackers from guessing or manipulating the redirect URI.
- Credential leakage: Credential leakage occurs when sensitive information—such as client secrets, access tokens, or refresh tokens—is accidentally exposed or improperly stored. This is one of the most dangerous threats because these credentials allow attackers to gain unauthorized access to user data or impersonate clients.
- Mix-up attacks: Mix-up attacks happen when an attacker tricks the client and the authorization server into misdirecting the communication between the two parties. This type of attack typically involves the attacker mixing up the interactions between the client and the authorization server in order to gain unauthorized access. To mitigate against mix-up attacks:
- Use state parameters to prevent authorization codes from being misdirected, as they ensure that the request is coming from the legitimate client.
- Implement strong client authentication to verify the identity of the client when making authorization requests.
- Use PKCE (Proof Key for Code Exchange) for public clients (e.g., mobile apps) to prevent code interception and ensure that the code is only usable by the legitimate client.
- Carefully validate the client ID in every interaction between the client and the authorization server to ensure that the authorization response is directed to the correct client.
RFC 9700 suggests that developers should constantly review the latest security research, update their OAuth implementation regularly, and be aware of emerging risks.
Conclusion
When properly implemented, OAuth 2.0 is a robust and secure protocol. However, the risks associated with OAuth often arise from poor implementation rather than flaws in the protocol itself. Therefore, it is crucial for developers to stay updated on security best practices and follow established guidelines for secure implementation.
By understanding these best practices and following the standard closely, developers can ensure that OAuth 2.0 remains a secure choice for delegated access in modern applications.
RFC 9700 also includes an updated OAuth 2.0 attacker model, details on common security attacks, and guidance for their mitigation. Stay tuned for our next article, which will cover all these.