Skip to content

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 only

application/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

ScenarioResponse
Token belongs to the authenticating client and is active200revoked_at is set on the refresh token; subsequent refresh_token grants for the same value return invalid_grant
Token does not exist200 — no-op
Token belongs to a different client200 — no-op (server silently refuses to act, per §2.2)
Token is already revoked200 — no-op
client_secret is wrong401 invalid_client
client_id is missing400 invalid_request
token_type_hint=access_token200 — 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_at is set.
  • token_type_hint=access_token — the request is accepted with 200 but 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 their exp.

Discovery

The endpoint is advertised in /.well-known/openid-configuration:

json
{
  "revocation_endpoint": "https://<your-domain>/oauth/revoke"
}

Released under the MIT License.