Client Credentials
RFC 6749 §4.4Client type: Confidential clients acting on their own behalf (no user involved) – machine-to-machine (M2M)
The Client Credentials grant issues tokens to applications acting on their own behalf, not on behalf of a user. A service authenticates with its client_id and client_secret to get an access token. No user authorization UI involved. Standard for microservice-to-microservice API calls, batch jobs, background workers, and CI/CD pipelines calling APIs.
How it works
Client Credentials is the machine-to-machine (M2M) grant type. There is no user, no authorization UI, no redirect.
Flow (two steps total): 1. Client POSTs client_id, client_secret, grant_type=client_credentials, scope to /token 2. Authorization server returns access_token (no refresh_token – client can just re-authenticate)
No refresh token: since the client can authenticate at any time (it has its credentials), there is no need for a refresh token in Client Credentials. Just request a new access token when the current one expires.
Use cases: - Service A calls Service B's API: Service A authenticates with its own credentials - GitHub Actions calling a protected API - Cron jobs and batch processors - CI/CD pipelines deploying to cloud APIs - Microservice mesh authentication (often replaced by mTLS in service meshes)
Scopes in M2M: scopes still apply to restrict what the service can access. A reporting service should only have read scopes, not write scopes, even for M2M tokens.
Client secret security: client secrets must be stored securely (environment variables, secrets manager). Rotate them regularly. Consider using private_key_jwt authentication (RFC 7523) instead of client_secret for higher-security environments.
Flow Steps
- 1
Client POSTs to /token with grant_type=client_credentials, client_id, client_secret, scope
- 2
Authorization server validates credentials and issues access_token
- 3
Client uses access_token in Authorization: Bearer header for API calls
- 4
On expiry (expires_in), client requests a new token directly (no refresh token needed)
Parameters
| Parameter | Required | Description |
|---|---|---|
| grant_type | Yes | Must be 'client_credentials' |
| client_id | Yes | Application/service identifier |
| client_secret | Yes | Application/service secret. Can also use client_assertion (JWT) for higher security. |
| scope | No | Requested scopes for the token. Principle of least privilege. |
Examples
# Service-to-service authentication
POST https://auth.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=svc_payment
&client_secret=secret_abc123
&scope=orders:read invoices:write
# Response (no refresh_token)
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "orders:read invoices:write"
}
# Use token for API call
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
https://api.example.com/ordersWhen to use
Any machine-to-machine communication where there is no user: microservices, batch jobs, CI/CD pipelines, background workers, scheduled tasks calling external APIs.