Join our Newsletter — 33% off our NHI Course
Home› Guides› OAuth 2.0 Security Best Current Practice (RFC 9700)…
Guide Authorisation

OAuth 2.0 Security Best Current Practice (RFC 9700) Guide: What Changed and What to Fix

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 27 October 2026 14 min read
On this page

Many OAuth deployments still follow the 2012 rules of RFC 6749, which allowed patterns now known to leak codes and tokens, from loose redirect matching to the implicit grant. RFC 9700, published in January 2025 as BCP 240, is the IETF's consolidated security advice for OAuth 2.0. It updates RFC 6749, RFC 6750 and RFC 6819, deprecates the least secure modes and describes 17 classes of attack with countermeasures. This guide walks through what changed, turns Section 2 into an audit table, explains the attacks practitioners most often miss (mix-up, code injection, PKCE downgrade) and gives a plan to bring an estate into line. It is for identity architects, authorisation server owners and application teams who need to audit an OAuth deployment against the current standard.

Key takeaways

  • Exact redirect URI matching is mandatory. Authorisation servers MUST use exact string matching, with one exception for the port of localhost redirects in native apps.
  • PKCE is for everyone. Public clients MUST use it, confidential clients are recommended to, and servers MUST support it and block the PKCE downgrade.
  • Implicit and password grants are out. Clients SHOULD NOT use the implicit grant, and the resource owner password credentials grant MUST NOT be used.
  • Bearer tokens should be sender-constrained. Mutual TLS (RFC 8705) or DPoP (RFC 9449) stop a stolen access token working elsewhere; public clients must also protect refresh tokens.
  • Clients talking to several servers need mix-up defence. The iss response parameter (RFC 9207) is the preferred fix and is cheap to deploy.

What RFC 9700 is and what changed

RFC 9700 is a Best Current Practice, not a new protocol. It does not replace RFC 6749; it narrows the choices RFC 6749 left open. The OAuth 2.0 and OpenID Connect Guide explains the protocol; the OAuth 2.1, GNAP and FAPI Guide covers OAuth 2.1, which folds much of this advice into the core specification.

AreaRFC 6749 era practiceRFC 9700 position
Redirect URIsPrefix or pattern matching commonExact string matching required (Section 2.1)
PKCEMobile apps onlyPublic clients MUST, confidential clients recommended, S256 (Section 2.1.1)
Implicit grantStandard for single-page appsSHOULD NOT be used (Section 2.1.2)
Password grantAllowed for trusted clientsMUST NOT be used (Section 2.4)
Access tokensBearerSender-constrained with mTLS or DPoP recommended (Section 2.2)
Refresh tokens for public clientsLong-lived bearerSender-constrained or rotated (Section 2.2)
Token privilegesBroad scopesAudience and privilege restriction (Section 2.3)
Client authenticationClient secretsAsymmetric methods recommended (Section 2.5)
DiscoveryManual configurationAuthorisation server metadata (RFC 8414) recommended (Section 2.6)

Section 2 as an audit table

Section 2 is the normative summary; Section 4 explains the attacks behind each rule. Use the table below as the audit worksheet, one row per check, recording pass, fail or not applicable for each client and authorisation server.

