Sequence diagram of the OAuth 2.0 Authorization Code flow with PKCE — the recommended pattern for SPAs, mobile apps, and any public client that cannot keep a secret.
sequenceDiagram
autonumber
participant U as User
participant C as Client App
participant A as Authorization Server
participant R as Resource Server
U->>C: Click "Sign in"
C->>C: Generate code_verifier + code_challenge
C->>A: GET /authorize?response_type=code&code_challenge&...
A->>U: Show login + consent
U->>A: Approve
A-->>C: 302 redirect with auth_code
C->>A: POST /token (auth_code + code_verifier)
A->>A: Verify PKCE challenge
A-->>C: { access_token, refresh_token, id_token }
C->>R: GET /api/me (Bearer access_token)
R-->>C: 200 user profile JSON
Note over C,A: Refresh: POST /token grant_type=refresh_token
Step-by-step interaction between the user, client app, authorization server, and resource server during an OAuth 2.0 sign-in. The client generates a PKCE code_verifier and a derived code_challenge, redirects the user to /authorize, and after consent receives a short-lived authorization code. It exchanges the code (plus the original code_verifier) for tokens at /token; the auth server verifies the PKCE challenge before issuing an access token, refresh token, and OIDC id_token. The client then calls a protected resource as a Bearer.
This is the modern default for any new OAuth integration. PKCE makes the flow safe even for public clients (SPAs, mobile, CLIs) that cannot keep a client secret confidential. Use it whenever the client can perform a redirect — almost always preferred over the older Implicit flow (now deprecated) and the Resource Owner Password Credentials grant (which leaks the user password to the client).
For confidential server-side clients, you can keep the PKCE step (defense in depth) and additionally include the client_secret on the token request. For machine-to-machine flows where there is no user, switch to the Client Credentials grant (a much shorter sequence). For step-up auth, add an acr_values parameter to the /authorize call and prompt the user for a stronger factor. If you need offline access, request the offline_access scope and persist the refresh token in the browser via httpOnly cookie or in secure mobile storage.