Skip to main content
Digital systems15 min read

OAuth for public clients: the modern flow that should pass the tests

RFC 9700 makes today's baseline explicit: authorization code, PKCE for public clients, exact redirect URI matching, mix-up protection, and retirement of implicit and password grants.

Updated
Nature
Engineering baseline based on published RFCs
Audience
Architecture, development, application security, IAM, and QA
Published and verified
4 August 2026
Limit
The final profile depends on client type, authorisation server, resources, and threat model
Physical OAuth flow model with separated channels, verifier pairs, and a persimmon token
A secure flow proves origin, transaction, recipient, and possession at every boundary.AI-generated image under Velqory editorial direction.

In brief

Native applications and browser frontends cannot keep a client secret secret; security must come from the flow. The client creates a unique verifier and sends only the challenge, binds the response to the transaction, validates the correct issuer, and exchanges the code through the intended channel. PAR reduces request exposure in the browser; DPoP can bind tokens to a key holder; audience and scope limit where and why a token works. The matrix below turns standards into testable acceptance criteria.

  • Public clients must use PKCE; S256 is the transformation to require where supported.
  • The implicit grant exposes tokens at the authorisation endpoint and is no longer the recommended choice; use response type code.
  • The resource owner password credentials grant must not be used.
  • PKCE does not replace every check: redirect URI, issuer, state or nonce, audience, and token policy still matter.

The secure design removes fake secrets and binds every response to the right transaction

A secret packaged in a native app or delivered to a browser can be extracted; do not use it as proof of a public client's identity. PKCE creates per-transaction proof: the client keeps the verifier, sends the challenge, and only a party presenting the matching verifier can exchange the code.

When a client talks to more than one authorisation server, validate the response issuer or use an equivalent countermeasure. Exact redirect URI matching reduces code leakage; state or equivalent protection binds the response to the session; nonce protects specific OpenID Connect properties.

Authorisation flow with explicit verification

  1. 1
    Create transaction

    Generate unique verifier, S256 challenge, state, and nonce where applicable.

  2. 2
    Send request

    Use an external browser and preregistered redirect URI; use PAR where the profile requires it.

  3. 3
    Validate return

    Compare state, issuer, redirect, and context before accepting the code.

  4. 4
    Exchange code

    Present the verifier to the correct token endpoint and reject reuse or mismatch.

  5. 5
    Use limited token

    Validate audience, scope, expiry, and DPoP proof when adopted.

Every control needs a negative test, not just configuration

Test what the system rejects. A happy path confirms interoperability; it does not prove that a stolen code, swapped issuer, or nearly matching redirect fails securely.

Threat, control, and acceptance criterion

ThreatControlNegative testAcceptance
Code interceptionPKCE S256Exchange without verifier or with anotherFails without token
Server mix-upiss validationReturn a different issuerClient rejects before exchange
Redirect leakageExact matchingChange path, host, or queryRequest or return rejected
Request tamperingPARChange browser parametersServer uses pushed request
Token replayDPoP where applicableUse token without key or from another originResource rejects proof
Excess privilegeAudience and scopeUse token at another resource or actionResource refuses

The release gate should prove client, server, and resource as one system

Automate tests against the real authorisation-server configuration and a representative resource server. Validate logs too: they must not retain full codes, verifiers, tokens, or sensitive parameters. Rotation and revocation should work without reinstalling the entire app.

PAR and DPoP solve specific threat classes; do not treat them as universal seals. DPoP loses value if both token and key are stolen from the same compromised context. Start from the actual attacker and the mechanism's operational cost.

  • Reject implicit and password grants for new clients.
  • Use an external browser for native apps, not an embedded webview.
  • Maintain an exact redirect allowlist per environment.
  • Test expiry, refresh rotation, revocation, logout, and clock skew.

Consulted on the stated update date.

  1. 01RFC 9700 — Best Current Practice for OAuth 2.0 SecurityRFC Editor · SYS-RFC
  2. 02RFC 7636 — Proof Key for Code ExchangeRFC Editor · SYS-RFC
  3. 03RFC 9126 — Pushed Authorization RequestsRFC Editor · SYS-RFC
  4. 04RFC 9207 — Authorization Server Issuer IdentificationRFC Editor · SYS-RFC
  5. 05RFC 9449 — Demonstrating Proof of PossessionRFC Editor · SYS-RFC
  6. 06RFC 8252 — OAuth 2.0 for Native AppsRFC Editor · SYS-RFC