# `AttestoPhoenix.Controller.RevocationController`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/controller/revocation_controller.ex#L1)

`POST /oauth/revoke` - OAuth 2.0 Token Revocation (RFC 7009).

A client presents a credential it issued and asks the authorization
server to invalidate it. This endpoint revokes the *refresh* credential:
revoking one refresh token tears down its whole family (every token
descended from the same authorization), via the configured
`Attesto.RefreshStore`. Access tokens are stateless, short-lived JWTs
with no server-side state to drop, so a hint pointing at one is honored
as a no-op success rather than an error (RFC 7009 §2.2).

## Client authentication (RFC 7009 §2.1, RFC 6749 §2.3)

The revocation endpoint requires the same client authentication as the
token endpoint. A confidential client authenticates with
`client_secret_basic` (HTTP Basic) or `client_secret_post` (form
parameters). Authentication is fail-closed: a request that names a
client but does not prove the secret is rejected `invalid_client`
(HTTP 401, RFC 6749 §5.2), and a request that names no client at all is
likewise rejected, since this endpoint serves confidential clients. The
authenticated `client_id` is then threaded into revocation so one client
cannot revoke another client's tokens (RFC 7009 §2.1).

Client lookup and secret comparison are delegated to the host through the
`:load_client` and `:verify_client_secret` configuration callbacks; this
controller owns no client registry.

## No-existence oracle (RFC 7009 §2.2)

Once the client is authenticated, the response is always `HTTP 200` with
an empty body, whether or not the presented token existed, was expired,
or was already revoked. A revocation endpoint must not let a caller probe
which tokens are live. The only non-200 outcomes are a malformed request
(`invalid_request`, missing the required `token` parameter) and failed
client authentication (`invalid_client`).

## Caching (RFC 6749 §5.1)

Every response carries `Cache-Control: no-store` and `Pragma: no-cache`,
so an intermediary never caches a revocation result.

## Configuration

Built on `AttestoPhoenix.Config`. The callbacks this controller reads:

  * `:load_client` - resolve an OAuth client by `client_id`.
  * `:verify_client_secret` - constant-time client-secret comparison.
  * `:on_event` (optional) - audit/telemetry hook; receives a
    `:token_revoked` `AttestoPhoenix.Event` after a successful revocation
    request.

The configured `AttestoPhoenix.Config` is read from
`conn.private[:attesto_phoenix_config]`, placed there by the host's
router pipeline. The `Attesto.RefreshStore` revocation runs over defaults
to the package's Ecto-backed store; a host pipeline may override it by
putting a module under `conn.private[:attesto_phoenix_refresh_store]`.

# `create`

```elixir
@spec create(Plug.Conn.t(), map()) :: Plug.Conn.t()
```

Handle `POST /oauth/revoke` (RFC 7009 §2.1).

Authenticates the client, then revokes the presented refresh token and
its family. Always responds `200` once the client is authenticated,
regardless of whether the token existed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
