AttestoPhoenix.Controller.RegistrationController (AttestoPhoenix v0.6.1)

Copy Markdown View Source

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.

Summary

Functions

Dynamic client registration action (RFC 7591 §3.1).

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

Functions

create(conn, params)

@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(conn, map)

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

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

The OpenID conformance suite uses this as cleanup for dynamically registered clients. A host must wire both :client_registration_access_token_hash and :unregister_client; absent either callback, the endpoint fails closed.