Learn Keycloak - Client Registration & Dynamic Clients
Episode 26 of 31

Learn Keycloak - Client Registration & Dynamic Clients

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.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

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.

The Client Registration Concept

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:

  • Endpoint: the path /clients-registrations/openid-connect under each realm
  • Methods: POST to register, GET to read, PUT to update, DELETE to delete
  • Format: a JSON document containing client metadata

Client 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.

Initial Access Token

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:

Registering a client dynamically
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.

Managing Registered Clients

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:

Updating a client with the registration access token
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.

Client Types: Public vs Confidential

The client model determines how securely credentials can be stored. Here's the comparison:

AspectPublic ClientConfidential Client
ExamplesSPA, mobile appBackend, service-to-service
SecretHas noneHas client_secret or a key
Main flowAuthorization Code + PKCEAuthorization Code, private key JWT, mTLS
Secret riskCan't be stored securelyMust be guarded server-side
Commonly used forEnd-users in browser/phoneAPI 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.

Client Policies

At scale, managing every client individually isn't realistic. Client policies enable centralized governance:

  • Client profiles — sets of configuration that can be applied to many clients at once.
  • Client executors — units that apply configuration, e.g. forcing a minimum client_secret length or adding attributes to tokens.
  • Client conditions — the conditions under which a profile applies, e.g. based on role, scopes, or client metadata.
  • Policy enforcement — a default profile forced onto all new clients, and the enforcer of compliance.

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.

Client Authentication Methods

When a confidential client communicates with Keycloak, it must prove its identity. The methods vary:

MethodHow it worksSecurity Level
Client secretshared secret sent with the token requestBasic
Client JWTclient signs an assertion with a secretMedium
Private key JWTclient signs an assertion with a private keyHigh
mTLSclient certificate verified at the TLS layerHigh
Client assertiona JWT token proving the client's identityVaries

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.

Closing

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:

  • The initial access token is the gate — limit its usage and validity.
  • Don't store secrets in public clients — PKCE is the answer for SPAs and mobile apps.
  • Client policies enforce standards automatically — compliance without per-client manual review.
  • Private key JWT and mTLS are the strongest client authentication methods.

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.

Learn Keycloak - Client Registration & Dynamic Clients | Learn SSO with Keycloak