Pushed Authorization Request storage (RFC 9126), as conn-free core.
This is the single place that turns an authenticated client and a parsed
authorization request into either a stored request_uri reference or an
AttestoPhoenix.OAuthError. The Pushed Authorization Request endpoint
(RFC 9126) authenticates the client (RFC 6749 §2.3), stores the submitted
authorization request parameters behind a request_uri, and returns that
reference to be used at the authorization endpoint, which still performs the
normal client/redirect/scope/PKCE validation when the reference is resolved.
North star
AttestoPhoenix.Controller.PARController parses the request off the
Plug.Conn, authenticates the client via AttestoPhoenix.ClientAuthentication
(RFC 6749 §2.3), lifts the DPoP facts into a %Request{} of plain data, and
calls store/2. This module reads only data, never touches a conn, and never
emits an event. Policy is carried on the %AttestoPhoenix.Config{} the caller
passes in (the :par_store persistence, the :par_ttl, the host callbacks);
nothing is hardcoded here.
Return value
{:ok, %{request_uri: request_uri, expires_in: ttl}} on success, where
request_uri is a freshly generated urn:ietf:params:oauth:request_uri:
reference (RFC 9126 §2.2) and ttl is the configured lifetime in seconds.
{:error, %AttestoPhoenix.OAuthError{}} on failure.
Security details preserved
- The stored record carries the authenticated
client_idresolved through the host's:client_idcallback (RFC 6749 §2.2), overriding any body-supplied value. When no:client_idcallback is configured the request's own presentedclient_idis left intact (not clobbered withnil); the library makes no assumption about the opaque client shape. The client-authentication credentials (client_secret,client_assertion,client_assertion_type) are dropped before storage. - RFC 9449: when a
DPoPproof is presented at the PAR endpoint, it is verified against the canonical request URL/method (RFC 9449 §4.2 / §4.3) with the configured replay check, and itsjktis stored as thedpop_jktthe authorization code will later be sender-constrained to. A submitteddpop_jktrequest parameter that disagrees with the verified proof's thumbprint is rejected (invalid_dpop_proof). Presenting more than oneDPoPproof is rejected (RFC 9449 §4.1). Adpop_jktparameter submitted without a proof is honoured as-is, since the proof of possession is demonstrated later at the token endpoint.
Summary
Types
The conn-free DPoP facts the controller lifts off the PAR request (RFC 9449 §4.1 / §4.2 / §4.3).
Functions
Store a pushed authorization request, returning the request_uri reference
and its lifetime, or an error.
Types
@type dpop_input() :: %{ optional(:proofs) => [String.t()], optional(:http_uri) => String.t() | nil, optional(:http_method) => String.t() | nil }
The conn-free DPoP facts the controller lifts off the PAR request (RFC 9449 §4.1 / §4.2 / §4.3).
:proofs- theDPoPrequest-header values (Plug.Conn.get_req_header(conn, "dpop"));[]when no proof was presented, more than one entry being a rejected ambiguous request.:http_uri- the canonical request URL (htu) the proof is bound to.:http_method- the HTTP method (htm) the proof is bound to.
Functions
@spec store( AttestoPhoenix.Config.t(), AttestoPhoenix.AuthorizationServer.PAR.Request.t() ) :: {:ok, %{request_uri: String.t(), expires_in: pos_integer()}} | {:error, AttestoPhoenix.OAuthError.t()}
Store a pushed authorization request, returning the request_uri reference
and its lifetime, or an error.
config is the validated %AttestoPhoenix.Config{} carrying the :par_store
persistence, the :par_ttl, and the host callbacks; request is the
AttestoPhoenix.AuthorizationServer.PAR.Request the controller built from the
authenticated client, the request body, and the conn-free DPoP facts. See the
module docs for the return shape and the security details preserved.