Skip to main content

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.

note

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​

ParameterRequiredDescription
grant_typeYesMust be client_credentials
scopeNoSpace-separated list of scopes (defaults to client's allowed scopes, minus openid)
client_idConditionalRequired if not using HTTP Basic authentication
client_secretConditionalRequired 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 header
  • client_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 scope is provided, the client's configured scopes are used
  • The openid scope is automatically stripped because there is no user identity to assert
  • If no valid scopes remain after stripping openid, the request fails with invalid_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:

ClaimValue
subThe client_id (the client is the resource owner)
azpThe client_id
audIncludes the issuer URL and any configured allowed_audiences
scopeThe granted scopes

Limitations​

  • No user identity -- no id_token is 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