OAuth 2.0 Flow Diagram: Authorization Code with PKCE, Step by Step

The OAuth 2.0 authorization code flow with PKCE as a sequence diagram: every redirect, parameter and token, what each party checks, and which old flows to avoid.

· Founder & developer

Published

OAuth 2.0 Flow Diagram: Authorization Code with PKCE, Step by Step

OAuth 2.0 is easy to use and hard to picture. A login button triggers redirects between three or four parties, each carrying parameters with similar names, and most bugs come from one party skipping a check it was supposed to make. A sequence diagram is the clearest way to see who sends what to whom, and in which order.

This post draws the flow you should use today for almost every client: the authorization code grant with PKCE. It walks through each step, explains what each party checks, and lists the older flows that current guidance tells you to drop.

The four parties

Role in the spec In practice Holds
Resource owner The user Their credentials
Client Your web app, single-page app or mobile app The tokens it receives
Authorization server Your identity provider User accounts, consent, signing keys
Resource server Your API The data, and the rules for which tokens it accepts

The client never sees the user's password. That is the point of OAuth: the authorization server authenticates the user and hands the client a token with limited scope and lifetime.

Which flow, in 2026

The original OAuth 2.0 framework, RFC 6749, defined several grants. The OAuth 2.0 Security Best Current Practice, RFC 9700, published in January 2025, narrows the choice:

  • Authorization code with PKCE for user sign-in, from any kind of client. Public clients (mobile apps, single-page apps) must use PKCE; for confidential clients with a server-side secret it is recommended.
  • Not the implicit grant. It returns tokens in the browser redirect, where they can leak; clients should not use it.
  • Not the resource owner password credentials grant. Handing the user's password to the client defeats the purpose; the BCP says it must not be used.
  • Client credentials for service-to-service calls with no user involved.

The OAuth 2.1 draft consolidates these rules into one document, so new libraries increasingly assume them.

PKCE in one paragraph

Proof Key for Code Exchange, RFC 7636, protects the authorization code. Before redirecting, the client creates a random code_verifier (43 to 128 characters) and sends only its SHA-256 hash, the code_challenge, with code_challenge_method=S256. When the client later exchanges the code for tokens, it sends the verifier. The server hashes it and compares. An attacker who intercepts the code cannot use it without the verifier, which never left the client.