CheckRequirementWhoHow to test
Redirect URI matchingExact string match (2.1)ASRequest with an added path segment, query or different case; must be refused
Open redirectorsNone on client or AS (2.1, 4.11)BothReview redirect endpoints and the AS behaviour on invalid client_id or redirect_uri
CSRFPKCE, nonce or one-time state (2.1, 4.7)ClientReplay an authorisation response in a different browser session
Mix-upIssuer check when using more than one AS (2.1, 4.4)ClientConfirm the client stores the expected issuer per request and checks iss
PKCES256 for public clients, recommended for confidential (2.1.1)BothSend plain; send no challenge; check the AS rejects as configured
PKCE downgradeReject code_verifier if no challenge was sent (4.8)ASStrip the challenge from the authorisation request, then present a verifier
Implicit grantNot used (2.1.2)Client and ASLook for response_type=token or id_token token in configs and logs
Sender-constrained access tokensmTLS or DPoP (2.2)AS and RSReplay a captured token from another client; must fail
Refresh token protectionRotation or sender constraint for public clients (2.2, 4.14)ASUse an old refresh token twice; the family should be revoked
Audience restrictionTokens bound to a resource (2.3, 4.10.2)AS and RSPresent a token for API A to API B
Password grantDisabled (2.4)ASAttempt grant_type=password
Client authenticationAsymmetric where feasible (2.5)ASList clients still using secrets
MetadataPublished and used (2.6)BothFetch /.well-known/oauth-authorization-server
TransportNo http redirect URIs except native loopback (2.6)ASSearch registrations for http://
CORSNot supported at the authorisation endpoint (2.6)ASSend a preflight to the authorisation endpoint

Redirect-based flow attacks in depth

Insufficient redirect URI validation (Section 4.1)

  • The attack: a server that accepts any URI under a registered prefix lets an attacker register or find a page on that host and receive the code or token.
  • The fix: exact string matching. Native apps may vary only the port of a localhost redirect, as RFC 8252 allows. Section 4.1.3 also suggests appending a fragment such as #_ to the Location header so browsers do not reattach an attacker's fragment.
  • Where it bites: wildcard redirect URIs for preview environments, and multi-tenant SaaS hosts where tenants control content.

Credential leakage via Referer and browser history (Sections 4.2 and 4.3)

  • The attack: a code or token in a URL leaks through the Referer header to third-party resources on the landing page, or sits in browser history.
  • The fix: no third-party resources on the redirect landing page or authorisation endpoint, a Referrer-Policy such as no-referrer, the form post response mode, one-time codes, and revocation of tokens if a code is redeemed twice.

Mix-up attacks (Section 4.4)

A client that works with several authorisation servers (a SaaS app supporting many enterprise IdPs, an aggregator, an AI agent platform) can be tricked into sending a code issued by an honest server to an attacker-controlled server. Two defences exist:

  • Issuer identification (preferred): the server returns iss in the authorisation response (RFC 9207) or in an ID Token. The client stores the expected issuer per request, bound to the browser session, and aborts on mismatch. RFC 9207 also requires clients to reject responses without iss from servers that advertise authorization_response_iss_parameter_supported.
  • Distinct redirect URIs: one redirect URI per issuer. RFC 9700 says to use this only when other options are unavailable, because an attacker may register a client with the honest server using the attacker's redirect URI.

Authorisation code injection (Section 4.5)

An attacker who has stolen a code injects it into their own session with the legitimate client, so the client redeems it and logs the attacker in as the victim. PKCE binds the code to the browser that started the flow; the OpenID Connect nonce does the same for confidential clients, but Section 4.5.3.2 notes it does not protect public clients. Section 4.5.4 adds that an attacker who can alter the challenge or nonce in the victim's request can bypass both, so protecting authorisation responses still matters.

PKCE downgrade (Section 4.8)

If PKCE is optional, an attacker removes code_challenge from the request so the code is issued without binding. The server must reject a token request containing a code_verifier when the original authorisation request had no challenge. Servers that require PKCE for every client meet this automatically, which is the simplest audit outcome.

Other front-channel issues

  • CSRF (4.7): clients must confirm the server supports PKCE before relying on it for CSRF protection; otherwise use one-time state or nonce, and protect any data carried in state against tampering.
  • Open redirection (4.11): the server must authenticate the user before redirecting and must not redirect automatically on an invalid client_id and redirect_uri combination, which attackers use to bounce victims to phishing pages (MITRE ATT&CK T1566.002 Spearphishing Link).
  • 307 redirects (4.12): the server MUST NOT use HTTP 307 when the request may carry user credentials, because the browser would re-post the form to the client. Use 303.
  • Clickjacking (4.16): authorisation servers MUST prevent clickjacking, with CSP frame-ancestors on the authorisation endpoint and X-Frame-Options for older browsers.
  • In-browser messaging (4.17): flows using postMessage must match origins exactly and never use the * wildcard.

