Requiring and validating pushed requests
A pushed authorization request (RFC 9126) sends the authorize request's parameters to the server over the back channel,
authenticated, and hands the browser a short-lived reference in their place. The exchange is recorded in
pushed authorization requests; this page is about the server's rules for it. The
code is included from
samples/Interaction.Events,
whose test pushes requests the server accepts and refuses, and sends one through the front channel that it refuses.
// Every client must push its authorization requests (RFC 9126); discovery says so.
options.PushedAuthorization.Required = true;
// A confidential client's pushes may name a redirect URI it never registered (RFC 9126 §2.4).
options.PushedAuthorization.AllowUnregisteredPushedRedirectUris = true;
Requiring pushing of every client
Required is the server-wide form of
Client.RequirePushedAuthorization — what an installation with no legacy clients left should set. It is read in two
places: the authorize endpoint refuses any request that arrives without a reference, and the discovery document
advertises require_pushed_authorization_requests so a client learns the rule before it sends anything. RFC 9126 §5
makes that a property of the server, so it is advertised only for the server-wide flag; a per-client requirement is not
published, because doing so would misinform every other client. Each refusal raises PushedAuthorizationFailureEvent
(events).
What the refusal says, since 0.96.0, is Duende's. A request without a reference — or with a
request_urithat is not this server's PAR URN, such as a request object URL — is refused withinvalid_request, "Pushed authorization is required.", as Duende 8.0.1'sRequestObjectValidatorrefuses it. It wasinvalid_request_uriuntil then, which describes a reference the request did not carry; a reference that is malformed, unknown, expired, redeemed or another client's is stillinvalid_request_uri. And the rule is asked only of a client that resolves to an enabled one, as Duende loads the client first: a request naming no client isinvalid_request"Missing client_id.", and an unknown or disabled clientunauthorized_client, whatever the server requires of the clients it can serve.
Required = truewithEndpoints.EnablePushedAuthorizationEndpoint = falserefuses to start, as of 0.54.0. The two settings are individually sensible and together describe a server that can authorize nobody: every request is refused for carrying no reference, and there is no endpoint at which a reference can be obtained. It is a boot failure rather than a runtime surprise because nothing can report the state —require_pushed_authorization_requestsis published inside the branch that publishes the endpoint, so with the endpoint off a client is told neither that pushing is required nor where to push, and every refusal that follows looks like its own bug.
A push is validated when it is pushed
New in 0.122.0, as Duende 8.0.1's pushed authorization validator does it: a push is validated as the authorize
request it will become — the redirect URI, the response type, the scopes, PKCE, resource indicators, a request object —
before it is stored, and your authorize request hook is
asked about it then too. A request the authorize endpoint would refuse is refused to the client that pushed it, 400 with
the authorize endpoint's error, where until 0.122.0 it was stored and refused to a user's browser when the reference was
redeemed.
AllowUnregisteredPushedRedirectUris lets a client
with RequireClientSecret name any redirect URI in a pushed request: it authenticated to push it, so the URI is its own
word rather than a browser's. The same request redeemed is held to the same rule, a public client never is, and a
front-channel request never is — including, since 0.177.0, a request object fetched by request_uri, which this
server stores behind a reference of the same shape but which nobody authenticated to send
(request objects by reference). A confidential client then needs no registered redirect URI at all, and the
configuration validator accepts it. Both options, and the reference's lifetime, are in the
configuration reference.
The seams are Duende's: IPushedAuthorizationRequestValidator and
IPushedAuthorizationResponseGenerator, each with an open default.
IAuthorizeRequestValidator's ValidateAsync takes Duende's
AuthorizeRequestType — Authorize, PushedAuthorization or
AuthorizeWithPushedParameters — which the validated request and the redirect URI validator's context carry.
Breaking in 0.122.0: it comes before the cancellation token, so a call passing the token positionally as the third
argument names it instead.