Skip to content

Issue a client credentials token

Add client_credentials to the server’s grant types in config/sesame.ts:

grantTypes: ['authorization_code', 'refresh_token', 'client_credentials'],
clientCredentialsAccessTokenTtl: '2h',

Keep the other required configuration fields. Declare the API scopes the service needs, such as read.

Create a confidential client with an owner user ID and the grant enabled:

import sesame from '@julr/sesame/services/main'
const { client, clientSecret } = await sesame.createClient({
name: 'Reporting service',
redirectUris: [],
userId: '42',
grantTypes: ['client_credentials'],
scopes: ['read'],
})

Use an existing service account ID in place of 42. The OAuth guard resolves that user when the token calls the API. Save the generated client ID and secret.

Set the credentials as shell variables and make the request:

Terminal window
curl https://auth.example.com/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
--data-urlencode grant_type=client_credentials \
--data-urlencode scope=read

To bind the token to one API, add --data-urlencode resource=https://auth.example.com/api/mcp after registering that resource.

The response contains an access token, type, lifetime, and scopes. It has no refresh token or ID token. Request another token after expiry.

If you omit scope, Sésame uses the client’s non-built-in, non-OIDC scopes. Do not request openid, profile, email, or offline_access with this grant.