Token-side protections

Sender-constrained tokens (Sections 2.2 and 4.10.1)

A bearer token works for anyone who holds it, which is why token theft is so effective (ATT&CK T1528 Steal Application Access Token, then T1550.001 Application Access Token). Sender-constraining binds the token to a key the client holds.

MechanismBindingBest fitWatch out for
Mutual TLS (RFC 8705)Token cnf carries x5t#S256 of the client certificateServer-to-server, existing PKI, open bankingTLS terminating proxies must pass the certificate securely (Section 4.13)
DPoP (RFC 9449)Token bound to a key; client signs a proof per requestBrowser, mobile and cases where mTLS is impracticalNonce handling, proof replay windows, library maturity
Audience restriction onlyToken valid at one resourceEverywhere, as a baselineDoes not stop replay at that same resource

See the DPoP Guide and the mTLS Guide. The Salesloft Drift breach shows the cost of bearer tokens: stolen OAuth access and refresh tokens for an integration were used from anonymising infrastructure against hundreds of Salesforce tenants. Sender-constraining is one of the controls that limits that kind of replay; the breach page itself focuses on IP restrictions, scopes and revocation.

Audience and privilege restriction (Sections 2.3 and 4.10.2)

  • Audience: resource servers should check the audience and reject mismatches; the audience should be the URL the client actually calls. Clients signal it with the resource parameter (RFC 8707), and servers return invalid_target for unknown resources.
  • Privilege: limit tokens with scope or authorization_details (RFC 9396). See the OAuth Scope Design Guide.

Refresh tokens (Section 4.14)

  • The server MUST decide, based on a risk assessment, whether to issue refresh tokens at all.
  • Refresh tokens MUST be bound to the scope and resource servers the user consented to.
  • For public clients the server MUST detect replay, through sender-constraining or rotation. With rotation, reuse of an old token revokes the active one.
  • Servers may revoke refresh tokens on events such as password change or logout, and should expire them after inactivity. See the OAuth Refresh Token Rotation and Expiry Guide.

Client impersonating a resource owner (Section 4.15)

If a resource server cannot tell a client credentials token from a user token, a client that chooses a client_id resembling a user identifier may reach that user's data. Servers SHOULD NOT let clients influence their client_id or claims that could be confused with a user, and otherwise MUST give resource servers another way to distinguish the two. This matters for machine-to-machine and agent clients that register themselves.

Applying the BCP: a phased plan

  1. Inventory: list every authorisation server, client, grant type, redirect URI and authentication method. Most findings come from this step alone: implicit flows, password grants and wildcard redirects.
  2. Quick wins on the server (weeks 1 to 4): disable the password grant, enforce exact redirect matching, require PKCE with S256 for public clients, publish metadata and return iss. Run each change in report-only or logging mode first where the product allows, then enforce for a pilot ring of clients.
  3. Client remediation (months 1 to 3): move single-page apps from implicit to code with PKCE, preferably behind a backend for frontend (see the OAuth for Browser-Based Applications and the BFF Pattern Guide); add iss checks in multi-issuer clients; fix open redirectors.
  4. Token hardening (months 3 to 9): introduce audience restriction with resource indicators, refresh token rotation for public clients, and sender-constrained tokens for high-value APIs. Pilot DPoP or mTLS with one API before scaling.
  5. Client authentication (ongoing): migrate confidential clients to private_key_jwt or mTLS. See the JWT Client Authentication Guide.
  6. Rollback: keep each enforcement change behind a per-client toggle so a broken integration can be exempted temporarily, with an expiry date, rather than reverting the control globally.

