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

OAuth 2.0 Dynamic Client Registration endpoint (RFC 7591 §3).

Handles `POST /oauth/register`. This module owns the HTTP and protocol-framing
concerns only: it parses the RFC 7591 §2 client-metadata document, validates
the requested metadata against the server's advertised policy, mints the
client's credentials through the `Attesto` core, hands the validated,
issuance-ready attributes to the host's persistence callback, and renders the
RFC 7591 §3.2.1 client information response or the RFC 7591 §3.2.2 error body.
It carries no business-domain logic; the client registry is owned entirely by
the host through the `:register_client` callback resolved from
`AttestoPhoenix.Config`.

## Disabled by default

Dynamic registration is an open door: a successful request mints a new client
from an otherwise unauthenticated POST. The library therefore mounts this
endpoint only when the host opts in (`AttestoPhoenix.Router`'s
`:registration` option) AND supplies a `:register_client` callback
(`AttestoPhoenix.Config` raises at boot otherwise). Any admission control the
host wants in front of registration - a registration access token
(RFC 7591 §3), an allowlist, rate limiting - lives in the host pipeline ahead
of this action; the library does not assume one.

## Wire contract

`POST /oauth/register` with `application/json`: the request body is a JSON
client-metadata document (RFC 7591 §3.1). Any other Content-Type is rejected
as `invalid_client_metadata` rather than parsed through an unintended path. A
metadata document carries nested arrays (`redirect_uris`, `grant_types`) that
have no canonical form-encoded representation, so no form encoding is offered
here.

Recognised metadata members (RFC 7591 §2) include `redirect_uris`,
`grant_types`, `token_endpoint_auth_method`, and a space-delimited `scope`
string. The request is validated member by member against the server's policy
inputs - the scope catalog (`AttestoPhoenix.Config`'s `:scopes_supported`),
the supported grant types, and the supported token-endpoint auth methods -
and the first failure is returned.

## Issued credentials

This controller owns credential generation: it mints the `client_id` and (for
a confidential client, i.e. any `token_endpoint_auth_method` other than
`none`) a high-entropy `client_secret` via `Attesto.Secret` (RFC 6749 §2.3.1
high-entropy secret). The plaintext secret appears in the RFC 7591 §3.2.1
response exactly once, accompanied by the REQUIRED `client_secret_expires_at`
(`0`, non-expiring); only its one-way hash is handed to the host for
persistence, so a leaked client store yields no usable secret.

## Responses

Success renders HTTP 201 with the RFC 7591 §3.2.1 client information response
(the registered metadata plus the synthesised `client_id`, the optional
`client_secret` with its REQUIRED `client_secret_expires_at`, and
`client_id_issued_at`). Failure renders the RFC 7591
§3.2.2 error body (`{"error": code, "error_description": ...}`) with the
RFC 7591 §3.2.2 codes `invalid_redirect_uri` and `invalid_client_metadata`.
A host store rejection surfaces as `invalid_client_metadata` (the request
named a client the store would not accept) rather than a 500. Both success
and error responses carry no-store cache headers (RFC 7234 §5.2) because the
body can carry a freshly minted credential.

## Event

A successful registration emits a `:client_registered` event (RFC 7591)
through `AttestoPhoenix.Event` carrying the issued `client_id`. The plaintext
secret is never placed on the event.

# `create`

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

Dynamic client registration action (RFC 7591 §3.1).

Validates the client-metadata document, mints the client's credentials,
persists via the host callback, and renders either the RFC 7591 §3.2.1
client information response or an RFC 7591 §3.2.2 error. Every response
carries no-store cache headers (RFC 7234 §5.2).

# `delete`

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

Dynamic client registration management delete action (RFC 7592 §2).

Deletes a previously registered client at its client configuration endpoint
(RFC 7592 §2.3). A host must wire both
`:client_registration_access_token_hash` and `:unregister_client`; absent
either callback, the endpoint fails closed.

---

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