Client Credentials Grant
Use the client credentials grant for machine-to-machine flows where no user is
involved — whichever party is authenticating proves its own identity directly
and gets a token for itself, with no resource owner in the picture. RFC 6749's
"client" is a generic role, not a synonym for O2ID's Application resource —
several different principals can authenticate this way; see
Getting a Token as an Unattended Agent
for how an AI agent uses this same grant with a signed assertion instead of a
client secret, and Getting a Token for a Resource Server
for how to scope the resulting token to a specific downstream API rather than
O2ID's own management API. Only confidential applications (those registered
with a clientSecret) can use the application variant below.
Token endpoint: POST /oauth2/token
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be client_credentials |
client_id | Yes | The application's client ID |
client_secret | Yes | The application's client secret |
scope | No | Space-delimited list of requested scopes |
# Create a confidential application
APP_JSON=$(curl -s -X POST http://localhost:8080/applications \
-H "Content-Type: application/json" \
-d '{"name":"Service App","clientSecret":"my-secret","callbackUrls":["https://app.example.com/callback"]}')
CLIENT_ID=$(printf '%s' "$APP_JSON" | jq -r .clientId)
# Request a token
curl -s -X POST http://localhost:8080/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=${CLIENT_ID}" \
--data-urlencode "client_secret=my-secret" \
--data-urlencode "scope=read write"
The response includes access_token and token_type but no refresh_token
(RFC 6749 §4.4.3). The sub claim in the token is the client's own client ID.
Public clients (registered without a clientSecret) receive
{"error":"unauthorized_client"}. The granted scope is clamped to the
application's own allowedScopes (see
Managing Applications) — there is
no human role scope involved in this path at all, since client_credentials
has no concept of a resource owner.