Client Credentials Flow
The OAuth2 client credentials grant is for server-to-server access with no
interactive user. A client authenticates at the token endpoint with a signed
JWT client assertion and receives an access
token directly. There is no client_secret.
All requests go to the authorization server's token endpoint
(https://auth.jlr-vcdp.com/realms/public/protocol/openid-connect/token), not the
API gateway.
What the resulting token can access is fixed by how the client was configured when its credentials were created (vehicles and categories for owners, registered vehicles for fleets) - you do not request scopes here.
It is used in two situations on this platform:
- MQTT streaming - every persona. Connecting to the real-time stream always uses a client-credentials token, supplied as the MQTT connection password. Streaming does not use DPoP.
- REST API - fleet operators. B2B fleet operators access their own registered vehicles' data via the REST API using a client-credentials token (with a DPoP proof on each request, like all REST calls).
Request a token
First build a client assertion JWT. Then call the token endpoint. The only difference between a token for REST and a token for streaming is whether you include a DPoP proof.
For the REST API (DPoP-bound)
Include a DPoP proof so the token is bound to your key pair:
curl -X POST https://auth.jlr-vcdp.com/realms/public/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "DPoP: YOUR_DPOP_PROOF_JWT" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "client_assertion=YOUR_SIGNED_ASSERTION_JWT"
For MQTT streaming (no DPoP)
No DPoP proof is needed because the stream does not use DPoP:
curl -X POST https://auth.jlr-vcdp.com/realms/public/protocol/openid-connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "client_assertion=YOUR_SIGNED_ASSERTION_JWT"
Response
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "DPoP",
"expires_in": 3600
}
Client-credentials tokens typically have no refresh token - when the token expires, request a new one with a freshly signed assertion. See Tokens & Refresh.
Using the token
- REST API: send
Authorization: DPoP <access_token>plus a fresh DPoP proof on each request. See REST API. - MQTT streaming: supply the token as the password when connecting your MQTT client. See Streaming.
Keep your private key safe
The security of this flow rests on your private key - the one whose public certificate you uploaded during credential creation. Store it in a secret manager or HSM, never commit it to source control, and never ship it in a browser or mobile client. The client credentials grant is for trusted backends only.
Next steps
- Client Assertion - Build the signed JWT this flow requires.
- Tokens & Refresh - Lifetimes and re-issuing tokens.
- Streaming - Connect an MQTT client with your token.