Client Credentials Flow
The Client Credentials flow is designed for server-to-server authentication where no user is involved. The client (your application) authenticates directly with the authorization server using its own credentials to obtain an access token.
When to use
Section titled “When to use”Use the client credentials flow when:
- Backend services need to communicate with each other
- Scheduled jobs or cron tasks need to access APIs
- Microservices need to authenticate with other services
- Daemons or background workers need API access
- Any scenario where no user is involved in the authorization
How it works
Section titled “How it works”The client credentials flow is straightforward:
- Your service sends a token request to the authorization server’s
/auth/tokenendpoint, including the client ID, client secret, and requested scopes - The auth server validates the credentials and returns an access token
- Your service uses the token to authenticate when calling other APIs
Token request
Section titled “Token request”Make a POST request to the token endpoint with your client credentials.
Request parameters
Section titled “Request parameters”| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be client_credentials |
client_id | Yes | Your client identifier |
client_secret | Yes | Your client secret |
scope | Yes | Space-separated list of resource:permission scopes |
Authentication methods
Section titled “Authentication methods”You can provide client credentials in two ways:
curl -X POST https://auth.example.com/auth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=my-service" \ -d "client_secret=my-secret" \ -d "scope=product-api:read product-api:write"curl -X POST https://auth.example.com/auth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -u "my-service:my-secret" \ -d "grant_type=client_credentials" \ -d "scope=product-api:read product-api:write"Token response
Section titled “Token response”{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 3600, "scope": "product-api:read product-api:write"}Note that:
- No refresh token is issued for client credentials flow
- No ID token is issued (there’s no user to identify)
- You should request a new token before the current one expires
Using the access token
Section titled “Using the access token”Include the access token in the Authorization header when calling protected APIs:
curl -X GET https://api.example.com/products \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Configuration
Section titled “Configuration”Creating a client for client credentials
Section titled “Creating a client for client credentials”- Navigate to Clients → Add Client in the admin console
- Set Client type to Confidential (required for client credentials)
- Enter a Client identifier (e.g.,
my-backend-service) - Go to OAuth2 flows and enable Client Credentials
- Note your Client ID and Client Secret
Setting up permissions
Section titled “Setting up permissions”The client credentials flow uses resource:permission scopes. To set this up:
- Create a Resource (Settings → Resources → Add Resource)
- Example:
product-api
- Example:
- Add Permissions to the resource
- Example:
read,write,delete
- Example:
- Assign permissions to your client (Clients → [Client] → Permissions)
- Check the permissions the client should have
The client can then request these scopes:
scope=product-api:read product-api:writeError handling
Section titled “Error handling”Error responses follow RFC 6749 Section 5.2 and include cache prevention headers:
Cache-Control: no-storePragma: no-cacheCommon errors
Section titled “Common errors”| Error | HTTP Status | Description |
|---|---|---|
invalid_client | 401 | Client authentication failed (wrong ID or secret) |
invalid_scope | 400 | Requested scope is invalid or not allowed for this client |
unauthorized_client | 400 | Client is not authorized for client_credentials grant |
invalid_request | 400 | Missing required parameter |
For invalid_client errors, a WWW-Authenticate: Basic header is included when HTTP Basic authentication was used.
Example error response
Section titled “Example error response”HTTP/1.1 401 UnauthorizedContent-Type: application/jsonCache-Control: no-storePragma: no-cacheWWW-Authenticate: Basic
{ "error": "invalid_client", "error_description": "Client authentication failed. Please review your client_secret."}Security best practices
Section titled “Security best practices”- Protect client secrets - Store secrets in environment variables or secret management systems, never in code
- Use minimal scopes - Only request the permissions your service actually needs
- Rotate secrets regularly - Periodically regenerate client secrets and update services
- Monitor token usage - Log and monitor API calls to detect unusual patterns
- Use short token lifetimes - Configure appropriate expiration times for your use case
- Network security - Restrict which networks can access the token endpoint if possible
- Don’t share credentials - Each service should have its own client credentials
Token validation
Section titled “Token validation”When your API receives a token from another service, validate it:
- Verify the signature using the authorization server’s public keys (JWKS endpoint)
- Check the issuer (
iss) matches your authorization server - Verify the audience (
aud) includes your API’s resource identifier - Check expiration (
exp) to ensure the token hasn’t expired - Validate scopes to ensure the token has the required permissions
See the Tokens documentation for more details on token validation.