Skip to main content

OAuth2 Client Credentials

The OAuth2 Client Credentials grant is a machine-to-machine authentication flow standardized by RFC 6749. Instead of a long-lived API key, a client exchanges a client_id and client_secret for a short-lived access token, then uses that token to call Vengtoo APIs.

Use this flow when:

  • A backend service, worker, or cron job needs to call Vengtoo on its own behalf (no end user involved).
  • You manage Vengtoo configuration with Terraform and apply it through a CI/CD pipeline.
  • You are exposing an MCP server to AI agents and want each agent instance to authenticate with its own credentials.

Vengtoo handles authorization — it does not authenticate end users. Bring your own identity provider (Auth0, Clerk, Cognito, your own login) and pass the authenticated user's identity as the subject when calling the evaluation endpoint.

When to use OAuth2 vs API keys

Use caseRecommended
Long-lived backend integrationAPI key (simpler)
Short-lived service-to-serviceOAuth2 CC (tokens expire, safer)
Third-party integration with scoped accessOAuth2 CC (scopes)
Compliance / rotating credentialsOAuth2 CC (tokens auto-expire)

Step 1 — Create an OAuth client

  1. Sign in to the Vengtoo Console.
  2. Go to Settings → API Access and open the OAuth Clients tab.
  3. Click Create Client and fill in:
    • Name — a human-readable label (for example, ci-deploy-bot).
    • Description — optional context about what the client is for.
    • Scopes — any combination of read, write, admin.
  4. Click Save.

You will be shown a client_id and a client_secret of the form:

client_id: client_a3f8b2c1...
client_secret: azx_cs_9f3a8b4c2d1e...

Copy the client_secret immediately. For security reasons it is shown only once. If you lose it, regenerate a new secret from the same client entry in the Console.

Step 2 — Request an access token

Exchange the client credentials for an access token at the token endpoint:

curl -X POST https://api.vengtoo.com/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=client_a3f8b2c1..." \
-d "client_secret=azx_cs_9f3a8b4c2d1e..."

A successful response looks like:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
  • access_token — the bearer token to send with subsequent API calls.
  • token_type — always Bearer.
  • expires_in — lifetime in seconds. Re-request a token before or after expiry.
  • scope — the scopes granted to this token.

Step 3 — Use the token

Pass the access token in the Authorization header on every request:

curl -X POST https://api.vengtoo.com/access/v1/evaluation \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6..." \
-H "Content-Type: application/json" \
-d '{
"subject": { "external_id": "alice-uuid-from-your-db", "type": "user" },
"resource": { "external_id": "doc-uuid-from-your-db", "type": "document" },
"action": { "name": "read" }
}'

The same token works against the Management API, the Authorize API, and any other Vengtoo endpoint — subject to the scopes on the token.

Scopes

ScopeGrants
readRead-only endpoints (GET: list, detail, authorize checks)
writeEverything in read, plus POST, PUT, PATCH, DELETE (CRUD)
adminAll methods — short-circuits scope checks entirely

Scopes are cumulative: admin implies write, and write implies read. Request only the minimum scopes your integration needs.

Token lifetime

  • Default: 1 hour (expires_in: 3600).
  • Configurable per client in the Console.

Shorter lifetimes reduce the blast radius if a token leaks; longer lifetimes reduce token-endpoint traffic. One hour is a reasonable default for most backend integrations.

Refreshing tokens

The Client Credentials flow does not use refresh tokens. This is intentional and matches the OAuth2 specification — since the client already holds its own credentials, it can simply request a new token whenever one expires.

Typical pattern:

  1. Request a token.
  2. Cache it in memory until roughly 60 seconds before expires_in.
  3. On expiry (or on a 401 response), request a new token with the same client_id / client_secret.

Security best practices

  • Store client_secret in a secrets manager (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, Kubernetes Secrets) — never in source code or unencrypted config files.
  • Rotate secrets periodically. From the Console, open the client and click Regenerate secret. Deploy the new secret, then revoke the old one.
  • Use separate clients per environment (development, staging, production) so a leaked dev secret cannot reach production data.
  • Limit scopes to the minimum required. Prefer read over write, and write over admin.
  • Monitor token issuance in the Audit Log (filter by OAuth client) to detect unusual issuance patterns.
  • Do not log tokens. Strip Authorization headers from request logs.

Error responses

Errors follow the OAuth2 standard error format:

{ "error": "invalid_client" }
StatuserrorMeaning
400invalid_requestMissing grant_type, client_id, or client_secret, or the body is not form-encoded
400unsupported_grant_typeOnly client_credentials is supported at this endpoint
401invalid_clientclient_id or client_secret is wrong, or the client has been revoked
500server_errorInternal error (e.g. signing key unavailable)