Breaking down the Authorization Code flow step by step: authorization request, login and consent, the code, token exchange, using the access token, and the PKCE extension for added security.

Episode 4 introduced OAuth 2.0 generally and called Authorization Code the safest grant type for web apps. Episode 5 breaks it down step by step: how the browser, the application, and Keycloak communicate, what happens when the user logs in, how the code is exchanged for tokens, and how PKCE keeps this flow safe for SPAs and mobile apps.
Authorization Code is the safest grant type for web applications because the user's password never reaches the application or browser — the user interacts directly with Keycloak's login page. The application only receives a short-lived code that is exchanged for tokens on the server, so the access token is never exposed in a URL or in JavaScript.
This security is why the flow has become the de facto standard for modern web apps and the basis of many SSO integrations.
The Authorization Code flow runs in six main steps:
| Step | Actor | What Happens |
|---|---|---|
| 1. Authorization request | Browser to Keycloak | The app redirects the browser to the auth endpoint |
| 2. Login | Browser to Keycloak | The user enters credentials on the login page |
| 3. Consent | Browser to Keycloak | The user approves the permissions the app requests |
| 4. Callback | Keycloak to browser | Keycloak redirects back with an authorization code |
| 5. Token exchange | App to Keycloak | The app exchanges the code for access, refresh, and ID tokens |
| 6. Access | App to resource server | The app uses the access token to call the API |
The application redirects the user's browser to Keycloak's authorization endpoint with several required parameters:
http://localhost:8080/realms/belajar/protocol/openid-connect/auth?client_id=portal-app&redirect_uri=http://localhost:3000/callback&response_type=code&scope=openid&state=acak-1234The response_type=code parameter signals that the client wants the authorization code flow, client_id names the application, redirect_uri is where Keycloak redirects back, and state is a random value for CSRF protection that must be verified on callback.
Keycloak displays the login page. After the credentials are correct, if the application requests certain scopes, the user sees a consent page explaining the requested permissions. This user approval is the essence of delegated authorization.
After a successful login, Keycloak redirects the browser back to redirect_uri, inserting code and state into the query string. The application must verify state before continuing. This code has a very short lifetime and can only be used once.
Important
Always verify the state parameter when the callback arrives before
processing the code. Without this verification, your application
is vulnerable to CSRF attacks that can hijack the login session.
The application — not the browser — exchanges the code at the token endpoint:
curl -X POST "http://localhost:8080/realms/belajar/protocol/openid-connect/token" \
-d "grant_type=authorization_code" \
-d "client_id=portal-app" \
-d "client_secret=rahasia-aplikasi" \
-d "code=KODE_DARI_CALLBACK" \
-d "redirect_uri=http://localhost:3000/callback"The response contains the access token, refresh token, and ID token all at once. The access token is used to call the resource server, the ID token provides the user's identity information, and the refresh token gets a new access token.
PKCE (Proof Key for Code Exchange) protects the authorization code from misuse, especially on SPAs and mobile apps that can't store a secret. The principle: the application proves that the code being sent is really its own.
S256 method computes a SHA-256 hash, while the plain method sends the verifier as-is.The PKCE steps in the Authorization Code flow:
| Stage | Value Sent |
|---|---|
| Start of flow | code_challenge and code_challenge_method |
| Token exchange | code_verifier |
| Verification | Keycloak matches the challenge derived from the verifier |
Because mobile apps and SPAs don't have a secure client_secret, PKCE takes over the client identity-proofing role. Even though server-side apps may still use a secret, best practice now recommends PKCE everywhere.
To enable this flow in Keycloak:
confidential if the app can store a secret, public if it can't (SPA and mobile).redirect_uri values explicitly. Never use a loose wildcard.S256 as the challenge method.Four best practices that keep this flow safe:
Episode 5 broke down the Authorization Code flow: the authorization request to the /realms/{realm}/protocol/openid-connect/auth endpoint, user login and consent, the callback carrying the code, exchanging the code for access, refresh, and ID tokens at the token endpoint, and the PKCE extension that keeps this flow safe for SPAs and mobile.
Key takeaways:
In the next episode (episode 6), we cover the Client Credentials flow — how backend services communicate with each other without a user, complete with service accounts and their best practices.