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.
| Parameter | Type | Description |
|---|---|---|
| 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_idandclient_secretreturn401 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_idclaim matching the authenticated caller; refresh tokens are looked up by(app_id, token, client_id). Cross-client probe attempts on access tokens emit atoken_introspection_deniedaudit event. Cache-Control: no-storeandPragma: no-cacheare 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).