Decision guidance: a small organisation using a hosted IdP mostly needs to configure it correctly (disable legacy grants, enforce PKCE) and fix its own clients. A large organisation running its own authorisation servers owns every row of the audit table and should treat the BCP as a release gate. Regulated sectors that need more than the BCP offers should adopt a profile that turns the recommendations into requirements, such as the FAPI 2.0 Security Profile Implementation Guide.

Detection and measurement

  • Authorisation server logs: redirect URI mismatches, requests without PKCE, plain challenges, code redemption failures and double redemptions, refresh token reuse events, and any attempt at grant_type=password.
  • Resource server logs: audience mismatches, DPoP proof failures, certificate binding failures and tokens presented from unexpected networks.
  • Client telemetry: iss mismatches and state failures, which can indicate active mix-up or CSRF attempts.
MetricTarget
Clients using implicit or password grantsZero
Redirect URIs registered with wildcards or httpZero (native loopback excepted)
Authorisation requests with PKCE S256100% of public clients, rising for confidential
High-value APIs accepting only sender-constrained tokensRising to 100%
Refresh token reuse events investigated100% within one working day
Audit table rows passed per authorisation serverAll rows, reviewed yearly

Framework mapping

FrameworkReferenceRFC 9700 area
OWASP ASVS 5.0V10.4.1, V10.4.2, V10.4.6Exact redirect matching, one-time codes, PKCE
OWASP ASVS 5.0V10.2.1, V10.2.2Client CSRF and mix-up defences
OWASP ASVS 5.0V10.3.1, V10.3.5, V10.4.5Audience, sender-constrained tokens, refresh token replay
OWASP ASVS 5.0V3.4.6CSP frame-ancestors (clickjacking)
NIST SP 800-53 Rev. 5SC-23 Session Authenticity; SC-8 Transmission Confidentiality and IntegrityBinding of codes and tokens; transport
NIST SP 800-53 Rev. 5AC-6 Least Privilege; IA-9 Service Identification and AuthenticationToken privilege restriction; client authentication
NIST CSF 2.0PR.AA-03, PR.DS-02Authentication of services; data in transit
MITRE ATT&CKT1528, T1550.001, T1566.002Token theft and replay; phishing via open redirectors

Common failure patterns

  • Wildcard redirects for convenience: a https://*.example.com/* registration for preview builds becomes the code theft path once any subdomain can host attacker content.
  • PKCE optional on the server: clients send a challenge, but the server does not require one, which leaves the downgrade open.
  • Single-page apps still on implicit: tokens in URL fragments, in history and visible to every script on the page.
  • Multi-issuer clients without iss checks: SaaS platforms that let customers bring their own IdP are the classic mix-up target.
  • Long-lived bearer refresh tokens in integrations: theft from one vendor yields durable access to every customer tenant, as in the Salesloft Drift case.
  • Password grant kept for a legacy tool: it trains users and systems to hand passwords to clients and bypasses MFA at the authorisation server.

Practitioner checklist

  • Inventory authorisation servers, clients, grants, redirect URIs and authentication methods.
  • Enforce exact redirect URI matching and remove wildcard and http registrations.
  • Require PKCE with S256 for every public client and reject verifiers without a prior challenge.
  • Disable the implicit and password grants at the server.
  • Return iss in authorisation responses and check it in every multi-issuer client.
  • Restrict token audience with resource indicators and validate it at every API.
  • Rotate or sender-constrain refresh tokens for public clients; revoke on reuse.
  • Pilot DPoP or mTLS for high-value APIs, then make it the default.
  • Set CSP frame-ancestors, a strict Referrer-Policy and 303 redirects on the authorisation server.
  • Rerun the Section 2 audit table yearly and on every new client type.

Standards and references

Related NHI Mgmt Group resources: OAuth 2.0 and OpenID Connect Guide · OAuth 2.1, GNAP and FAPI Guide · DPoP Guide · OAuth Token Theft Incident Playbook · FAPI 2.0 Security Profile Implementation Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 27 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.