Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Token

The token endpoint exchanges credentials for access tokens. It supports three grant types: authorization_code, refresh_token, and client_credentials.

POST/oauth2/tokenNo Auth

Exchanges credentials for access tokens. Supports authorization_code (with PKCE), refresh_token, and client_credentials (M2M, RFC 6749 §4.4) grant types.

ParameterTypeDescription
grant_type*
body
"authorization_code" | "refresh_token" | "client_credentials"
code
body
stringAuthorization code (required for authorization_code grant)
redirect_uri
body
stringMust match the original request (required for authorization_code grant)
client_id
body
string
client_secret
body
string
code_verifier
body
stringPKCE verifier (required for authorization_code grant)
refresh_token
body
stringRefresh token (required for refresh_token grant)
scope
body
stringSpace-separated scopes (optional, client_credentials grant). Must be a subset of the client's registered scopes. If omitted, all registered scopes are issued.
Response
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g...",
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "scope": "openid profile email"
}

Client Credentials (M2M)

The client_credentials grant (RFC 6749 §4.4) is for service-to-service authentication where there is no user. The client must be registered with client_credentials in its grant_types; otherwise the response is unauthorized_client (400).

curl -X POST https://your-app.aero2.dev/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=api:read api:write"

The scope parameter is optional. If omitted, the response includes all scopes registered for the client. Each requested scope must be a subset of the client's registered scopes; otherwise the response is invalid_scope (400). No refresh token is issued — the client re-authenticates when the access token expires.

Issued token claims

{
  "sub": "YOUR_CLIENT_ID",
  "aud": "YOUR_CLIENT_ID",
  "iss": "https://your-app.aero2.dev",
  "scope": "api:read api:write",
  "client_id": "YOUR_CLIENT_ID",
  "gty": "client_credentials",
  "token_use": "access",
  "iat": 1735686000,
  "exp": 1735689600
}

The gty: "client_credentials" claim (a non-standard convention popularized by Auth0/Okta) lets resource servers branch cleanly between user-bearing tokens and M2M tokens without inspecting the shape of sub.

Token Lifetimes

TokenLifetime
Access token1 hour
ID token1 hour
Refresh token30 days
Session token1 hour