Skip to content

Authorization Code Flow + 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 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.

  1. The app generates a random code_verifier and derives a code_challenge (SHA-256 hash, base64url-encoded).
  2. 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 a state parameter.
  3. Keycloak shows the login page. The user enters credentials and authenticates.
  4. Keycloak redirects back to the app’s redirect_uri with an authorization code (and the state value for CSRF protection).
  5. The app sends the code and code_verifier to Keycloak’s token endpoint (POST /token) to exchange for tokens.
  6. Keycloak verifies that SHA-256(code_verifier) matches the code_challenge sent earlier. If they match, it returns the access_token, id_token, and refresh_token.

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}"
Why is PKCE needed for public clients?
What does the app send with the authorization request to enable PKCE?
What does Keycloak verify when the code is exchanged at the token endpoint?
Which flow is deprecated and should NOT be used?