RFC 7009 — OAuth 2.0 Token Revocation
Spec: datatracker.ietf.org/doc/html/rfc7009Status: Partial
RFC 7009 defines a standard endpoint that lets a client tell the authorization server "this token is no longer needed; please invalidate it." AuthHero exposes this at POST /oauth/revoke and advertises it in the discovery document as revocation_endpoint.
Endpoint
POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=<token-to-revoke>
&token_type_hint=refresh_token # optional
&client_id=<client-id>
&client_secret=<client-secret> # confidential clients onlyapplication/json request bodies are also accepted. Client credentials may be supplied either in the body (client_secret_post) or via an Authorization: Basic header (client_secret_basic).
A successful revocation returns 200 OK with no body and Cache-Control: no-store. Per RFC 7009 §2.2, the server returns 200 whether or not the token actually existed — this prevents the endpoint from being used as a token-existence oracle.
Behavior
| Scenario | Response |
|---|---|
| Token belongs to the authenticating client and is active | 200 — revoked_at is set on the refresh token; subsequent refresh_token grants for the same value return invalid_grant |
| Token does not exist | 200 — no-op |
| Token belongs to a different client | 200 — no-op (server silently refuses to act, per §2.2) |
| Token is already revoked | 200 — no-op |
client_secret is wrong | 401 invalid_client |
client_id is missing | 400 invalid_request |
token_type_hint=access_token | 200 — see below |
What is and isn't revocable
AuthHero currently tracks refresh tokens server-side; access tokens are stateless JWTs and are not stored, so they cannot be revoked individually. This is the same trade-off Auth0 makes.
token_type_hint=refresh_token(or omitted) — the token value is looked up in the refresh-token store. If found and owned by the authenticating client,revoked_atis set.token_type_hint=access_token— the request is accepted with200but no lookup is performed. To revoke a user's active access tokens you should revoke the underlying session or refresh token; short-lived JWT access tokens will then expire naturally on theirexp.
Discovery
The endpoint is advertised in /.well-known/openid-configuration:
{
"revocation_endpoint": "https://<your-domain>/oauth/revoke"
}