Stackbook Logo
security-complianceestablished · medium operational burden

OAuth 2.0 Authorization Code with PKCE

Also known as: oidc-code-flow, pkce

Intent

Let a client obtain tokens on a user's behalf without ever handling the user's credentials, safely even for public clients.

Problem

Apps need delegated access to APIs, but passing passwords to every client is unsafe. Public clients such as SPAs and mobile apps cannot keep a client secret, so a stolen authorization code could be redeemed by an attacker.

Forces

  • •Public clients cannot store secrets
  • •Redirect-based flows expose codes to interception
  • •Users expect single sign-on across apps
  • •Tokens must be short-lived yet usable without constant re-login

Solution

Overview

Authorization Code with PKCE is the recommended OAuth 2.0 flow for nearly every interactive client. OAuth handles authorization (what an app may do); OpenID Connect layers an ID token on top to handle authentication (who the user is). PKCE binds the code to the client instance that started the flow.

In practice, adopt a certified library or managed identity provider rather than hand-rolling the protocol. Most vulnerabilities come from integration mistakes, not the specification, so test redirect handling, token storage and logout behavior explicitly, and review the provider security best-practice guidance whenever you add a new client type.

How it works

Client: verifier = random(64B); challenge = BASE64URL(SHA256(verifier))
Client -> AS /authorize?response_type=code&client_id=..&redirect_uri=..
                       &scope=openid profile&state=xyz
                       &code_challenge=..&code_challenge_method=S256
User authenticates at AS, consents
AS -> Client redirect_uri?code=abc&state=xyz
Client -> AS /token  grant_type=authorization_code, code=abc, code_verifier=verifier
AS: SHA256(verifier) == challenge ? issue access + ID + refresh token

The client validates state, then validates the ID token signature, issuer, audience, nonce and expiry before trusting it.

Pitfalls

  • Registering loose redirect URIs. Require exact string matching.
  • Skipping the state or nonce checks, which enables CSRF and replay.
  • Storing refresh tokens in localStorage. Prefer a backend-for-frontend holding tokens in an HttpOnly cookie session.
  • Using the ID token as an API access token. Its audience is the client, not the API.
  • Long-lived access tokens. Keep them to minutes and rotate refresh tokens with reuse detection.

Key takeaways

  • Use code flow plus PKCE for every client, public or confidential.
  • Exact redirect URIs, state and nonce are mandatory.
  • ID tokens authenticate, access tokens authorize; never swap them.
  • Consider a backend-for-frontend so browsers never see tokens.

✓ When to Use

  • •Browser SPAs, native and mobile apps
  • •Server-rendered apps that want OIDC login
  • •Any third-party access to user data

✗ When Not to Use

  • •Machine-to-machine calls (use client credentials)
  • •Legacy implicit flow or password grant scenarios
  • •Internal services already trusting mTLS identity

Pros

  • +No credentials reach the client
  • +Stolen code is useless without the verifier
  • +Standard, widely supported by IdPs and libraries

Cons

  • −Redirect handling is easy to misconfigure
  • −Refresh token storage in browsers is hard
  • −Adds an IdP dependency to login

Cost Profile

Infrastructure

Medium — IdP or managed identity service

Operational

Medium — key rotation, client registration

Cognitive

High — many parameters and flows

Failure Modes

  • Open or wildcard redirect URIs leak codes

  • Missing state check enables login CSRF

  • Refresh tokens stolen from local storage

Real-World Examples

Alternatives

    Related Patterns

      Competency Domains

      security compliancedistribution communication