Client Credentials Grant
The Client Credentials grant allows a confidential client to obtain an access token on its own behalf, without any user involvement. This is designed for machine-to-machine (M2M) communication where the client itself is the resource owner.
The client credentials grant does not involve a user, so no ID token or refresh token is issued. The sub claim in the access token is set to the client_id.
Request
curl -X POST https://auth.example.com/oauth2/token \
-u "my-service:my-service-secret" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "scope=read write"
Or with client_secret_post authentication:
curl -X POST https://auth.example.com/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=my-service" \
-d "client_secret=my-service-secret" \
-d "scope=read write"
Parameters
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be client_credentials |
scope | No | Space-separated list of scopes (defaults to client's allowed scopes, minus openid) |
client_id | Conditional | Required if not using HTTP Basic authentication |
client_secret | Conditional | Required if not using HTTP Basic authentication |
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "read write"
}
Note the absence of id_token and refresh_token in the response. Per RFC 6749 Section 4.4.3, a refresh token SHOULD NOT be included.
Requirements
Client type
Only confidential clients can use the client credentials grant. Public clients will receive an unauthorized_client error. This is enforced per RFC 6749 Section 4.4.2.
Client authentication
The client MUST authenticate with the authorization server. Supported authentication methods:
client_secret_basic-- client ID and secret sent via HTTP Basic authentication headerclient_secret_post-- client ID and secret sent in the request body
Grant type registration
The client must have client_credentials in its grant_types:
curl -X POST https://auth.example.com/admin/api/clients \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"client_id": "my-service",
"client_name": "My Background Service",
"client_secret": "my-service-secret",
"redirect_uris": [],
"grant_types": ["client_credentials"],
"response_types": [],
"scopes": "read write",
"client_type": "confidential",
"token_endpoint_auth_method": "client_secret_basic"
}'
Scope handling
- If no
scopeis provided, the client's configured scopes are used - The
openidscope is automatically stripped because there is no user identity to assert - If no valid scopes remain after stripping
openid, the request fails withinvalid_scope - Requested scopes are validated against the client's allowed scopes
Access token structure
The access token is a signed JWT (RS256) with the following notable claims:
| Claim | Value |
|---|---|
sub | The client_id (the client is the resource owner) |
azp | The client_id |
aud | Includes the issuer URL and any configured allowed_audiences |
scope | The granted scopes |
Limitations
- No user identity -- no
id_tokenis issued - No refresh token -- each token must be obtained independently
- No SSO session -- each grant is a standalone token issuance
- Confidential clients only -- public clients cannot use this grant