Introspect
This page helps your API ask the auth server whether an access token is still active, with the introspection endpoint, /auth/introspect.
It’s the RFC 7662 token introspection endpoint, the introspection_endpoint in the discovery document. A signature check tells your API a token was issued and hasn’t expired. Introspection also tells it whether the token was cut off since: its session ended, its grant was revoked, its user or its client was disabled.
Introspect a token
Section titled “Introspect a token”-
Give your API a confidential client, one with a client secret, listed for its resource, as Protect an API shows. See Introspection resources.
-
From your API, send the access token it received, with the client’s credentials:
POST /auth/introspect HTTP/1.1Host: auth.example.comContent-Type: application/x-www-form-urlencodedAuthorization: Basic cHJvZHVjdC1hcGktaW50cm9zcGVjdG9yOm15LXNlY3JldA==token=eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiIsInR5cCI6ImF0K2p3dCJ9... -
Read whether it’s active:
{"active": true,"scope": "openid product-api:read","client_id": "my-web-app","sub": "8a1f7c2e-5b3d-4e6f-9a0b-1c2d3e4f5a6b","aud": ["product-api", "authserver"],"iss": "https://auth.example.com","exp": 1760000300,"iat": 1760000000,"nbf": 1760000000,"jti": "9a6f1c1e-2d0b-4f3e-8a55-0c1b7a3e2f44","token_type": "Bearer"}Anything else is answered with
activealone, and your API refuses the request:{"active": false}
Still check scope for the permission the operation needs, as Protect an API describes.
A DPoP-bound token
Section titled “A DPoP-bound token”An access token bound to a key with DPoP is answered with token_type DPoP, and its cnf, whose jkt is the thumbprint of the key:
{ "active": true, "scope": "product-api:read", "client_id": "my-spa", "sub": "8a1f7c2e-5b3d-4e6f-9a0b-1c2d3e4f5a6b", "aud": "product-api", "iss": "https://auth.example.com", "exp": 1760000300, "iat": 1760000000, "nbf": 1760000000, "jti": "9a6f1c1e-2d0b-4f3e-8a55-0c1b7a3e2f44", "token_type": "DPoP", "cnf": { "jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" }}Introspection doesn’t check the proof: your API does, as Accept DPoP-bound tokens describes. Send no proof to /auth/introspect: the endpoint ignores a DPoP header.
When a token is active
Section titled “When a token is active”An access token is active while it’s live and its aud names a resource on the calling client’s introspection resources. Live means it hasn’t expired, its iss is the current issuer, its user and its client exist and are enabled, and nothing has ended it since, such as its session ending, its user’s credentials changing, or it or its grant being revoked. The auth server’s own /userinfo and APIs make the same checks. See Ending sessions.
A refresh token is active only to the client it was issued to, while it’s live and the flow that issued it is still enabled for that client. Live means nothing has ended it: it hasn’t expired or been rotated or revoked, its user is enabled, and neither its session ending nor its user’s credentials changing has ended it. So your app’s backend can find out whether its refresh token is still good without using it. Introspecting it spends nothing, and the refresh token keeps working.
Active doesn’t promise the next refresh succeeds. A refresh checks the user’s consent and permissions, and your client’s scope allowances, when it’s made, so a refresh with an active token can still be refused with invalid_grant.
Every other token gets {"active": false}: one that’s expired, revoked, forged or malformed, an ID token, an access token for a resource the client doesn’t list, and another client’s refresh token. The answer never says which, as RFC 7662 section 2.2 recommends.
What an active answer carries
Section titled “What an active answer carries”The members are read from the token, and they’re the same for every client that may see it. A user’s profile claims aren’t among them: call /userinfo for those.
| Member | Access token | Refresh token |
|---|---|---|
active |
true |
true |
scope |
The token’s scope | The token’s scope |
client_id |
The client the token was issued to | Your client |
sub |
The user, or for a client credentials token, the client | The user |
aud |
The token’s aud, a string or an array |
Not sent |
iss |
The issuer | The issuer |
exp, iat |
When the token expires and was issued, in seconds since the epoch | The same |
nbf |
When the token became usable | Not sent |
jti |
The token’s identifier | The token’s identifier |
token_type |
DPoP for a DPoP-bound token, Bearer otherwise |
Not sent |
cnf |
For a DPoP-bound token only: jkt, the thumbprint of its key |
Not sent |
The request
Section titled “The request”/auth/introspect takes a POST, with the parameters in an application/x-www-form-urlencoded body. Each parameter goes in once.
| Parameter | Required | What it does |
|---|---|---|
token |
Yes | The access token or refresh token to ask about |
token_type_hint |
No | Ignored. The auth server reads the token’s type from the token |
Client authentication
Section titled “Client authentication”The calling client authenticates exactly as at the token endpoint, with client_secret_basic or client_secret_post. See Client authentication for the rules and the refusals, which are the same here.
A public client can’t introspect, since it has no secret to authenticate with.
The auth server’s log has a client authentication refused at the introspection endpoint warning for each refusal. See Logs.
The answer
Section titled “The answer”| Status | When |
|---|---|
200 OK |
The token is active, or {"active": false} |
400 Bad Request, invalid_request |
token or client_id is missing, a parameter or the Authorization header is repeated, the body can’t be read, or the secret is sent both ways |
401 Unauthorized, invalid_client |
The client couldn’t be authenticated, or it’s a public client |
500 Internal Server Error, server_error |
The auth server couldn’t check the token. That says nothing about the token: try again, or refuse the request |
Every answer carries Cache-Control: no-store. If your API caches answers itself, keep each one no longer than you can accept a revoked token being honored, and never past the token’s exp.
An error is a JSON body with error and error_description, as on the token endpoint.