OAuth2 Flows
OAuth2 defines several grant types, also called flows. Different choreographies, same goal: get an access token into the hands of the right client.
Picking the right flow comes down to two questions. Who is the client? And can it keep a secret?
| Flow | Use when |
|---|---|
| Authorization Code + PKCE | A user is in front of a browser or mobile app |
| Client Credentials | A backend service authenticates as itself, no user involved |
| Device Authorization | The device has no good way to type a password (TV, CLI) |
| Refresh Token | Renew an access token without bothering the user |
A few older flows (Implicit, Resource Owner Password Credentials) still exist but you shouldn’t reach for them. OAuth 2.1 drops them outright.
Authorization Code with PKCE
The most common flow. Used by every web app, single-page app, and mobile app that logs users in.
The story
A user visits a web app and clicks “Sign in.” The app sends them to FerrisKey, the user logs in, FerrisKey sends them back with a short code, and the app exchanges that code for a token.
The detail that matters: the code travels through the user’s browser, where it is exposed. PKCE (Proof Key for Code Exchange) makes that exposure harmless.
How PKCE works
Before the redirect, the client generates a random secret called the code_verifier and sends its hash (the code_challenge) in the authorization request. When the client later trades the code for a token, it must also send the original code_verifier. The server checks that hash(code_verifier) == code_challenge.
An attacker who intercepts the code cannot use it: they would also need the code_verifier, which never left the client.
The full exchange
sequenceDiagram
participant U as User
participant C as Client App
participant AS as Authorization Server
participant API as Resource Server
C->>C: Generate code_verifier + code_challenge
U->>C: Click "Sign In"
C->>AS: Redirect with code_challenge
AS->>U: Login page
U->>AS: Credentials
AS->>C: Redirect back with code
C->>AS: code + code_verifier
AS->>C: access_token + refresh_token
C->>API: Request + access_token
API->>C: Protected resourceUse this flow when
- You have a browser-based or mobile app and a real user.
- You want the most flexible, future-proof option.
Client Credentials
Used for machine-to-machine calls: one backend service calling another backend service. No user is involved.
The story
A nightly batch job needs to call your billing API. The batch job is the client. It has a client_id and client_secret, and it exchanges those for an access token directly.
curl -X POST https://auth.example.com/realms/my-app/protocol/openid-connect/token \
-d "grant_type=client_credentials" \
-d "client_id=batch-job" \
-d "client_secret=••••••••"
Use this flow when
- The caller is a server you control.
- The caller can safely store a secret (a private key, a static credential).
- There is no end-user in the loop.
The resulting access token represents the client itself, not a user. Permissions are tied to the client, not to a person.
Device Authorization Flow
Used by clients that cannot easily display a login form or accept typed passwords. Smart TVs, terminal CLIs, IoT devices.
The story
You log in to a CLI on your laptop. The CLI cannot open a browser inline, so it shows you a short code and a URL. You open the URL on your phone, type the code, log in. The CLI polls in the background. Once you finish, the CLI gets its token.
$ ferriskey login
Open https://auth.example.com/device in your browser
and enter code: WXYZ-1234
(waiting...)
✓ Logged in as alice@example.com
Use this flow when
- The device has limited input (no keyboard) or no browser.
- You want a CLI to authenticate as a user without prompting for a password.
Refresh Token grant
After an Authorization Code flow, the client receives both an access_token (short-lived, minutes) and a refresh_token (longer-lived). When the access token expires, the client uses the refresh token to obtain a new one silently, without redirecting the user.
curl -X POST .../token \
-d "grant_type=refresh_token" \
-d "refresh_token=••••••••" \
-d "client_id=my-frontend"
See Tokens for how the two token types differ.
Flows that are out of fashion
- Implicit Flow: the access token was returned directly in the URL fragment. Replaced by Authorization Code + PKCE.
- Resource Owner Password Credentials (ROPC): the client collects the user’s password and forwards it. Defeats the point of OAuth2. Useful for migration scripts only.
In FerrisKey
Each flow maps to a setting on your FerrisKey client: