Registering applications dynamically through the Client Registration endpoint with an initial access token, managing client policies, and choosing client authentication methods from shared secret to private key JWT and mTLS.

In episode 25 you governed what users may do with fine-grained authorization. Episode 26 discusses the other side of the client ecosystem: how new applications can join without going through the admin console every time. Dynamic client registration allows applications to register themselves via the API — a very useful pattern in the era of CI/CD, SPAs, and an ever-growing number of microservices.
So far you've created clients one by one in the admin console. At scale that's inefficient. Keycloak provides a Client Registration endpoint that accepts dynamic registration per the OpenID Connect Dynamic Client Registration specification:
/clients-registrations/openid-connect under each realmPOST to register, GET to read, PUT to update, DELETE to deleteClient metadata covers redirect_uris, grant_types, response_types, client_name, client_secret, and more — exactly what you'd normally fill into the admin console form, but in JSON that a pipeline can produce.
Dynamic doesn't mean uncontrolled. Combine dynamic registration with the client policies below: registration stays open via the API, but security standards are still enforced by centralized policy. For internal applications that rarely change, the admin console remains faster — dynamic registration excels when the number of clients is large and changes frequently.
Dynamic registration must not be open without control. Keycloak uses the initial access token: a token admins create at Realm Settings → Client Registration → Initial Access Token. When creating it, you can limit the number of uses and its validity period — once the quota is exhausted, the token can't be used anymore:
curl -X POST "https://sso.example.com/realms/bank/clients-registrations/openid-connect" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6I..." \
-H "Content-Type: application/json" \
-d '{
"client_id": "bank-mobile",
"client_name": "Bank Mobile",
"grant_types": ["authorization_code", "refresh_token"],
"redirect_uris": ["https://bank.example.com/callback"],
"public": true
}'The initial access token is sent in the Authorization header. The registration response contains client_id, client_secret (for confidential clients), and just as important the registration access token — the token for managing that client later. Store both securely; they are the key to managing that client.
Once registered, a client can be managed via the registration_client_uri returned in the response. Carrying the registration access token, an application or pipeline can read, update, or delete its own client configuration — without admin access:
curl -X PUT "https://sso.example.com/realms/bank/clients-registrations/openid-connect/119ef730-e427" \
-H "Authorization: Bearer <registration_access_token>" \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": ["https://bank.example.com/callback", "https://bank.example.com/mobile"]
}'This pattern is very useful for automated deployments: every release of a new service registers or updates its own client with the right redirect URIs and grant types, and deletes its client when the environment is torn down.
The registration access token is unique per client and can only manage that client — it can't be used to change another client. If lost, an admin can reissue it through the admin console, so treat it as a credential as important as client_secret.
The client model determines how securely credentials can be stored. Here's the comparison:
| Aspect | Public Client | Confidential Client |
|---|---|---|
| Examples | SPA, mobile app | Backend, service-to-service |
| Secret | Has none | Has client_secret or a key |
| Main flow | Authorization Code + PKCE | Authorization Code, private key JWT, mTLS |
| Secret risk | Can't be stored securely | Must be guarded server-side |
| Commonly used for | End-users in browser/phone | API server, daemons |
Rule of thumb: if the application can be decompiled or its code is readable in the browser, it's public — never embed a secret in it. Use PKCE. Applications running on a server can be confidential. One note: direct access grants (Resource Owner Password) are indeed available in Keycloak, but are considered a weak pattern and are no longer recommended — avoid them except for legacy cases that truly can't be avoided.
At scale, managing every client individually isn't realistic. Client policies enable centralized governance:
client_secret length or adding attributes to tokens.With client policies, you can guarantee a minimum security standard — all new clients automatically use an approved authentication method and a strong secret — without reviewing every registration by hand.
When a confidential client communicates with Keycloak, it must prove its identity. The methods vary:
| Method | How it works | Security Level |
|---|---|---|
| Client secret | shared secret sent with the token request | Basic |
| Client JWT | client signs an assertion with a secret | Medium |
| Private key JWT | client signs an assertion with a private key | High |
| mTLS | client certificate verified at the TLS layer | High |
| Client assertion | a JWT token proving the client's identity | Varies |
client_secret_post and client_secret_basic are basic methods based on a shared secret. private key JWT (private_key_jwt) raises the bar: the client holds a private key while the public key is registered in Keycloak — there's no shared secret that can leak from the server side. For the highest security, mTLS verifies the client certificate directly at the TLS layer. Client assertion is the general term for proving a client's identity with a JWT token, used by several of the methods above.
Tip
For service-to-service connections, start with private_key_jwt and make sure client secrets never reach logs. Clients that have long used a shared secret should be migrated gradually to private key JWT — client by client, not all at once.
Episode 26 taught client management at scale: dynamic client registration through the Client Registration endpoint with initial access tokens and registration access tokens; managing registered clients; the difference between public and confidential clients; client policies for enforcing standards; and client authentication methods from shared secret to private key JWT and mTLS.
Key takeaways:
In the next episode (episode 27), you take Keycloak to enterprise scale: high availability & clustering — many instances, distributed caching, and load balancing behind a single entrance.