SenseCrypt Docs
Integrations

Machine-to-machine

Get an access token for the SenseCrypt Management API with the OAuth 2.0 client-credentials grant — register an M2M app, bind roles, call the token endpoint, and use the token.

Use the client-credentials grant when a backend service needs to call the SenseCrypt Management API with no user present — for example to automate tenant administration or drive SCIM provisioning programmatically. The result is an access token whose capabilities come from the roles bound to the machine app.

Note: client_credentials is intentionally not listed in the discovery document's grant_types_supported (that list describes the user-facing flows). The grant is fully supported at the token endpoint — this guide documents it explicitly so you don't assume otherwise.

Prerequisites

  • A SenseCrypt tenant.
  • An M2M app created in the admin console, with one or more M2M roles bound to it.

1. Create an M2M app and bind roles

In the admin console, create an M2M app. You receive a client_id (of the form m2m_...) and a client_secret (store the secret securely — treat it like any credential). Bind the roles that grant the capabilities this service needs; the token's capabilities are the union of its bound roles, recomputed on every mint, so role changes take effect on the next token.

2. Request a token

The M2M app is tenant-scoped, so request a token from the tenant's issuer token endpoint (resolve it from discovery; conventionally /v1/idp/oidc/token). The requested audience must be the tenant's Management API identifier — the exact value {issuer}/v1/admin/ (note the trailing slash):

curl -X POST {token_endpoint} \
  -d grant_type=client_credentials \
  -d client_id={client_id} \
  -d client_secret={client_secret} \
  -d audience={issuer}/v1/admin/

Successful response:

{
  "access_token": "eyJhbGciOiJFUzI1Ni…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "oidc_apps:read auth_users:create …"
}

The ES256 access token carries:

  • sub in the form {client_id}@clients,
  • azp = your client_id, and gty = client-credentials,
  • scope and a permissions claim = the capabilities of the app's bound roles,
  • token_use: "access", a jti, and an expiry from the app's configured access-token lifetime (3600s by default).

If the audience is not the tenant's Management API identifier, the request is rejected with invalid_target ("audience must be the management API identifier"). Both audience (Auth0 spelling) and resource (RFC 8707) are accepted; if you send both, resource wins.

3. Call the Management API

Send the access token as a bearer credential to the Management API:

curl {management_api_url} \
  -H "Authorization: Bearer {access_token}"

Each capability is a fine-grained entity:verb (for example oidc_apps:read, auth_users:create). The token can only act within the one tenant it was minted for, and only up to the capabilities its roles carry.

Audience string vs. call host. The audience you request is the Management API identifier {issuer}/v1/admin/ (built from the issuer host). The admin API itself is served on your tenant's admin host, which may differ from the issuer host. Request the audience exactly as {issuer}/v1/admin/, but confirm the actual {management_api_url} admin host with your operator. See the API Reference for the admin endpoints.

End to end, the three steps chain from token mint to an authorized Management API call:

POST /v1/idp/oidc/tokengrant_type=client_credentials, client_id, client_secret,audience={issuer}/v1/admin/ Resolve M2M app in THIS tenant;verify client_secret (with rotation grace) audience must equal the management identifierelse invalid_target permissions = union of bound M2M roles' capabilities(recomputed on every mint) Sign ES256 token: sub={client_id}@clients, azp,gty=client-credentials, scope, permissions, token_use=access 200 { access_token, token_type: Bearer, expires_in, scope } {management_api_url} + Authorization: Bearer {access_token} Verify ES256 signature + aud;enforce entity:verb capability gate 200 authorized — or 403 if a required capability is missing Throttle per IP + client_id before any crypto/DB work Backend service (M2M client) Token endpoint Management API

SCIM via M2M

An M2M role can also grant the SCIM capabilities (scim:read / scim:write). A token carrying those can call the SCIM endpoints directly — an alternative to an opaque ssct_ SCIM token.

Verify it worked

  • Decode the token (e.g. on jwt.io or with your JWT library) and confirm sub is {client_id}@clients, aud is {issuer}/v1/admin/, and permissions lists the capabilities you expect from the bound roles.
  • Make one read call you're authorized for (for example listing OIDC apps if the token has oidc_apps:read) and confirm a 200.
  • Call an endpoint your roles don't cover and confirm you're denied — that proves the capability gate is enforcing, not that your token is broken.

Troubleshooting

  • invalid_target. The audience isn't exactly {issuer}/v1/admin/ (watch the trailing slash and the issuer host).
  • invalid_client. Wrong client_id/client_secret, a disabled app, or a credential from a different tenant. All of these collapse to a single opaque invalid_client (HTTP 401) by design.
  • Authorized token, but a call returns 403. The token is valid but its bound roles don't include the capability that endpoint needs. Add the role in the console and mint a fresh token (capabilities are recomputed per mint).
  • Calls 404 / connection fails. You may be calling the issuer host instead of the admin host. The audience string and the call URL can differ — see the note in step 3.

Notes

  • Client-credentials mints no refresh token and no ID token — request a fresh access token when it expires.
  • Secrets support a rotation grace window, so you can roll a secret without an outage.
  • Introspection: an M2M app may introspect its own Management-API tokens.

On this page