The flow, step by step

  1. The user clicks Sign in. The client generates state (a random value tied to the user's session) and the PKCE verifier and challenge.
  2. The client redirects to /authorize with response_type=code, client_id, the registered redirect_uri, the requested scope, state and the code challenge.
  3. The authorization server shows its login and consent page. The client is not involved; this happens on the server's domain.
  4. The user signs in, completes MFA if required, and consents to the scopes.
  5. The server redirects back to the exact registered redirect_uri with a short-lived, single-use code and the same state. The client checks that state matches what it sent; if not, it stops. RFC 6749 recommends a code lifetime of at most 10 minutes.
  6. The client posts to /token with the code, the redirect URI and the code_verifier (plus its client authentication, if it is a confidential client). This is a back-channel request, not a redirect.
  7. The server returns tokens: an access token, usually a refresh token, and an ID token if you use OpenID Connect.
  8. The client calls the API with Authorization: Bearer <access token>.
  9. The API validates the token and responds. For JWT access tokens it checks the signature, issuer, audience, expiry and scopes locally; for opaque tokens it asks the authorization server's introspection endpoint.
  10. When the access token expires, the client posts the refresh token to /token.
  11. The server returns a new access token, and for public clients a new refresh token. RFC 9700 requires refresh tokens for public clients to be either sender-constrained or rotated, so a stolen refresh token stops working after one use by either party.

The sequence diagram

A sequence diagram with a user, a client app, an authorization server and an API. The client sends the user to authorize with a code challenge and state, gets a code back, exchanges code and verifier for tokens, calls the API with a bearer token and later refreshes it.

6 components · 11 connections

Open in Archboard
Diagram contents
  • User
  • Client app
  • Authorization server
  • API (resource server)
  • access token expired
  • 1. Sign in
  • 2. GET /authorize + code_challenge, state
  • 3. Login and consent page
  • 4. Credentials, MFA, consent
  • 5. Redirect to redirect_uri with code, state
  • 6. POST /token: code + code_verifier
  • 7. Access token, refresh token
  • 8. GET /orders, Authorization: Bearer token
  • 9. 200 OK
  • 10. POST /token: refresh_token
  • 11. New access token (and rotated refresh token)
The authorization code flow with PKCE as a sequence diagram. The client sends a code challenge with the authorization request, proves it holds the matching verifier when it exchanges the code, and uses the access token at the API until it refreshes it.

How to read it:

  • Four lifelines, left to right: the user, the client app, the authorization server and the API.
  • Solid arrows with filled heads are requests; dashed arrows are responses and redirects back.
  • Steps 2 to 5 are front-channel redirects through the browser; step 6 is the back-channel token request where PKCE pays off.
  • The opt fragment at the bottom only runs when the access token has expired.
  • The Markdown block lists the checks each party makes, which is where most real bugs hide.

Mistakes that show up in reviews

  • Not checking state. Without it, an attacker can complete a login flow in the victim's browser with the attacker's account.
  • Loose redirect URI matching. The server must compare against the registered URI exactly; prefix or wildcard matching enables code theft.
  • Tokens in local storage of a single-page app. Any script injection can read them. Many teams put a small backend in front of the SPA (a backend-for-frontend) that holds tokens and gives the browser a session cookie.
  • APIs that accept any valid token. Check the audience, or a token issued for another API will work on yours.
  • Embedded web views in mobile apps. RFC 8252 says native apps should use the system browser, so the app cannot see the user's credentials.
  • Long-lived access tokens to avoid refresh logic. Keep access tokens short and refresh them; revocation then takes effect quickly.

What to draw on an OAuth diagram

  • Every party as its own lifeline, including the browser if your client has a backend.
  • Front channel and back channel distinguished, because they have different security properties.
  • Parameter names on the arrows: state, code_challenge, code, code_verifier. Reviewers look for them.
  • Who validates what. The checks are the design; the arrows only move data around.

If you are drawing the wider security picture, the zero trust architecture diagram shows where the identity provider sits among the other controls, and the API gateway architecture diagram covers validating tokens once at the edge. The sequence diagram example explains the notation used here in more depth.

Build it in Archboard

  1. Sign in with Google or GitHub and create a diagram.
  2. From the Sequence pack, add an Actor for the user and three Lifeline nodes for the client app, the authorization server and the API, in one row with the same height.
  3. Draw each message as a straight edge between lifelines, top to bottom, with the parameter names in the label. Use dashed edges with open arrowheads for responses and redirects.
  4. Add a Fragment with the opt operator around the refresh steps.
  5. Add a Markdown block from the Core pack with the checks each party makes.

The example uses 6 diagram nodes, within the Free plan.

FAQ

What is the difference between OAuth 2.0 and OpenID Connect? OAuth 2.0 is about delegated access: the client gets a token to call an API. OpenID Connect adds identity on top: an ID token that tells the client who the user is, and a standard user info endpoint.

Do I need PKCE if my app has a client secret? RFC 9700 recommends it for confidential clients too. It protects against code injection and costs one extra parameter on each side.

Should the implicit grant still be used? No. Current best practice says clients should not use it. Use the authorization code grant with PKCE instead, also in single-page apps.

Where should a single-page app store tokens? Preferably not in the browser at all: a backend-for-frontend can keep tokens server-side and give the browser a secure session cookie. If tokens must live in the browser, keep them in memory, keep access tokens short and use refresh token rotation.

How does the API validate an access token? For JWT access tokens, by checking the signature with the authorization server's published keys plus issuer, audience, expiry and scope. For opaque tokens, by calling the introspection endpoint.

Start free — one seat, no credit card.

Related posts

← Back to blog

Open a canvas in a minute

Sign in with Google or GitHub. Free for a single seat. Upgrade when the team joins.