Keycloak and Enterprise Identity
Four Flows and Where Each Belongs
Summary
For public clients, authorization code + PKCE; for server-to-server communication, client credentials. Most of the rest are flows with reasons not to use them.
Why this was needed
The question "why does a single Google login button need such a complicated protocol?" is natural. The answer is this. The goal is to let a third-party app access limited resources on the user's behalf without giving the user's password to that app.
Several flows were created for this goal, and over time some were deprecated.
How it works
Authorization Code is the default and the standard. The user is redirected to the authentication server and logs in, returns to the app with an authorization code, and the app's backend exchanges that code for tokens. The key points are that the code is exposed in the browser URL but is single-use and cannot be used alone. The exchange requires the client secret.
The problem is SPAs and mobile apps. They cannot keep a secret safe. If you put it in the source, anyone can see it. So in the past the Implicit flow — receiving the token directly in the URL fragment without a code — was used. It has the serious problem that the token remains in the browser history and in the referrer, so it is no longer recommended.
The replacement is PKCE (Proof Key for Code Exchange). For every request, the client generates a random string code_verifier and sends a code_challenge, which is its SHA256 hash encoded in Base64URL, in the authorization request. Later, when exchanging the code for tokens, it submits the original code_verifier. The authentication server recomputes the hash and checks that they match. Even if an attacker intercepts the authorization code, without the code_verifier they cannot obtain a token. It becomes safe even without a secret.
Client Credentials is for server-to-server communication with no user involvement. Batch jobs, service accounts, internal API calls. Since there is no user context, sub is a service account.
Resource Owner Password is a flow in which the app directly receives the user's ID and password and exchanges them for a token. It is exactly the pattern OAuth set out to eliminate, so it is effectively deprecated. It is not used except for legacy migration.
What you meet in the field
Here is a summary of where each kind of token is stored and its lifetime.
| Token | Purpose | Lifetime | Where stored |
|---|---|---|---|
| Authorization Code | Single-use, for token exchange | A few seconds | URL parameter (disappears immediately) |
| ID Token | Verifying the user's identity | 5–60 minutes | Client |
| Access Token | Permission to access APIs | 5–60 minutes | Memory or BFF |
| Refresh Token | Renewing access tokens | Long (revocable) | httpOnly cookie or server |
The reason you must not keep a refresh token in localStorage is clear. JavaScript can read it through XSS, and a refresh token is long-lived, so if it is stolen it allows persistent access.
You also must not omit the state parameter. Without it, an attacker can send an authorization code issued to their own account to the victim's callback and make the victim log in to the attacker's account.
Where to keep the token in a browser app
Even if you choose the flow correctly, things diverge again on where to store the token you received. There are only three places you can choose in a browser, and what each is weak against differs.
| Place | Weak point | Notes |
|---|---|---|
localStorage |
Is read as is through XSS | It survives a refresh and is convenient, but is the most dangerous |
| JavaScript memory | Still exposed to XSS, disappears on refresh | Reasonable for holding an access token briefly |
httpOnly cookie |
JavaScript cannot read it, but you must block CSRF separately | Needs SameSite and verification together |
The practical answer that came out of this is the BFF (Backend For Frontend). The server holds the tokens without giving them to the browser at all, and gives the browser only a session cookie. The server receives the browser's requests, attaches the token, and forwards them behind. It removes the problems of the previous three places at once, in exchange for one more layer of server.
It must be made clear that whichever you choose, most things collapse if there is XSS. Once a script runs, even if it cannot read the token directly, it can send requests from that page on your behalf. So you must care about content security policy and input handling with the same weight as worrying about where to store tokens.
Logout is also less simple than it seems. As we saw in the previous module, access tokens are not revoked, so logout is closer to invalidating the refresh token and discarding what the client holds. If several applications share the same login session, when you log out in one place the others must also be cleaned up, and there is a separate convention for that and each application has to implement it. It is not "I pressed the logout button, so it is over", but a matter of deciding in the design how far things are cleaned up.
What to look at in the next check
This module covers only concepts. From the next module on, you will get tokens from a real Keycloak and take them apart, and then complete the PKCE flow with a script.