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

Introspection

The introspection endpoint returns metadata about an access or refresh token per RFC 7662. Resource servers use it to validate tokens server-side without pulling the JWKS.

POST/oauth2/introspectNo Auth

Returns metadata about an access or refresh token (RFC 7662). Requires client authentication. Returns `{ active: false }` for any token that fails verification — never leaks the reason.

ParameterTypeDescription
token*
body
string
token_type_hint
body
"access_token" | "refresh_token"
client_id*
body
string
client_secret*
body
string
Response
{
  "active": true,
  "scope": "api:read api:write",
  "client_id": "svc-orders",
  "token_type": "Bearer",
  "exp": 1735689600,
  "iat": 1735686000,
  "sub": "svc-orders",
  "aud": "svc-orders",
  "iss": "https://your-app.aero2.dev"
}

Behavior

  • Client authentication is required. Calls without valid client_id and client_secret return 401 invalid_client.
  • { active: false } for any failure. Expired tokens, revoked refresh tokens, malformed JWTs, wrong-issuer tokens, unknown refresh tokens, and tokens belonging to a different client all return the same { active: false } payload — never the reason. This matches RFC 7662 and prevents token probing.
  • Cross-tenant tokens are rejected. A token issued under one app's issuer cannot be introspected under a different app's hostname (issuer check inside JWT verification).
  • Tokens are scoped to the issuing client. Only the client the token was issued to can introspect it. Access tokens are gated on the JWT's client_id claim matching the authenticated caller; refresh tokens are looked up by (app_id, token, client_id). Cross-client probe attempts on access tokens emit a token_introspection_denied audit event.
  • Cache-Control: no-store and Pragma: no-cache are set on every response per RFC 7662 §4 — intermediaries must not cache token state.

Example

curl -X POST https://your-app.aero2.dev/oauth2/introspect \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=eyJhbGciOiJSUzI1NiIs..." \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Active access token response:

{
  "active": true,
  "scope": "api:read api:write",
  "client_id": "svc-orders",
  "token_type": "Bearer",
  "exp": 1735689600,
  "iat": 1735686000,
  "sub": "svc-orders",
  "aud": "svc-orders",
  "iss": "https://your-app.aero2.dev"
}

For tokens issued via the authorization code flow, username (the user's email) is also included.

Refresh-token responses omit token_type (RFC 6749 §1.4 only defines Bearer/MAC for access tokens).