Learn Keycloak - Client Credentials Flow
Episode 6 of 31

Learn Keycloak - Client Credentials Flow

Learning the client credentials flow for machine-to-machine communication: recognizing when this flow fits, preparing a confidential client, using service accounts and client roles, and integrating it with backend APIs without user presence.

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

Introduction

In episode 5 you learned the authorization code flow, which takes users through the Keycloak login screen, complete with PKCE and redirect URIs. Episode 6 flips the perspective: there's no user behind the screen at all. You'll learn the client credentials flow — the OAuth 2.0 flow for communication between two applications, not between an application and a human. This is the flow most often used when one backend service calls another backend service.

When to Use Client Credentials

Client credentials is the simplest flow of all the OAuth 2.0 flows:

  • Machine to machine — no human types a password.
  • No user — the authenticated identity is the application, not a person.
  • No consent screen — there's no user who needs to approve anything.
  • No refresh token — tokens are issued on direct request and can be re-requested at any time.

The analogy: if the authorization code flow is an employee showing their ID card when entering a building, client credentials is the access card of a delivery robot — it doesn't matter who sent it, the important thing is the robot is authenticated.

The Client Credentials Flow Step by Step

The flow is brief, just two major steps:

  1. The client sends its credentials (client ID and client secret) to the token endpoint.
  2. The token endpoint validates the credentials, then returns an access token.

No redirects, no authorization code, no user interaction:

Requesting an access token via client credentials
curl -X POST "https://kc.example.com/realms/my-realm/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=payment-service" \
  -d "client_secret=4f9a2c8d..."

Notice grant_type=client_credentials as the flow marker. Without that parameter, Keycloak doesn't know which flow you're using. The response comes back as JSON:

Token endpoint response
{
  "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
  "expires_in": 300,
  "token_type": "Bearer",
  "scope": "email profile"
}

An important note: in this flow Keycloak does not return a refresh_token — unlike flows involving a user. Since there's no user session to maintain, the client simply requests a new token when the old one expires.

Service Accounts and Client Roles

Every confidential client created in Keycloak automatically has a service account — a virtual user whose name matches the client ID. When you open the Service account roles tab in the admin console, this virtual user is what the token represents.

Because a service account is a regular user from Keycloak's point of view, you can:

  • Assign client roles — roles scoped to a specific client.
  • Assign realm roles — roles that apply across the whole realm.
  • Configure permissions — restrict which APIs the service may call.

The assigned roles appear in the access token:

Service account access token payload
{
  "sub": "bf3a1c2e-...",
  "preferred_username": "service-account-payment-service",
  "realm_access": {
    "roles": ["view-orders"]
  },
  "resource_access": {
    "payment-service": {
      "roles": ["approve-payment"]
    }
  }
}

Here realm_access.roles holds the global roles, while resource_access holds client roles specific to the target service. The target server can check these roles before processing a request — this is the basis of machine-to-machine access control in Keycloak.

Setting Up a Confidential Client

To use this flow, the client must be of type confidential — a client that has a secret. The steps in the admin console:

  1. Create a new client, name it e.g. payment-service.
  2. Set Client authentication to On — this is what makes the client confidential.
  3. Leave Standard flow off, since this flow doesn't use redirects.
  4. Enable Service accounts roles so the service account tab appears.
  5. Save, then copy the Client secret from the Credentials tab.

Tip

Run a local Keycloak with kc.sh start-dev if you don't have one yet, then open the admin console at http://localhost:8080/admin. The names my-realm and payment-service in this episode are examples used consistently throughout the series.

The secret is confidential. Never put it in frontend code, public repositories, or logs. Store it in an environment variable or a secret manager like Vault, and rotate it periodically via the Credentials tab.

Client Authentication Methods

A client secret is just one method. Keycloak supports several ways for a client to prove its identity:

MethodHow It WorksStrength
Client secretSecret sent in the Authorization header or form bodySimplest, suitable for internal use
Signed JWTClient signs a JWT assertion with its private keyNo secret in transit
mTLSClient uses a TLS certificate for authenticationStrongest, suited for tight perimeters

The method is chosen via the Client authentication dropdown in the Credentials tab. For most internal cases, a client secret is enough; for cross-organization integrations or certificate-based infrastructure, consider JWT or mTLS.

API Integration Example

The most common pattern is a backend service requesting a token, then sending it to the target API as a Bearer token:

Calling an API with the access token
TOKEN=$(curl -s -X POST "https://kc.example.com/realms/my-realm/protocol/openid-connect/token" \
  -d "grant_type=client_credentials" \
  -d "client_id=payment-service" \
  -d "client_secret=4f9a2c8d..." | jq -r .access_token)
 
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://api.example.com/orders/pending"

Key steps on the target API side: validate the token signature via JWKS, check iss, aud, and exp, then check the roles in realm_access or resource_access before allowing the request.

Real-World Use Cases

This flow appears most often in four scenarios:

  • Backend services — internal services calling each other without user involvement.
  • Microservice authentication — each service uses its own token, and inter-service access is restricted by roles.
  • Scheduled jobs — cron jobs or batch pipelines that need a token to call an API.
  • Third-party integration — external applications subscribed to public APIs with their own credentials.

For scheduled jobs, remember: tokens have a limited lifetime (expires_in). Don't store old tokens on disk; request a new token when you're about to use it.

Closing

In this episode (episode 6) you learned the client credentials flow: when to use it, how tokens are requested and used, the role of service accounts and client roles, confidential client configuration, client authentication methods, and an API integration example.

Key takeaways:

  • No user, no refresh token — the identity is the application, and tokens are re-requested when they expire.
  • The service account is the access key — client roles in resource_access control what a service may do.
  • The secret is a sensitive asset — store it in a secret manager, not in code or logs.
  • The target API must still validate — check signature, iss, aud, exp, and roles before processing.

In the next episode (episode 7) we go deeper into token management: refresh tokens — how to extend a user's access without asking for their password again, when tokens are rotated, and how to invalidate them when stolen or terminated.