TryMellon
Navigation

Token introspection (RFC 7662)

Live revocation check for tryMellon session tokens via the OAuth introspection endpoint.

Token introspection

POST /oauth/introspect implements RFC 7662. Use it to ask “is this token still valid right now” — useful for sensitive operations where catching revocation immediately matters more than the extra round-trip.

When to use introspection vs offline JWT

Offline (JWKS)Introspection
Latency~0ms~50ms
Catches revocation❌ until exp✅ immediately
Catches expiry
Use forEvery authenticated readPayments, role grants, admin actions

Combine: validate offline on every request and introspect on the operations you can’t afford to be wrong about. Subscribe to session.revoked webhooks to invalidate your session cache proactively (see Handling revocation).

Endpoint

POST https://api.trymellonauth.com/oauth/introspect
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

token=$JWT&token_type_hint=access_token

Response (active):

{
  "active": true,
  "sub": "user_abc",
  "aud": "client_xyz",
  "iss": "https://api.trymellonauth.com",
  "iat": 1717000000,
  "exp": 1717003600,
  "client_id": "client_xyz",
  "scope": "openid"
}

Response (inactive — revoked, expired, malformed, or unknown):

{ "active": false }

Per RFC 7662, active: false is the only signal you should rely on for inactive tokens — no further fields are guaranteed.

cURL

curl -X POST https://api.trymellonauth.com/oauth/introspect \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "token=$TOKEN" \
  -d "token_type_hint=access_token"

Node.js helper

const credentials = Buffer.from(
  `${process.env.TRYMELLON_CLIENT_ID}:${process.env.TRYMELLON_CLIENT_SECRET}`,
).toString('base64');

export async function isActive(token: string): Promise<boolean> {
  const res = await fetch('https://api.trymellonauth.com/oauth/introspect', {
    method: 'POST',
    headers: {
      Authorization: `Basic ${credentials}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({ token, token_type_hint: 'access_token' }),
  });
  if (!res.ok) return false;
  const body = await res.json() as { active: boolean };
  return body.active;
}

Cost considerations

Introspection is a synchronous network call. At high throughput, calling it on every request adds latency and pressure on the API. Recommended pattern:

  1. Verify offline on every request (fast).
  2. Maintain a short-lived deny list in your cache (Redis SET with TTL = remaining token lifetime).
  3. Add to deny list when session.revoked webhook arrives.
  4. Optionally introspect on sensitive operations as a belt-and-suspenders check.

Troubleshooting

ErrorCauseFix
401 UnauthorizedWrong Basic Auth credentials.Confirm client_id:client_secret, base64-encoded, in Authorization: Basic ….
active: false immediatelyToken was issued for a different application.Confirm the introspecting app’s client_id matches the token’s aud.
INTROSPECTION_FAILED (SDK)Endpoint returned 5xx or network error.Treat as inactive; retry with backoff.