Authorization Code Flow + PKCE
Why Authorization Code + PKCE?
Section titled “Why Authorization Code + PKCE?”This is the recommended flow for public clients — SPAs and mobile apps — that cannot keep a client secret secure. The older Implicit flow is deprecated and must not be used in new projects.
PKCE (Proof Key for Code Exchange) protects against authorization code interception attacks. Because a public client cannot hold a secret, PKCE replaces it: the app generates a one-time secret (code_verifier) at the start of each flow, derives a code_challenge from it, and later proves it holds the original verifier when exchanging the code for tokens.
The actors and their roles
Section titled “The actors and their roles”The user’s browser drives the redirect dance. Your SPA or mobile app (the client) initiates the flow and handles the redirect callback. Keycloak (the authorization server) authenticates the user and issues tokens. Your API (the resource server) accepts the resulting access token to authorize calls.
Step-by-step flow
Section titled “Step-by-step flow”- The app generates a random
code_verifierand derives acode_challenge(SHA-256 hash, base64url-encoded). - The app redirects the browser to Keycloak’s authorization endpoint with:
response_type=code,client_id,redirect_uri,scope=openid,code_challenge,code_challenge_method=S256, and astateparameter. - Keycloak shows the login page. The user enters credentials and authenticates.
- Keycloak redirects back to the app’s
redirect_uriwith an authorizationcode(and thestatevalue for CSRF protection). - The app sends the
codeandcode_verifierto Keycloak’s token endpoint (POST /token) to exchange for tokens. - Keycloak verifies that
SHA-256(code_verifier)matches thecode_challengesent earlier. If they match, it returns theaccess_token,id_token, andrefresh_token.
Token exchange (step 5)
Section titled “Token exchange (step 5)”The curl below shows the token exchange at step 5. Fill in your values before running.
curl -X POST https://${KC_URL}/realms/${REALM}/protocol/openid-connect/token \
-d "grant_type=authorization_code" \
-d "client_id=${CLIENT_ID}" \
-d "code=${AUTH_CODE}" \
-d "redirect_uri=${REDIRECT_URI}" \
-d "code_verifier=${CODE_VERIFIER}"