Token Introspection (Beta)
The beta_introspection block configures OAuth 2.0 Token Introspection (RFC 7662) for a jwt block. It allows Couper to verify token validity with an authorization server in addition to local JWT signature validation.
| Block name | Context | Label |
|---|---|---|
beta_introspection | JWT Block | no label |
When to Use Token Introspection
Standard JWT validation only checks the token’s signature and claims (expiry, audience, etc.) locally. This is fast but has a limitation: tokens cannot be revoked before they expire.
Use token introspection when you need to:
- Detect revoked tokens - The authorization server can mark tokens as inactive (e.g., after user logout, password change, or security incident)
- Enforce real-time access policies - Check current token status rather than relying solely on embedded claims
- Support token revocation requirements - Compliance scenarios that require immediate token invalidation
How It Works
- Couper first validates the JWT signature and claims locally
- If valid, Couper calls the authorization server’s introspection endpoint
- The server responds with
{"active": true}or{"active": false} - Access is granted only if both validations pass
Caching
To reduce load on the authorization server, introspection responses are cached based on the ttl attribute:
- Positive TTL (e.g.,
"60s"): Responses are cached; the same token won’t trigger another introspection call until the TTL expires - Zero or negative TTL (e.g.,
"0s"): No caching; every request calls the introspection endpoint
Choose a TTL that balances security (shorter = faster revocation detection) against performance (longer = fewer introspection calls).
Example
definitions {
jwt "my_jwt" {
signature_algorithm = "RS256"
key_file = "pub_key.pem"
bearer = true
beta_introspection {
endpoint = "https://authorization-server.example/introspect"
client_id = "my_resource_server"
client_secret = env.CLIENT_SECRET
ttl = "60s"
}
}
}
Error Handling
When a token is valid but marked inactive by the introspection endpoint, Couper returns a jwt_token_inactive error. You can handle this with an error_handler:
jwt "my_jwt" {
# ... jwt configuration ...
beta_introspection {
# ... introspection configuration ...
}
error_handler "jwt_token_inactive" {
response {
status = 401
json_body = { error = "token_revoked", error_description = "This token has been revoked" }
}
}
}
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
backend | string | - | References a backend in definitions for introspection requests. Mutually exclusive with backend block. |
client_id | string | - | The client identifier. |
client_secret | string | - | The client password. Required unless the endpoint_auth_method is "private_key_jwt". |
endpoint | string | - | The authorization server’s introspection_endpoint. |
endpoint_auth_method | string | "client_secret_basic" | Defines the method to authenticate the client at the introspection endpoint. If set to "client_secret_post", the client credentials are transported in the request body. If set to "client_secret_basic", the client credentials are transported via Basic Authentication. If set to "client_secret_jwt", the client is authenticated via a JWT signed with the client_secret. If set to "private_key_jwt", the client is authenticated via a JWT signed with its private key (see jwt_signing_profile block). |
ttl | duration | - | The time-to-live of a cached introspection response. With a non-positive value the introspection endpoint is called each time a token is validated. |
Duration
Example: timeout = "300s"
| Duration units | Description |
|---|---|
ns | nanoseconds |
us (or µs) | microseconds |
ms | milliseconds |
s | seconds |
m | minutes |
h | hours |
Nested Blocks
| Name | Description |
|---|---|
backend | Configures a backend for introspection requests (zero or one). Mutually exclusive with backend attribute. |
jwt_signing_profile | Configures a JWT signing profile to create a client assertion if endpoint_auth_method is either "client_secret_jwt" or "private_key_jwt". |