OAuth Flow Diagram Guide
How to draw an OAuth flow diagram that is actually useful — the four participants, front channel vs back channel, and three ready-to-edit examples including PKCE and refresh token rotation.
What is an OAuth flow diagram?
An OAuth flow diagram shows how a user grants an application access to their data on another service without handing over their password. It draws four parties and the order in which they talk: the resource owner (the human), the client (your app), the authorization server (the thing that issues tokens), and the resource server (the API holding the data).
Naming those four correctly is most of the value. The single most common mistake in OAuth diagrams is collapsing the authorization server and the resource server into one box labelled "Google". They are different systems with different jobs — one decides who you are and what you may do, the other serves data and checks the token it was handed. Once they are one box, the diagram can no longer show the thing that matters: which of them ever sees the user's password (only the first) and which only ever sees a token (only the second).
The other reason to draw it is that OAuth is a redirect protocol, and redirects are invisible in code. Reading a handler tells you what happens at one endpoint; it does not tell you that the browser bounced through three origins carrying a state parameter that must come back unchanged. A sequence diagram makes the bounce visible, which is why almost every real OAuth bug — mismatched redirect_uri, dropped state, code replayed twice — is obvious in a diagram and invisible in a stack trace.
If you have read the RFC's diagram and still cannot draw your own, that is expected: the spec draws the abstract protocol. Your diagram needs the parts the spec leaves as an exercise, which is what the examples below are.
What an OAuth flow diagram has to show
Five things. The first one is the one that separates a useful OAuth diagram from a redrawn spec figure:
- Which arrows go through the browser. OAuth has a front channel (redirects the user's browser follows, visible in the address bar and in history) and a back channel (server-to-server calls nobody can see). Every design decision in the protocol comes from that split. Mark it — a dotted arrow, a note, a colour, anything.
- What each arrow carries.
code,access_token,refresh_tokenandid_tokenare four different things with four different lifetimes and blast radii. Writing "token" on every arrow throws away the entire security story. - The authorization server and the resource server as separate lifelines, even when the same vendor operates both. See above.
- At least one rejection branch. Expired code,
statemismatch, PKCE verifier mismatch, revoked refresh token — pick the one your team actually argues about and draw it as analt. - Where the token ends up. A diagram that stops at "access token issued" answers half the question. The interesting half is what the client does with it: memory, cookie, or somewhere it should not be.
Here is the skeleton — authorization code, no error branches, four separate participants. Every example below is this diagram with one specific thing added.
View the Mermaid source
sequenceDiagram
participant User as Resource Owner
participant App as Client App
participant Auth as Authorization Server
participant API as Resource Server
User->>App: click Sign in
App->>Auth: redirect to /authorize
Auth->>User: login and consent screen
User->>Auth: approve
Auth-->>App: redirect back with code
App->>Auth: POST /token with the code
Auth-->>App: access token
App->>API: GET /me with the access token
API-->>App: profile dataHow to draw an OAuth flow diagram
Three steps. Step 1 is where most diagrams go wrong, and it takes about two minutes.
Step 1 · Pin down which flow you are drawing
"OAuth" is not one flow, and drawing a generic one produces a diagram that is wrong for every specific case. Answer one question — is there a human present at the moment the token is issued?
If yes, you are drawing authorization code with PKCE. This is the answer for web apps, single-page apps and mobile apps alike. It has been the answer for every browser-based client since PKCE became mandatory for public clients; if you find a tutorial drawing implicit flow (token returned straight in the redirect URL), it predates that and you should not copy it.
If no — a cron job, a service syncing with a partner API — you are drawing client credentials, and your diagram has no user lifeline at all. That absence is information: it is why there is no consent screen and no refresh token.
Everything else (device code for TVs, token exchange between services) is a variation you will know you need. Pick one and draw only it. A diagram covering two flows at once covers neither.
Step 2 · Split the front channel from the back channel
Go through your arrows and sort each one into two piles: the browser carries this and our server calls this directly.
The sort is mechanical — anything that is a redirect, a URL the user's address bar shows, or a form the user submits, is front channel — but the result is the whole point of the protocol. The front channel is observable: it is in browser history, in Referer headers, in server logs, potentially on a shared machine's screen. That is exactly why the authorization code goes through it and the access token does not. The code is short-lived, single-use, and useless without the client secret or a PKCE verifier — it is designed to survive being seen.
Once sorted, check one thing: is there anything on the front channel that would be damaging if a stranger read it over your shoulder? If yes, that is a finding, and it is the kind this diagram exists to produce.
Step 3 · Draw it
Describe the handshake in sentences and let text2diagram lay it out — "the app redirects the browser to the authorization server with a code challenge, the user logs in and approves, the server redirects back with a code, the app exchanges the code plus the verifier for tokens at the token endpoint" comes back as an editable sequenceDiagram.
Or open one of the examples below in the editor and rename the participants to your provider. The arrangement is the part that is hard to get right; Authorization Server → Okta is a find-and-replace.
OAuth flow diagram examples
Three flows the spec's figure does not draw for you: the modern default, what happens when a token expires, and what it looks like when there is no user at all.
1 · Authorization code with PKCE
The two arrows worth studying are the first and the last. The app generates a code_verifier and sends only its hash (code_challenge) through the front channel; at the end it sends the verifier itself through the back channel. An attacker who steals the code out of a redirect has the code but not the verifier, so the exchange fails — that is the entire idea, and it is legible in the diagram in a way it is not in prose.
Also note the check state matches self-arrow. It is not a network call, so people leave it out; leaving it out is how CSRF on the callback endpoint ships. A step that guards something deserves a box even when nothing crosses the wire.
View the Mermaid source
sequenceDiagram
autonumber
participant User as Browser
participant App as Client App
participant Auth as Authorization Server
participant API as Resource Server
App->>App: generate code_verifier and code_challenge
App->>Auth: GET /authorize with client_id, redirect_uri, code_challenge, state
Auth->>User: login and consent
User->>Auth: approve
Auth-->>App: 302 back to redirect_uri with code and state
App->>App: check state matches what we sent
App->>Auth: POST /token with code and code_verifier
alt verifier does not match the challenge
Auth-->>App: 400 invalid_grant
else verifier matches
Auth-->>App: access_token and refresh_token
App->>API: GET /me with Bearer access_token
API-->>App: profile
end2 · Refresh token rotation and reuse detection
This is the flow teams skip and then debug at 2am. Access tokens expire constantly, so this path runs far more often than the login above — and it has a branch most diagrams omit.
With rotation, each refresh burns the old token and issues a new one. That turns a stolen refresh token into a detectable event: if RT1 is presented twice, either the attacker or the real client is using a token the server already retired, and the server cannot tell which. The only safe response is to revoke the whole family and force a fresh login. Drawing that second branch is what turns "we rotate refresh tokens" from a checkbox into a behaviour with a defined outcome.
View the Mermaid source
sequenceDiagram
autonumber
participant App as Client App
participant Auth as Authorization Server
participant API as Resource Server
App->>API: GET /orders with Bearer access_token
API-->>App: 401 token expired
App->>Auth: POST /token with refresh token RT1
alt RT1 is unused and valid
Auth->>Auth: revoke RT1, issue RT2
Auth-->>App: new access_token and RT2
App->>API: retry GET /orders
API-->>App: 200 OK
else RT1 was already used - it leaked
Auth->>Auth: revoke the whole token family
Auth-->>App: 400 invalid_grant
App->>App: clear session, send the user to login
end3 · Client credentials — no user at all
The shape to notice is what is missing: no browser lifeline, no consent screen, no redirect, no front channel at all. Every arrow here is server to server, which is why this flow is allowed to use a plain client secret and why PKCE has nothing to do here.
The branch worth drawing is the boring one — the token expiring partway through a long batch. It is boring right up until a nightly sync half-completes and leaves two systems disagreeing, at which point everyone wants to know whether the job re-authenticates and resumes or gives up. The diagram answers that in one glance.
View the Mermaid source
sequenceDiagram
autonumber
participant Job as Nightly Job
participant Auth as Authorization Server
participant API as Partner API
Note over Job: no user is present - nobody to consent
Job->>Auth: POST /token with client_id and client_secret
Auth-->>Job: short lived access_token, scoped
Job->>API: POST /sync with Bearer access_token
alt token still valid
API-->>Job: 200 OK
else token expired mid batch
API-->>Job: 401
Job->>Auth: POST /token again
Auth-->>Job: fresh access_token
Job->>API: retry POST /sync
API-->>Job: 200 OK
endIf an arrow carries a secret through the browser, that is the finding
Draw the diagram, then read only the front-channel arrows and ask what each one would cost you if it were screenshotted. An authorization code: nothing, by design. An access token in a URL fragment: a session. A client secret anywhere near the browser: everything — and it means the app is a public client that should be using PKCE instead.
This is the one review pass that an OAuth diagram makes cheap and that reading code makes nearly impossible, because the front channel is spread across a redirect, a callback route and a browser you do not control.
FAQ
What is the difference between OAuth and OpenID Connect?
OAuth 2.0 is authorization — it gets your app permission to call an API on someone's behalf. OpenID Connect is authentication — a thin layer on top of OAuth that also tells your app who the user is, via an extra
id_token(a signed JWT with the user's identity). In a diagram the difference is one arrow's payload: if the token endpoint returns anid_tokenalongside the access token, you are drawing OIDC. "Sign in with Google" is OIDC; "let this app read your Google Calendar" is plain OAuth.Which OAuth flow should my diagram show?
Authorization code with PKCE if a human is present — web app, SPA, mobile, all three. Client credentials if no human is present. Those two cover the overwhelming majority of real systems. Do not draw implicit flow: it returns the access token in the redirect URL, which puts a live credential in browser history and
Refererheaders, and it has been discouraged for years. If you inherited a diagram showing it, that diagram is documenting a problem, not a design.What is PKCE and why is it in every diagram now?
PKCE (Proof Key for Code Exchange) is two extra values that prove the app redeeming the authorization code is the same app that requested it. The client generates a random
code_verifier, sends its hash (code_challenge) on the front channel, and sends the verifier itself on the back channel when redeeming. It exists because public clients — SPAs and mobile apps — cannot keep a client secret, so without it a stolen code is enough to get tokens. It is now recommended for confidential clients too, which is why it shows up in essentially every current diagram.Should I draw the authorization server and resource server separately?
Yes, even when one vendor runs both. They answer different questions — one issues and validates tokens, the other serves data — and keeping them apart is what lets the diagram show that the resource server never sees a credential, only a token it validates. Merging them also hides a real operational fact: they usually have separate hostnames, separate outages and separate rate limits. The one exception is a deliberately abstract overview where you are teaching the concept rather than documenting a system.
How do I show token expiry and refresh in the same diagram?
Usually you should not — draw the login and the refresh as two diagrams. They happen at different times, run at wildly different frequencies, and combining them produces a diagram with a long tail nobody reads to the end of. Draw the login flow ending at "tokens issued", then a second diagram that starts from an expired access token, like the second example above. If you must combine them, use a
loopblock around the refresh section so the reader can see it repeats rather than following a one-time path.Is it free?
Yes. Anonymous users get 20 generations per day, logged-in users 500. Opening any example on this page in the editor costs nothing — that path does not call the model at all.