Skip to main content

Token Introspection

Token introspection lets a resource server (an API that receives tokens) ask LumoAuth whether a token is still valid and retrieve metadata about it (client, user, scopes, expiration). Use it when you hold an opaque token, or when you need to know about revocation before the token expires. Terms are explained in the glossary.

POST /orgs/{orgId}/api/v1/oauth/introspect

When to Use Introspection​

ScenarioRecommendation
Opaque tokensRequired — opaque tokens can only be validated via introspection
JWT tokens (normal validation)Not needed — validate the signature and expiration locally against the JWKS for better performance
JWT tokens (check for revocation)Recommended for sensitive operations — local JWT validation cannot detect revocation
Getting token metadataUse introspection when you need current scopes, user ID, or other claims

Authentication​

Call this endpoint from your backend using your client credentials (HTTP Basic or client_id/client_secret in the form body; discovery lists these as introspection_endpoint_auth_methods_supported). Never call it from client-side code. The calling client must be active and belong to the organization in the URL; a disabled client, a client from another organization, or a wrong secret all receive the same 401 invalid_client.

Only the client the token was issued to (or one named in its audience) can introspect it; other callers get {"active": false}.

Request​

ParameterRequiredDescription
tokenYesThe token to validate
token_type_hintNoaccess_token or refresh_token — speeds up lookup

Example Request​

curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/oauth/introspect \
-u "CLIENT_ID:CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9..." \
-d "token_type_hint=access_token"

Response​

Active Token​

{
"active": true,
"scope": "openid profile email",
"client_id": "abc123def456",
"username": "john@example.com",
"token_type": "Bearer",
"exp": 1704067200,
"iat": 1704063600,
"sub": "12345",
"aud": "api.example.com",
"iss": "https://app.lumoauth.dev/orgs/acme-corp/api/v1"
}

iss is byte-identical to the discovery issuer of the entry point you called (https://auth.acme.com on a verified custom domain). If you compare it, compare it with that value.

Inactive / Invalid Token​

If the token is expired, revoked, malformed, or does not exist, the response is simply the one below. The same applies to a still-unexpired user token whose user has since been blocked, deactivated or deleted, left the organization, signed out everywhere or had their password changed or reset, and to a token issued in another organization (including one for a CIMD client that uses the same client_id URL there). See Early invalidation.

{
"active": false
}

The server intentionally returns the same response for all invalid-token cases, so an attacker cannot distinguish between "unknown token" and "revoked token".

Response Fields​

FieldTypeDescription
activebooleanAlways present. Whether the token is currently valid.
scopestringSpace-separated list of granted scopes
client_idstringWhich client the token was issued to
usernamestringThe user's email, for user-bound tokens
token_typestringBearer for access tokens, refresh_token for refresh tokens
substringSubject — user ID or agent ID
expintegerExpiration timestamp (Unix epoch seconds)
iatintegerIssued-at timestamp
issstringIssuer, equal to the discovery issuer
audstring or arrayIntended audience
nbfintegerNot-before timestamp, when the token carries one
Standard

Token introspection is defined so resource servers that cannot (or should not) verify tokens locally can ask the authorization server. To comply, authenticate every introspection call, treat active: false as the only signal you need, and never expose the endpoint to browsers.

Troubleshooting​

  • 401 invalid_client although the secret is right. The client was disabled in the portal, or you are calling another organization's endpoint.
  • 429 too_many_requests. The endpoint allows 100 calls per minute per client; cache results for the token's remaining lifetime instead of introspecting on every request.
  • active: false for a token you just issued. You are introspecting with a client other than the one the token was issued to (and it is not in the token's aud).
  • active: false for a token that has not expired. The user was blocked, deactivated or deleted, signed out everywhere, or changed or reset their password after the token was issued. The user must sign in again.
  • iss does not equal what your resource server expects. Read the issuer from discovery; it is .../orgs/{orgId}/api/v1 on the organization path and the bare custom domain on a custom domain.

Standards used on this page​

Plain nameStandard
Token introspectionRFC 7662
JSON Web Key (JWK) and key sets (JWKS)RFC 7517

See the standards reference for what each one covers and how LumoAuth uses it.