Skip to main content

oauth_as/
http.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (C) 2026 Matthew Jackson
3
4//! An OPTIONAL HTTP service over [`AuthorizationServer`], behind the `http` cargo feature.
5//!
6//! # Why this is optional, and stays optional
7//!
8//! The premise of this crate is that the host owns the listener. A library that drags a web
9//! framework and an async runtime into every consumer has taken that decision away, so the
10//! default feature set is empty and nothing in the library below this module knows this module
11//! exists. Turning the feature on is the host saying "serve the RFC-shaped wire surface for me
12//! rather than making me write it"; leaving it off costs nothing, not even a compiled dependency.
13//!
14//! # Why there is no web framework in this module's PUBLIC API
15//!
16//! This module speaks `http` 1.x and `http-body` 1.x and nothing else: [`AuthorizationService`]
17//! is an `async fn` from an [`http::Request`] to an [`http::Response`]. Those two crates are 1.0
18//! and their major has never moved, so a host may mount this service under whatever server it
19//! already runs.
20//!
21//! That is a deliberate correction. Until 0.9 this module handed back an `axum::Router`, which
22//! put a 0.x major in the signature of the only way to use the feature: a host on any other axum
23//! major could not enable `http` AT ALL, and an axum major bump would have been a breaking change
24//! to THIS crate for reasons that have nothing to do with OAuth. axum is now a thin ADAPTER
25//! behind the separate `axum` feature (`impl From<AuthorizationService<..>> for axum::Router`),
26//! so the hazard is confined to hosts that opt into it.
27//!
28//! Nothing was hand-rolled to get there. This module already wrote its own percent-decoder, form
29//! parser, first-wins parameter logic, Basic-auth decoder and response builders, precisely so that
30//! the RFC-mandated headers land on the same response as the body they describe; it used a
31//! framework for exactly three things (route matching, one dynamic path segment, and body
32//! collection with a cap), and those three are what `Routes::resolve` and `collect_body` now do
33//! in this file.
34//!
35//! # What it serves
36//!
37//! Exactly the endpoints [`crate::metadata::AuthorizationServerMetadata`] advertises, at exactly the paths it
38//! advertises them, plus the RFC 8628 `verification_uri`. The paths are DERIVED from the metadata
39//! document rather than hard-coded, because an advertised endpoint that 404s is a lie a client
40//! cannot recover from: if a host overrides `token_endpoint`, the route moves with it or
41//! [`ServiceBuilder::build`] refuses to produce a service at all.
42//!
43//! Under the `jwt` feature that includes `jwks_uri`: the document advertises it exactly when the
44//! server signs its access tokens, and this router serves the RFC 7517 key set there. A resource
45//! server that cannot fetch the keys cannot verify a single RFC 9068 token, so an advertised
46//! `jwks_uri` this router cannot reach is the same lie as any other unroutable endpoint.
47//!
48//! # Cost
49//!
50//! The metadata document is serialized ONCE, when the router is built, and served from the
51//! resulting [`Bytes`] (a clone is a refcount bump, not a copy). The key set is serialized once
52//! for the same reason: its contents change only when the host rebuilds the router. The
53//! `WWW-Authenticate` challenge
54//! is likewise built once. There are no lazy statics, no background tasks, and no per-request
55//! rebuilding of anything derived from configuration. Request parsing borrows out of the request
56//! body and query string and only allocates for values that actually needed percent-decoding.
57//!
58//! # What the host still owns, and MUST wire
59//!
60//! Three things this module cannot invent, each with a seam, and each of which REFUSES when the
61//! seam is not wired:
62//!
63//! 1. Authenticating the RESOURCE OWNER ([`ServiceBuilder::with_subject_resolver`]). This module
64//!    cannot know how a host logs a user in.
65//! 2. CONSENT at the authorization endpoint ([`ServiceBuilder::with_approval_resolver`]). RFC 6749
66//!    s10.12 requires the AS to ensure the resource owner is aware of, and explicitly consents
67//!    to, the authorization. A subject resolver answers "who is this"; it does not answer "did
68//!    they agree", and treating the first as the second is an AS that silently authorizes any
69//!    registered client on any cross-site navigation.
70//! 3. A CSRF token bound to the host's session, for the device verification form
71//!    ([`ServiceBuilder::with_csrf_tokens`]). This crate has no session store, so it cannot mint
72//!    one; RFC 6749 s10.12 still requires the protection, so an unwired host gets a refusal and
73//!    is never served a submittable, forgeable form.
74//!
75//! Every non-interactive endpoint works regardless of all three.
76//!
77//! # What this service CANNOT do, and a host must not advertise through it
78//!
79//! RFC 8705 mutual-TLS client authentication. A build with the `mtls` feature advertises
80//! `tls_client_auth` and `self_signed_tls_client_auth` in the RFC 8414 document's
81//! `token_endpoint_auth_methods_supported`, and a client that reads them and authenticates that
82//! way THROUGH THIS SERVICE is refused with `invalid_client`, every time. That is not an oversight: this module is handed an already-parsed request, it never
83//! terminates TLS and never sees the connection, so there is no certificate here that anybody
84//! verified. Reading one out of a proxy header would trust that header on every deployment's
85//! behalf rather than on the one host that knows whether its terminator can be trusted; the
86//! `oauth_as::mtls` module's trust boundary section is the argument in full.
87//!
88//! So a host that terminates mTLS must call `AuthorizationServer::token_with_context` (or the
89//! other `*_with_credential` entry points) itself, passing the `ClientCredential::certificate` it
90//! verified. A host that mounts only this service should not register mTLS clients at all,
91//! because the metadata document will invite them and this endpoint will refuse them.
92
93use std::borrow::Cow;
94use std::sync::Arc;
95
96use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
97use base64::Engine as _;
98use bytes::Buf as _;
99use http::{header, HeaderMap, HeaderValue, Method, Request, StatusCode, Uri};
100use http_body::Body as _;
101use serde::Serialize;
102use sha2::{Digest as _, Sha256};
103
104use crate::authorization::{AuthorizationError, AuthorizationRequest};
105use crate::client::ClientId;
106use crate::device::{normalize_user_code, DeviceGrant, DeviceGrantState};
107use crate::error::{ErrorCode, ErrorResponse};
108use crate::events::{Attempt, AttemptOutcome, RateLimitDecision};
109use crate::grant::GrantType;
110use crate::metadata::well_known_path;
111use crate::scope::ScopeSet;
112use crate::server::{AuthorizationServer, Clock, DeviceApprovalError, TokenRequest, UserApproval};
113use crate::store::Storage;
114use crate::token::TokenTypeHint;
115
116/// Re-exported so a host building a [`ApprovalDecision::Respond`] body does not have to name
117/// `bytes` in its own manifest just to agree with this crate about which version it means.
118pub use bytes::Bytes;
119
120/// The response body this module produces: a single in-memory buffer, already complete.
121///
122/// Every response an authorization server emits is a short JSON document, a small HTML page, or
123/// nothing at all, and all of them are finished before the first byte is written. So the body type
124/// is a `Bytes` rather than a stream: there is nothing to stream, and a boxed
125/// `dyn http_body::Body` would add an allocation and a virtual call per response to express a
126/// capability this module never uses.
127///
128/// It implements [`http_body::Body`] with an INFALLIBLE error type and an EXACT size hint. The
129/// second matters on the wire: a server that knows the length emits `Content-Length` rather than
130/// falling back to chunked transfer encoding.
131#[derive(Debug, Default, Clone)]
132pub struct Body(Option<Bytes>);
133
134impl Body {
135    /// A body with no bytes at all. RFC 7009 s2.2's revocation success and RFC 7592 s2.3's
136    /// deletion both answer with one.
137    pub fn empty() -> Self {
138        Body(None)
139    }
140
141    /// The bytes, consuming the body.
142    pub fn into_bytes(self) -> Bytes {
143        self.0.unwrap_or_default()
144    }
145}
146
147impl From<Bytes> for Body {
148    fn from(bytes: Bytes) -> Self {
149        // An empty `Bytes` and "no frame at all" are the same response on the wire, and
150        // collapsing them here means `is_end_stream` is true from the start for an empty body,
151        // so a server need not poll for a frame it will never get.
152        match bytes.is_empty() {
153            true => Body(None),
154            false => Body(Some(bytes)),
155        }
156    }
157}
158
159// Spelled out one type at a time rather than as `impl<T: Into<Bytes>>`, which cannot be written:
160// it would overlap with the standard library's reflexive `From<T> for T` and coherence has no way
161// to rule that out.
162impl From<Vec<u8>> for Body {
163    fn from(value: Vec<u8>) -> Self {
164        Body::from(Bytes::from(value))
165    }
166}
167
168impl From<String> for Body {
169    fn from(value: String) -> Self {
170        Body::from(Bytes::from(value))
171    }
172}
173
174impl From<&'static str> for Body {
175    fn from(value: &'static str) -> Self {
176        Body::from(Bytes::from_static(value.as_bytes()))
177    }
178}
179
180impl http_body::Body for Body {
181    type Data = Bytes;
182    // This body is already in memory, so there is no read that could fail. Naming that in the
183    // type means a host mounting the service never has to write an error arm that cannot happen.
184    type Error = std::convert::Infallible;
185
186    fn poll_frame(
187        mut self: std::pin::Pin<&mut Self>,
188        _cx: &mut std::task::Context<'_>,
189    ) -> std::task::Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
190        std::task::Poll::Ready(self.0.take().map(|b| Ok(http_body::Frame::data(b))))
191    }
192
193    fn is_end_stream(&self) -> bool {
194        self.0.is_none()
195    }
196
197    fn size_hint(&self) -> http_body::SizeHint {
198        http_body::SizeHint::with_exact(self.0.as_ref().map_or(0, |b| b.len() as u64))
199    }
200}
201
202/// The response type this module produces, and the one
203/// [`ApprovalDecision::Respond`] carries.
204///
205/// A plain [`http::Response`], deliberately: the host that renders a consent screen builds it with
206/// whatever it already has, and `http` 1.x is the one HTTP vocabulary every Rust web framework
207/// agrees on.
208pub type Response = http::Response<Body>;
209
210/// Build a response with a status and a complete body, and no headers yet.
211///
212/// Every caller sets its own `Content-Type` (and, on the token plane, the RFC 6749 s5.1 caching
213/// directives), so nothing is guessed here. That is the same reason this module never used a JSON
214/// extractor: the RFC-mandated headers and the body they describe are set in one place.
215fn respond(status: StatusCode, body: impl Into<Body>) -> Response {
216    let mut resp = Response::new(body.into());
217    *resp.status_mut() = status;
218    resp
219}
220
221/// How the host names the authenticated resource owner for the interactive endpoints.
222///
223/// The `HeaderMap` is the request's, so a host can read its own session cookie or a
224/// reverse-proxy assertion header. `None` means "nobody is logged in", which is a refusal, not
225/// an error: see [`ServiceBuilder::with_subject_resolver`].
226pub type SubjectResolver = Arc<dyn Fn(&HeaderMap) -> Option<String> + Send + Sync>;
227
228/// A CSRF token hook: issue one for, or take one back from, the session this request carries.
229///
230/// `None` means "this request has no session", which is a refusal. See
231/// [`ServiceBuilder::with_csrf_tokens`] for the contract the two hooks satisfy together.
232pub type CsrfTokenHook = Arc<dyn Fn(&HeaderMap) -> Option<String> + Send + Sync>;
233
234/// What the host's approval step decided about one authorization request.
235///
236/// APPROVAL, not CONSENT, and the distinction is what the rename in 0.9.1 bought. This type is a
237/// UI PROMPT: a question asked about one request, answered once, and never stored. The `consent`
238/// module's [`crate::consent::ConsentRecord`] is a PERSISTED GRANT: a durable statement that
239/// survives the request and can be withdrawn. Both were called "consent" at the crate root, which
240/// is two meanings of one word in the one place a reader looks first. The direct API already
241/// called the prompt's answer [`crate::server::UserApproval`], so this is the crate agreeing with
242/// itself rather than inventing a third vocabulary.
243///
244/// Naming the third variant `Respond` is the point of the type: a real host shows a consent
245/// SCREEN, which means the first request renders HTML and a later one carries the answer. The
246/// resolver returns that page here and the router serves it unchanged, so interposing a consent
247/// UI never requires abandoning this router.
248/// `#[non_exhaustive]`: this type's shape already varies with the cargo features a host
249/// enables, so an exhaustive match on it was never portable between builds of this crate.
250#[non_exhaustive]
251pub enum ApprovalDecision {
252    /// The resource owner has agreed to this exact request. RFC 6749 s4.1.2: mint the code.
253    Approve,
254    /// The resource owner refused. RFC 6749 s4.1.2.1: `access_denied` at the redirect URI, which
255    /// is an answer the client is entitled to receive.
256    Deny,
257    /// The resource owner agreed AND asked not to be asked again: mint the code, and record (or
258    /// widen) the consent so a later request can be recognised as already granted.
259    ///
260    /// A separate variant rather than something the library infers, because remembering is a
261    /// statement about a user's intent and this crate never sees a user. It will not remember a
262    /// consent nobody asked it to remember, and it will not approve one it does remember.
263    #[cfg(feature = "consent")]
264    ApproveAndRemember,
265    /// Serve this response instead, unchanged: a consent screen, a step-up challenge, a redirect
266    /// back into the host's own flow. Nothing is issued.
267    ///
268    /// READ WHEN THIS IS REACHED, because one case a reader expects is not among them. The
269    /// authorization endpoint refuses BEFORE this resolver runs when
270    /// [`ServiceBuilder::with_subject_resolver`] answers `None`, so a resolver cannot answer a
271    /// signed-out visitor with a login redirect through this router: there is no subject to build
272    /// an [`ApprovalRequest`] around, and inventing one would be this crate deciding who the user
273    /// is. A host that wants to send an anonymous visitor to its login page does it in front of
274    /// this service, where its session already lives; what this variant serves is every decision
275    /// taken about a user the host has already named, step-up included.
276    Respond(Box<Response>),
277}
278
279/// What the host's approval resolver is told about the request it is being asked to approve.
280///
281/// Everything borrows: the resolver is called inside the request path and nothing here outlives
282/// it. The request has already passed RFC 6749 s4.1.1 validation, so `client_id`, `redirect_uri`
283/// and `scope` are the VALIDATED values (the redirect URI is a registered one, the scope is
284/// inside the client's registration), not raw query text.
285/// `#[non_exhaustive]`: this type's shape already varies with the cargo features a host
286/// enables, so an exhaustive match on it was never portable between builds of this crate.
287#[non_exhaustive]
288pub struct ApprovalRequest<'a> {
289    /// The request's headers, so the host can find its own session.
290    pub headers: &'a HeaderMap,
291    /// The authenticated resource owner, as named by the subject resolver.
292    pub subject: &'a str,
293    /// The client asking.
294    pub client_id: &'a ClientId,
295    /// The scope that will be granted if this is approved.
296    pub scope: &'a ScopeSet,
297    /// The registered redirect URI this request resolved to.
298    pub redirect_uri: &'a str,
299    /// The client's `state`, if it sent one.
300    pub state: Option<&'a str>,
301    /// The RFC 8707 resource indicators this request asked for, already validated against the
302    /// server's `allowed_resources`. Empty when the client named none.
303    ///
304    /// It is here because the audience a token will carry is part of what the user is being asked
305    /// to approve: "read your calendar" means something different at one resource server than at
306    /// another, and the host cannot recover this from the query. For a PAR request the query holds
307    /// only `client_id` and the request URI, and the pushed record has already been consumed by
308    /// the time this resolver runs; for a JAR request the values are inside the signed object.
309    pub resource: &'a [String],
310    /// The RFC 9396 `authorization_details` this request asked for, already parsed and already
311    /// checked against the server's supported types (section 5).
312    ///
313    /// THE TYPE CHECK IS NOT AN APPROVAL. `AuthorizationDetails::require_supported_types` inspects
314    /// the `type` string alone, so the amount, the `identifier`, the creditor account and every
315    /// other type-specific member of an element reach the issued token unexamined unless this
316    /// resolver looks at them. RFC 9396 section 2 makes the elements the thing being authorized,
317    /// and this crate never renders a screen, so the decision belongs here: a host that shows only
318    /// [`ApprovalRequest::scope`] is asking the user to approve a payment they were never shown.
319    /// Like [`ApprovalRequest::resource`], it cannot be recovered from the query on the PAR or JAR
320    /// paths.
321    #[cfg(feature = "rar")]
322    pub authorization_details: &'a crate::rar::AuthorizationDetails,
323    /// The full request URI, so a host that renders a consent screen can round-trip the user
324    /// back to exactly this request after they answer.
325    pub uri: &'a Uri,
326    /// What this user has already granted this client, if anything.
327    ///
328    /// This is the library REPORTING and the host DECIDING, and that split is the whole design.
329    /// [`crate::consent::ConsentRecord::covers`] answers whether the remembered grant already
330    /// covers what is being asked for now; whether that is a good enough reason to skip the prompt
331    /// depends on how long ago it was, what the scope means in this deployment, and whether the
332    /// user is on a device the host trusts, none of which this crate knows. So it is handed over,
333    /// and nothing here ever approves on the strength of it.
334    ///
335    /// `covers` takes all three of what is being asked for, and the third is
336    /// [`ApprovalRequest::authorization_details`], wrapped by
337    /// [`crate::consent::RequestedDetails::of`]. It answers `false` for any request that carries
338    /// one, so a resolver that skips its prompt on a `true` still asks about every RFC 9396
339    /// element: a remembered consent records a scope and a resource list, and an element it never
340    /// recorded is not something it can be said to cover. That method's docs give the argument in
341    /// full, including why the answer would barely change if it did record them.
342    #[cfg(feature = "consent")]
343    pub remembered: Option<&'a crate::consent::ConsentRecord>,
344}
345
346/// How the host makes the RFC 6749 s10.12 approval decision. See
347/// [`ServiceBuilder::with_approval_resolver`].
348pub type ApprovalResolver = Arc<dyn Fn(&ApprovalRequest<'_>) -> ApprovalDecision + Send + Sync>;
349
350/// How the host answers "when, and how, did you authenticate this user".
351///
352/// The third identity seam, and the one RFC 9470 needs: a subject resolver answers WHO, an approval
353/// resolver answers WHETHER THEY AGREED, and this answers HOW STRONGLY AND HOW RECENTLY. `None`
354/// means the host is not reporting one, which satisfies no `acr_values` and no `max_age`; see
355/// [`ServiceBuilder::with_authentication_reporter`].
356#[cfg(feature = "consent")]
357pub type AuthenticationReporter =
358    Arc<dyn Fn(&HeaderMap) -> Option<crate::consent::Authentication> + Send + Sync>;
359
360/// How the device verification form is protected against RFC 6749 s10.12 cross-site forced
361/// approval.
362enum VerificationProtection {
363    /// No seam wired. Every interactive path refuses; see [`ServiceBuilder::with_csrf_tokens`].
364    Unwired,
365    /// The host mints and takes back a session-bound token.
366    Tokens {
367        /// Mint a token for the form this GET is about to render.
368        issue: CsrfTokenHook,
369        /// Take back (and invalidate) the token this session was last issued.
370        consume: CsrfTokenHook,
371    },
372    /// Explicitly disabled by the host. See
373    /// [`ServiceBuilder::dangerously_disable_verification_protections`].
374    Disabled,
375}
376
377/// Why a router could not be built. Every variant is a host configuration mistake that would
378/// otherwise become a runtime 404 on an endpoint the metadata document promises.
379#[derive(Debug, Clone, PartialEq, Eq)]
380/// `#[non_exhaustive]`: this type's shape already varies with the cargo features a host
381/// enables, so an exhaustive match on it was never portable between builds of this crate.
382#[non_exhaustive]
383pub enum ServiceError {
384    /// An advertised endpoint is not under the issuer, so this router cannot serve it. The host
385    /// is either fronting a separate service or has a typo; either way, silently not routing it
386    /// would publish a promise nothing keeps.
387    EndpointOutsideIssuer {
388        /// The RFC 8414 member name.
389        endpoint: &'static str,
390        /// The URL as advertised.
391        url: String,
392    },
393    /// Two endpoints resolved to the same path, so one would shadow the other.
394    DuplicatePath {
395        /// The path claimed twice.
396        path: String,
397    },
398    /// The metadata document could not be serialized. Structurally impossible for the derived
399    /// document, but reported rather than panicked: a library does not abort a host's process.
400    MetadataNotSerializable {
401        /// The serializer's message.
402        detail: String,
403    },
404    /// The RFC 7517 key set could not be serialized. As structurally impossible as the metadata
405    /// case, and reported for the same reason.
406    #[cfg(feature = "jwt")]
407    JwksNotSerializable {
408        /// The serializer's message.
409        detail: String,
410    },
411    /// The document advertises a `jwks_uri` under the issuer, in a build with no `jwt` feature.
412    /// Nothing here can serve a key set, so the member is a promise of a path that can only 404.
413    /// A key set some other component holds is still fine: point the member outside the issuer,
414    /// which is what "some other component holds the keys" looks like in a URL.
415    #[cfg(not(feature = "jwt"))]
416    JwksNotServable {
417        /// The URL as advertised.
418        url: String,
419    },
420}
421
422impl std::fmt::Display for ServiceError {
423    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424        match self {
425            ServiceError::EndpointOutsideIssuer { endpoint, url } => write!(
426                f,
427                "advertised {endpoint} ({url}) is not under the issuer, so this router cannot \
428                 serve it"
429            ),
430            ServiceError::DuplicatePath { path } => {
431                write!(f, "two endpoints resolve to the same path {path}")
432            }
433            ServiceError::MetadataNotSerializable { detail } => {
434                write!(f, "RFC 8414 metadata could not be serialized: {detail}")
435            }
436            #[cfg(feature = "jwt")]
437            ServiceError::JwksNotSerializable { detail } => {
438                write!(f, "RFC 7517 key set could not be serialized: {detail}")
439            }
440            #[cfg(not(feature = "jwt"))]
441            ServiceError::JwksNotServable { url } => write!(
442                f,
443                "advertised jwks_uri ({url}) is under the issuer, but this build has no jwt \
444                 feature and so has no key set to serve there"
445            ),
446        }
447    }
448}
449
450impl std::error::Error for ServiceError {}
451
452/// Everything a handler needs, built once and shared by refcount.
453struct Inner<S: Storage, C: Clock> {
454    server: Arc<AuthorizationServer<S, C>>,
455    /// The RFC 8414 document, serialized at build time. Serving it is a refcount bump.
456    metadata: Bytes,
457    /// The RFC 7517 key set, serialized at build time, present exactly when the document
458    /// advertises a `jwks_uri` this router serves. PUBLIC parameters only: the bytes come from
459    /// [`AuthorizationServer::jwks`], which has no way to emit a private key parameter.
460    #[cfg(feature = "jwt")]
461    jwks: Option<Bytes>,
462    /// The RFC 6749 s5.2 / RFC 7617 challenge, built at build time because the realm never
463    /// changes and formatting it per request would be pure waste.
464    challenge: HeaderValue,
465    /// The issuer's `scheme://authority`, for the RFC 6749 s10.12 `Origin` check. Derived once,
466    /// because comparing against a freshly parsed issuer on every POST is pure waste.
467    origin: String,
468    subject: Option<SubjectResolver>,
469    approval: Option<ApprovalResolver>,
470    #[cfg(feature = "consent")]
471    authentication: Option<AuthenticationReporter>,
472    verification: VerificationProtection,
473    /// The paths, derived from the metadata document when the service was built.
474    routes: Routes,
475}
476
477impl<S: Storage, C: Clock> Inner<S, C> {
478    /// The authenticated resource owner, if the host can name one.
479    fn subject(&self, headers: &HeaderMap) -> Option<String> {
480        self.subject.as_ref().and_then(|f| f(headers))
481    }
482}
483
484/// Builds the router. Construct, attach the seams the interactive endpoints need, then
485/// [`build`](ServiceBuilder::build).
486///
487/// # The interactive endpoints refuse until they are wired
488///
489/// [`with_subject_resolver`](ServiceBuilder::with_subject_resolver) alone is NOT enough to run an
490/// authorization server safely, and this is the one thing to read in this file. It answers "who
491/// is this user"; RFC 6749 s10.12 also demands "did the user knowingly agree". Wire
492/// [`with_approval_resolver`](ServiceBuilder::with_approval_resolver) and
493/// [`with_csrf_tokens`](ServiceBuilder::with_csrf_tokens) too, or the authorization endpoint and
494/// the device verification form refuse rather than guessing that silence means yes.
495pub struct ServiceBuilder<S: Storage, C: Clock> {
496    server: Arc<AuthorizationServer<S, C>>,
497    subject: Option<SubjectResolver>,
498    approval: Option<ApprovalResolver>,
499    #[cfg(feature = "consent")]
500    authentication: Option<AuthenticationReporter>,
501    verification: VerificationProtection,
502}
503
504impl<S: Storage + 'static, C: Clock + 'static> ServiceBuilder<S, C> {
505    /// Start from a running server. `Arc` rather than ownership so the host keeps its handle for
506    /// administration (client registration, sweeping) while the router serves the same instance.
507    pub fn new(server: Arc<AuthorizationServer<S, C>>) -> Self {
508        ServiceBuilder {
509            server,
510            subject: None,
511            approval: None,
512            #[cfg(feature = "consent")]
513            authentication: None,
514            verification: VerificationProtection::Unwired,
515        }
516    }
517
518    /// Supply the host's answer to "who is the logged-in user for this request".
519    ///
520    /// The authorization endpoint cannot mint a code without a resource owner, and the device
521    /// verification page cannot approve a grant without one. This crate has no login UI and no
522    /// session model by design, so without a resolver both endpoints refuse with 403 rather than
523    /// inventing a user.
524    ///
525    /// This resolver is IDENTITY ONLY. It does not express approval; see
526    /// [`with_approval_resolver`](ServiceBuilder::with_approval_resolver).
527    pub fn with_subject_resolver<F>(mut self, resolver: F) -> Self
528    where
529        F: Fn(&HeaderMap) -> Option<String> + Send + Sync + 'static,
530    {
531        self.subject = Some(Arc::new(resolver));
532        self
533    }
534
535    /// Supply the host's answer to "has this user knowingly agreed to this exact request".
536    ///
537    /// RFC 6749 s10.12 requires the AS to "ensure that the malicious client cannot obtain
538    /// authorization without the awareness and explicit consent of the resource owner". An
539    /// authorization endpoint that mints a code as soon as it knows who the user is satisfies
540    /// neither half: any cross-site top-level navigation makes a logged-in user's browser hand a
541    /// registered client a code they never asked to issue. PKCE and exact redirect-URI matching
542    /// bound WHO may redeem that code; they say nothing about whether it should have existed.
543    ///
544    /// With NO resolver the authorization endpoint refuses with 403 and issues nothing. That is
545    /// deliberate and it is a behaviour change: a host that previously wired only
546    /// [`with_subject_resolver`](ServiceBuilder::with_subject_resolver) was running an
547    /// AUTO-APPROVING authorization server, and the fix is to say what the approval step is rather
548    /// than to leave it implied.
549    ///
550    /// Return [`ApprovalDecision::Respond`] to render a consent screen and finish the flow on a
551    /// later request; return [`ApprovalDecision::Approve`] only once the user has actually agreed.
552    pub fn with_approval_resolver<F>(mut self, resolver: F) -> Self
553    where
554        F: Fn(&ApprovalRequest<'_>) -> ApprovalDecision + Send + Sync + 'static,
555    {
556        self.approval = Some(Arc::new(resolver));
557        self
558    }
559
560    /// Supply the host's answer to "when, and how, did you authenticate this user".
561    ///
562    /// REQUIRED for RFC 9470 step-up authentication and useless without it. A client answering a
563    /// resource server's `insufficient_user_authentication` challenge repeats its authorization
564    /// request with `acr_values` and/or `max_age`; this server enforces those against whatever the
565    /// reporter returns, and a host with no reporter wired fails every such request. That is the
566    /// correct answer rather than a bug: an authorization server that cannot say when the user
567    /// logged in cannot honestly claim they logged in recently.
568    ///
569    /// Ordinary requests, which carry neither parameter, are unaffected whether this is wired or
570    /// not.
571    ///
572    /// The report is taken at FACE VALUE. This crate cannot authenticate anyone and has nothing to
573    /// check it against; see the [`crate::consent`] module docs.
574    #[cfg(feature = "consent")]
575    pub fn with_authentication_reporter<F>(mut self, reporter: F) -> Self
576    where
577        F: Fn(&HeaderMap) -> Option<crate::consent::Authentication> + Send + Sync + 'static,
578    {
579        self.authentication = Some(Arc::new(reporter));
580        self
581    }
582
583    /// Supply the host's session-bound CSRF token for the device verification form.
584    ///
585    /// `issue` is called when the form is RENDERED: it mints a token, binds it to whatever
586    /// session the request carries, and returns it to be embedded in the form. `consume` is
587    /// called when the form is SUBMITTED: it returns the token that session was last issued AND
588    /// invalidates it, which is what makes the token single use. The router compares the
589    /// submitted token with the consumed one in constant time; a mismatch, or either hook
590    /// answering `None`, is a refusal.
591    ///
592    /// Why this is a seam and not something the library does: approving a device grant binds a
593    /// third party's grant to the logged-in user, so RFC 6749 s10.12's CSRF requirement applies
594    /// with full force, and the countermeasure has to be bound to the SESSION. This crate has no
595    /// session store and will not grow one. With no hooks the verification endpoint renders no
596    /// form and approves nothing, because a form that works and is forgeable is worse than no
597    /// form at all.
598    pub fn with_csrf_tokens<I, V>(mut self, issue: I, consume: V) -> Self
599    where
600        I: Fn(&HeaderMap) -> Option<String> + Send + Sync + 'static,
601        V: Fn(&HeaderMap) -> Option<String> + Send + Sync + 'static,
602    {
603        self.verification = VerificationProtection::Tokens {
604            issue: Arc::new(issue),
605            consume: Arc::new(consume),
606        };
607        self
608    }
609
610    /// Turn OFF the device verification form's CSRF token requirement, its `Origin` check, and
611    /// its affirmative-action requirement.
612    ///
613    /// FOR NON-BROWSER TEST HARNESSES ONLY. On an endpoint a browser can reach this re-enables
614    /// the complete RFC 6749 s10.12 cross-site forced-approval chain: an attacker starts a device
615    /// grant for a client they control, gets any authenticated victim's browser to POST the
616    /// `user_code`, and polls out an access token and a refresh token for the victim's account.
617    /// That is account takeover, and it needs no interaction beyond loading a page.
618    ///
619    /// It exists because this crate's black-box conformance harness drives the verification
620    /// endpoint with an HTTP client and no browser session, so it cannot hold a CSRF token. It is
621    /// spelled this loudly so that it is greppable, and so that no production host reaches for it
622    /// without having read what it does.
623    pub fn dangerously_disable_verification_protections(mut self) -> Self {
624        self.verification = VerificationProtection::Disabled;
625        self
626    }
627
628    /// Derive the routes from the metadata document and build the service.
629    ///
630    /// # Errors
631    ///
632    /// [`ServiceError`] when the configuration advertises something this service cannot serve.
633    pub fn build(self) -> Result<AuthorizationService<S, C>, ServiceError> {
634        let config = self.server.config();
635        // The SERVER's document, not the configuration's: what this service can honour depends on
636        // the seams the host installed on the server as well as on the configuration, and RFC 7523
637        // `private_key_jwt` is the case that separates the two. See
638        // [`crate::AuthorizationServer::metadata`].
639        // `mut` for the RFC 8705 strip below, which is the one place the document this service
640        // SERVES has to say less than the document the server describes itself with.
641        #[allow(unused_mut)]
642        let mut meta = self.server.metadata();
643        // `from_config` trims the issuer, and derives every default endpoint from that trimmed
644        // form, so the prefix relation below holds for an unconfigured host by construction.
645        let issuer = meta.issuer.clone();
646
647        // RFC 8705 sections 2.1.1, 2.2.1 and 3.3, REMOVED from the served document.
648        //
649        // Every one of them is true of `AuthorizationServer` reached through a host's own handler
650        // with a certificate its TLS terminator verified, and none of them is true of THIS router,
651        // which is handed an already-parsed request and passes `certificate: None` on every
652        // credential it builds (see `Credentials::credential`). A client that reads
653        // `tls_client_auth` here and presents a certificate is answered `invalid_client` forever;
654        // one that reads the section 3.3 flag believes its token is certificate bound when it is a
655        // bearer token. The field doc used to offer "a host that is not doing that should not
656        // compile the `mtls` feature in", which cargo feature unification takes out of the host's
657        // hands: one other crate in the graph enabling `mtls` makes this router lie.
658        //
659        // Only the SERVED copy is touched. `AuthorizationServer::metadata()` is unchanged, so a
660        // host serving its own routes still publishes the full document, which for that host is
661        // the honest one.
662        #[cfg(feature = "mtls")]
663        {
664            meta.token_endpoint_auth_methods_supported.retain(|m| {
665                m != crate::mtls::TLS_CLIENT_AUTH && m != crate::mtls::SELF_SIGNED_TLS_CLIENT_AUTH
666            });
667            meta.tls_client_certificate_bound_access_tokens = false;
668        }
669
670        let default_introspection_endpoint = format!("{issuer}/introspect");
671
672        let authorize = endpoint_path(
673            &issuer,
674            "authorization_endpoint",
675            &meta.authorization_endpoint,
676        )?;
677        let token = endpoint_path(&issuer, "token_endpoint", &meta.token_endpoint)?;
678        let device = endpoint_path(
679            &issuer,
680            "device_authorization_endpoint",
681            &meta.device_authorization_endpoint,
682        )?;
683        // RFC 7662. The one route derived from the CONFIGURATION rather than from the document,
684        // and deliberately: `from_config` publishes `introspection_endpoint` only where the host
685        // named it, because whether this server answers a RESOURCE SERVER depends on whether the
686        // deployment registered any (`ServerConfig::resource_servers`), and only the host knows
687        // that. See the field's doc in `crate::metadata` for why 0.9.2 building the channel did
688        // NOT make the member unconditional. Withdrawing the ROUTE with the promise would take
689        // away the half that always works -- a client asking about its own token -- which is a
690        // functional regression rather than an honesty fix. Serving a path the document does not
691        // name misleads nobody; the rule this module opens with is about the other direction.
692        let introspect = Some(endpoint_path(
693            &issuer,
694            "introspection_endpoint",
695            match &config.introspection_endpoint {
696                Some(u) => u,
697                None => &default_introspection_endpoint,
698            },
699        )?);
700        let revoke = match &meta.revocation_endpoint {
701            Some(u) => Some(endpoint_path(&issuer, "revocation_endpoint", u)?),
702            None => None,
703        };
704        // RFC 7591 s3 / RFC 8414 s2. `from_config` advertises this exactly when the host enabled
705        // dynamic registration, so an off-issuer value is an error for the same reason
706        // introspection's is: these bytes are produced by this server and nothing else can produce
707        // them. A host that never enabled registration routes nothing here at all, which is the
708        // only way an endpoint that mints clients should ever come to exist.
709        let register = match &meta.registration_endpoint {
710            Some(u) => Some(endpoint_path(&issuer, "registration_endpoint", u)?),
711            None => None,
712        };
713        // RFC 7592 s3 `registration_client_uri`: `{registration_endpoint}/{client_id}`, which is
714        // exactly what `registration::register_dynamic_client` hands the client, so the URL it is
715        // told to use is the URL this service answers on.
716        //
717        // Stored as the PREFIX (with the trailing slash) because that is what the matcher needs;
718        // the pattern form below exists only so the collision check and its error message name
719        // something a host can recognise in its own configuration.
720        let manage_prefix = register
721            .as_ref()
722            .filter(|_| {
723                config
724                    .registration
725                    .as_ref()
726                    .is_some_and(|r| r.management_enabled)
727            })
728            .map(|p| format!("{p}/"));
729        let manage = manage_prefix.as_ref().map(|p| format!("{p}{{client_id}}"));
730        // The verification URI is NOT part of the RFC 8414 document (it is announced in each RFC
731        // 8628 s3.2 response), and a host may legitimately host its device page on a different
732        // origin entirely. So an off-issuer verification URI is not an error, it just means the
733        // host serves that page itself.
734        let verification =
735            endpoint_path(&issuer, "verification_uri", &config.verification_uri).ok();
736
737        // RFC 9126 s5 `pushed_authorization_request_endpoint`. `from_config` advertises it exactly
738        // when the host set `ServerConfig::par`, and section 5 says its presence is sufficient for
739        // a client to decide it may use PAR. So an advertised endpoint that is not routed is not a
740        // convenience gap, it is the one lie a client had no way to check first: it would push its
741        // whole request, including the PKCE challenge, at a 404 and have nowhere to fall back to.
742        // Off-issuer is an error for the same reason introspection's is: these bytes are minted by
743        // this server and nothing else can mint them.
744        #[cfg(feature = "par")]
745        let par = match &meta.pushed_authorization_request_endpoint {
746            Some(u) => Some(endpoint_path(
747                &issuer,
748                "pushed_authorization_request_endpoint",
749                u,
750            )?),
751            None => None,
752        };
753
754        // RFC 8414 s2 `jwks_uri`. `from_config` advertises it exactly when this server signs its
755        // access tokens, so when it is present the key set is ours to serve and an off-issuer URL
756        // is an error, exactly as it is for introspection and revocation. That is stricter than
757        // `verification_uri` above on purpose: the device page is a host's own branded HTML, while
758        // these bytes are produced by this server and nothing else can produce them.
759        #[cfg(feature = "jwt")]
760        let jwks_path = match &meta.jwks_uri {
761            Some(url) => Some(endpoint_path(&issuer, "jwks_uri", url)?),
762            None => None,
763        };
764        // RFC 8414 s2 `jwks_uri` in a build WITHOUT the `jwt` feature, which is a build that signs
765        // nothing and has no key set to serve. `http` does not imply `jwt` and
766        // `ServerConfig::jwks_uri` is a plain public field, so the document could advertise the
767        // member while every branch that routes it above is compiled out: `build` returned `Ok`,
768        // the document promised the endpoint, and `resolve` answered `None`. An advertised endpoint
769        // that 404s is the exact defect this module's docs open by naming, and RFC 9068 s4 makes it
770        // expensive: a resource server that cannot fetch the keys cannot verify anything.
771        //
772        // Refused rather than silently dropped, and only when the URL is UNDER the issuer. Off
773        // issuer is the documented case for this build (`metadata::advertised_jwks_uri`: some other
774        // component holds the keys), this service never claimed that path, and nothing it serves
775        // 404s. Under the issuer there is no reading on which the promise is kept.
776        #[cfg(not(feature = "jwt"))]
777        if let Some(url) = &meta.jwks_uri {
778            if endpoint_path(&issuer, "jwks_uri", url).is_ok() {
779                return Err(ServiceError::JwksNotServable {
780                    url: url.to_string(),
781                });
782            }
783        }
784
785        // Serialized ONCE here rather than per request: a key set changes only when the host
786        // rebuilds the router, and a verifier may fetch this on every cold cache.
787        #[cfg(feature = "jwt")]
788        let jwks = match (&jwks_path, self.server.jwks()) {
789            (Some(_), Some(keys)) => Some(Bytes::from(serde_json::to_vec(&keys).map_err(|e| {
790                ServiceError::JwksNotSerializable {
791                    detail: e.to_string(),
792                }
793            })?)),
794            // Both sides read the same `access_token_format`, so a path without keys cannot
795            // arise; if it somehow did, not routing is better than routing an empty key set that
796            // a verifier would read as "this issuer has no keys".
797            _ => None,
798        };
799
800        // RFC 8414 s3.1: the well-known string is inserted BETWEEN the host and the issuer's
801        // path, so this route is NOT `{issuer path}/.well-known/...` and is not the bare
802        // well-known path either once the issuer has a path. See `metadata::well_known_path`.
803        // Normalised like every route `endpoint_path` produces, and for the same reason: this one
804        // also carries the issuer's path, so a tenant whose name a client must escape is escaped
805        // here too and the document is served at the path the client actually asks for.
806        let well_known = encode_route_path(&well_known_path(&issuer));
807
808        let metadata = serde_json::to_vec(&meta)
809            .map_err(|e| ServiceError::MetadataNotSerializable {
810                detail: e.to_string(),
811            })?
812            .into();
813
814        let mut paths: Vec<&str> = vec![&well_known, &authorize, &token, &device];
815        paths.extend(introspect.as_deref());
816        paths.extend(revoke.as_deref());
817        paths.extend(verification.as_deref());
818        paths.extend(register.as_deref());
819        paths.extend(manage.as_deref());
820        #[cfg(feature = "par")]
821        paths.extend(par.as_deref());
822        #[cfg(feature = "jwt")]
823        paths.extend(jwks_path.as_deref());
824        for i in 0..paths.len() {
825            if paths[i + 1..].contains(&paths[i]) {
826                return Err(ServiceError::DuplicatePath {
827                    path: paths[i].to_string(),
828                });
829            }
830        }
831
832        let routes = Routes {
833            well_known,
834            authorize,
835            token,
836            device,
837            introspect,
838            revoke,
839            verification,
840            register,
841            manage: manage_prefix,
842            #[cfg(feature = "par")]
843            par,
844            #[cfg(feature = "jwt")]
845            jwks: jwks_path,
846        };
847
848        Ok(AuthorizationService {
849            inner: Arc::new(Inner {
850                server: self.server,
851                metadata,
852                #[cfg(feature = "jwt")]
853                jwks,
854                // RFC 7617 s2: the realm is a quoted-string, so `"` and `\` must be escaped.
855                //
856                // The issuer OUGHT to be a URL and so ought to contain neither, and until the
857                // 0.9.1 audit this code said so and stopped there. But `ServerConfig::issuer` is a
858                // bare `String` that this crate deliberately does not validate — it does not even
859                // require `https` — so "the issuer is a URL" is an assumption about the host's
860                // configuration, not a property of the type. A stray quote picked up from a config
861                // template would otherwise put a second `realm` auth-param on every 401 this
862                // server sends, which a conforming client parses as a different challenge.
863                //
864                // `HeaderValue::from_str` already refuses CR and LF, so there was never a response
865                // splitting hole here; the escape is about producing a challenge that parses as
866                // the one thing it means.
867                challenge: HeaderValue::from_str(&format!(
868                    "Basic realm=\"{}\"",
869                    escape_quoted_string(&issuer)
870                ))
871                .unwrap_or_else(|_| HeaderValue::from_static("Basic realm=\"oauth\"")),
872                origin: issuer_origin(&issuer).to_string(),
873                subject: self.subject,
874                approval: self.approval,
875                #[cfg(feature = "consent")]
876                authentication: self.authentication,
877                verification: self.verification,
878                routes,
879            }),
880        })
881    }
882}
883
884/// The largest request body any endpoint this service serves will read.
885///
886/// 64 KiB, chosen against the largest legitimate body rather than picked round. The biggest is an
887/// RFC 7591 s2 client metadata document (a registration with many redirect URIs and a `jwks`), and
888/// after that an RFC 9126 push carrying an RFC 9101 signed request object; both are kilobytes, not
889/// tens of them. Every other body is a form of a dozen short parameters.
890///
891/// Stated rather than inherited from the framework's default, because a cap this file did not
892/// choose is a cap that can change under it, and this one is a security property: these endpoints
893/// buffer the whole body before parsing, and they are reachable before the client is
894/// authenticated, so the ceiling on "how much memory can an anonymous request make this server
895/// hold" is set here.
896/// Public because it is a ceiling a host needs when sizing its own proxy or gateway limits, and
897/// because [`MAX_FORM_PARAMETERS`] documents itself in terms of it.
898pub const MAX_BODY_BYTES: usize = 64 * 1024;
899
900/// The largest number of form or query parameters any endpoint this service serves will decode.
901///
902/// [`MAX_BODY_BYTES`] does NOT bound this, and that is why the constant exists. Decoding is per
903/// PARAMETER, not per byte: every pair is split, percent-decoded and pushed onto a vector, and
904/// every later `param` lookup is a linear scan across all of them. MEASURED with
905/// `benches/http_surface.rs`, on an aarch64 macOS laptop, before this cap existed: `POST /token`
906/// costs 2.65 us with no extra parameters, 7.55 us with 64 ignored ones, 22.15 us with 256 and
907/// 83.33 us with 1024, against 163 ns for a 404 on an unrouted path. The growth is LINEAR (81 to
908/// 118 ns per parameter across that sweep), which is the problem rather than the reassurance: 64
909/// KiB of `&a=b` pairs is roughly 2300 parameters, so a byte cap alone left one unauthenticated
910/// packet buying about three orders of magnitude more work than the service's cheapest answer.
911/// The GET endpoints are worse still: their parameters arrive in a URL, which [`MAX_BODY_BYTES`]
912/// never applied to at all.
913///
914/// With the cap, the same 1024-parameter request costs 14.50 us, and what is left is not decoding:
915/// it is buffering and UTF-8 validating the body, which [`MAX_BODY_BYTES`] already bounds and which
916/// no parameter cap can avoid, since the count cannot be known before the bytes have arrived.
917///
918/// SIXTY-FOUR, counted from the largest legitimate request rather than rounded. The biggest form
919/// this crate defines is an RFC 9126 push of an authorization request, which can carry
920/// `response_type`, `client_id`, `redirect_uri`, `scope`, `state`, `code_challenge`,
921/// `code_challenge_method`, `nonce`, `prompt`, `login_hint`, `max_age`, `acr_values`, `request`,
922/// `authorization_details` and up to four of client authentication's parameters: about twenty.
923/// After it comes an RFC 8693 exchange at about thirteen. RFC 8707 s2 allows `resource` to repeat,
924/// which is the one parameter a conforming client can send many of, and RFC 6749 s3.1 lets a
925/// deployment define extension parameters this server ignores. Sixty-four is therefore roughly
926/// three times the largest request this crate can construct, with the whole of that headroom left
927/// for repetition and extensions, and it is still 36 times below what the byte cap alone allowed.
928///
929/// The check is a count of `&` separators with an early exit, so REFUSING is cheaper than parsing
930/// even the first pair: it stops reading the input at the sixty-fourth separator. That matters
931/// because a refusal is work an attacker chooses the rate of.
932pub const MAX_FORM_PARAMETERS: usize = 64;
933
934/// The absolute request path an advertised URL occupies, measured from the ORIGIN's root.
935///
936/// Origin-rooted rather than issuer-relative because that is what a router matches against. For
937/// an issuer with no path the two are the same string; for `https://as.example/tenant1` the
938/// token endpoint is served at `/tenant1/token`, so a host can build one router per tenant and
939/// merge them without any of them colliding.
940fn endpoint_path(issuer: &str, endpoint: &'static str, url: &str) -> Result<String, ServiceError> {
941    match url.strip_prefix(issuer) {
942        Some(rest) if rest.starts_with('/') => {
943            let prefix = crate::metadata::issuer_path(issuer);
944            let mut path = String::with_capacity(prefix.len() + rest.len());
945            path.push_str(prefix);
946            path.push_str(rest);
947            // NORMALISED TO WIRE FORM here, at build time, and nowhere else. The issuer arrives
948            // exactly as the host configured it and the two legal spellings of a path a client
949            // must escape produce the same bytes on the wire; `encode_route_path` is what makes
950            // the table hold those bytes, so `handle` can compare the raw path and nothing that
951            // sits in front of this service disagrees with it about which route was asked for.
952            Ok(encode_route_path(&path))
953        }
954        _ => Err(ServiceError::EndpointOutsideIssuer {
955            endpoint,
956            url: url.to_string(),
957        }),
958    }
959}
960
961/// The issuer's `scheme://authority`, which is what an `Origin` header carries (RFC 6454 s6.1:
962/// scheme, host, and port, with no path).
963///
964/// SEARCHED for, rather than computed by subtraction. Subtracting the length of [`crate::metadata::issuer_path`] from the
965/// issuer would put the split point wherever a trailing slash the path trimmed used to be, so an
966/// issuer with both a non-ASCII path and a trailing slash split inside a character and PANICKED:
967/// `https://as.example/\u{e9}//` landed on the second byte of the two-byte character. Searching
968/// for the separator can only ever land on a boundary, whatever the issuer contains. The one
969/// caller passes an issuer already trimmed by `from_config`, so nothing reachable produced that
970/// panic, but "safe because one caller trims first" is not a property this function can state.
971fn issuer_origin(issuer: &str) -> &str {
972    // Past `://` when there is one, so a colon in the scheme cannot be read as a port and the
973    // first `/` found is the one that starts the path. `issuer_path` reads the same shape.
974    let authority_at = match issuer.find("://") {
975        Some(i) => i + 3,
976        None => 0,
977    };
978    match issuer[authority_at..].find('/') {
979        Some(i) => &issuer[..authority_at + i],
980        None => issuer,
981    }
982}
983
984// ---------------------------------------------------------------------------------------------
985// The service, its route table, and its body reader
986// ---------------------------------------------------------------------------------------------
987
988/// The paths this service answers on, derived once by [`ServiceBuilder::build`].
989///
990/// Every field is an ORIGIN-ROOTED absolute path, and every optional one is `Some` exactly when
991/// the RFC 8414 document advertises the corresponding endpoint. That equivalence is the point:
992/// [`ServiceBuilder::build`] derives both from the same document, so "advertised" and "routed"
993/// cannot drift apart, and it refuses to build when two of them land on one path.
994#[derive(Debug)]
995struct Routes {
996    well_known: String,
997    authorize: String,
998    token: String,
999    device: String,
1000    introspect: Option<String>,
1001    revoke: Option<String>,
1002    verification: Option<String>,
1003    register: Option<String>,
1004    /// RFC 7592 `{registration_endpoint}/{client_id}`, held as the prefix INCLUDING its trailing
1005    /// slash. The one dynamic segment this service has.
1006    manage: Option<String>,
1007    #[cfg(feature = "par")]
1008    par: Option<String>,
1009    #[cfg(feature = "jwt")]
1010    jwks: Option<String>,
1011}
1012
1013/// Which endpoint a request path resolved to, plus anything captured out of the path.
1014enum Route<'a> {
1015    Metadata,
1016    Authorize,
1017    Token,
1018    Device,
1019    Introspect,
1020    Revoke,
1021    Verification,
1022    Register,
1023    /// RFC 7592: the `client_id` segment, RAW as it arrived. Decoded at the match arms by
1024    /// `decode_path_segment`, and only after the route has been decided, for the reason the
1025    /// resolver's own comment gives: the path is matched in wire form.
1026    ///
1027    /// THE DECODED VALUE IS NOT VALIDATED, and it goes to the host's
1028    /// [`crate::store::Storage::get_client`]. `%2F` decodes to a real `/` here, `%2E%2E` to `..`,
1029    /// `%00` to a NUL, and invalid UTF-8 to U+FFFD. Routing is unaffected — the raw path is what
1030    /// was matched, so nothing mounted under the registration endpoint can be reached this way —
1031    /// and this crate does not refuse the value, because an identifier's syntax is the host's
1032    /// (RFC 6749 s2.2) and a host whose ids are HTTPS URLs has a `/` in every one. It is also not
1033    /// a shape unique to this route: the authorization and token endpoints hand `get_client` an
1034    /// arbitrary unauthenticated string too, so validating here would close nothing. The
1035    /// obligation is stated where the value lands, on [`crate::store::Storage::get_client`], and
1036    /// `tests/storage_client_id_contract.rs` pins it.
1037    Manage(&'a str),
1038    #[cfg(feature = "par")]
1039    Par,
1040    #[cfg(feature = "jwt")]
1041    Jwks,
1042}
1043
1044impl Routes {
1045    /// Resolve a request path, or `None` for a 404.
1046    ///
1047    /// STATIC PATHS ARE TRIED FIRST, and that ordering is load bearing rather than incidental.
1048    /// The one dynamic route (RFC 7592 management, `{register}/{client_id}`) is a prefix match, so
1049    /// a host whose configuration puts some other endpoint underneath the registration endpoint
1050    /// would otherwise see that endpoint shadowed by a client id that can never exist. The
1051    /// duplicate-path check in [`ServiceBuilder::build`] compares literal strings and cannot see
1052    /// that case, so the matcher settles it the same way a trie-based router would: a literal
1053    /// segment beats a parameter.
1054    fn resolve<'a>(&self, path: &'a str) -> Option<Route<'a>> {
1055        // A linear walk over at most eleven short strings. A trie would be the right shape for a
1056        // table of hundreds; here it would be more code, more allocation at build time, and
1057        // slower, because the first comparison usually fails on its first byte.
1058        if path == self.well_known {
1059            return Some(Route::Metadata);
1060        }
1061        if path == self.authorize {
1062            return Some(Route::Authorize);
1063        }
1064        if path == self.token {
1065            return Some(Route::Token);
1066        }
1067        if path == self.device {
1068            return Some(Route::Device);
1069        }
1070        if self.introspect.as_deref() == Some(path) {
1071            return Some(Route::Introspect);
1072        }
1073        if self.revoke.as_deref() == Some(path) {
1074            return Some(Route::Revoke);
1075        }
1076        if self.verification.as_deref() == Some(path) {
1077            return Some(Route::Verification);
1078        }
1079        if self.register.as_deref() == Some(path) {
1080            return Some(Route::Register);
1081        }
1082        #[cfg(feature = "par")]
1083        if self.par.as_deref() == Some(path) {
1084            return Some(Route::Par);
1085        }
1086        #[cfg(feature = "jwt")]
1087        if self.jwks.as_deref() == Some(path) {
1088            return Some(Route::Jwks);
1089        }
1090        // ONE segment, and a non-empty one. `crate::registration`'s `registration_client_uri`
1091        // percent-encodes the id into the URL this server itself minted (RFC 7592 s3), and
1092        // `decode_path_segment` undoes exactly that below, so a RAW slash arriving here is a
1093        // different path rather than a client id whose name contains one. Until the 0.9.1 audit
1094        // this comment asserted the encoding while the minting side did not perform it; the
1095        // asymmetry was invisible only because a minted id is 32 hex characters.
1096        if let Some(prefix) = &self.manage {
1097            if let Some(rest) = path.strip_prefix(prefix.as_str()) {
1098                if !rest.is_empty() && !rest.contains('/') {
1099                    return Some(Route::Manage(rest));
1100                }
1101            }
1102        }
1103        None
1104    }
1105}
1106
1107/// The methods a route answers, for the RFC 9110 s15.5.6 `Allow` header a 405 must carry.
1108fn allowed(route: &Route<'_>) -> &'static str {
1109    match route {
1110        // HEAD is listed wherever GET is, because it is served: RFC 9110 s9.3.2 defines it as GET
1111        // with the body dropped, and a client (or a health check) that probes with HEAD must not
1112        // be told the endpoint does not accept it.
1113        Route::Metadata | Route::Authorize => "GET, HEAD",
1114        #[cfg(feature = "jwt")]
1115        Route::Jwks => "GET, HEAD",
1116        Route::Token | Route::Device | Route::Introspect | Route::Revoke | Route::Register => {
1117            "POST"
1118        }
1119        #[cfg(feature = "par")]
1120        Route::Par => "POST",
1121        Route::Verification => "GET, HEAD, POST",
1122        Route::Manage(_) => "GET, HEAD, PUT, DELETE",
1123    }
1124}
1125
1126/// An RFC-shaped authorization server as an HTTP service.
1127///
1128/// Built by [`ServiceBuilder`]. Cheap to clone (one refcount bump) and safe to share, so a host
1129/// clones one per connection or per task without duplicating any of the state or re-serializing
1130/// anything.
1131///
1132/// # Mounting it
1133///
1134/// With the `axum` feature, `axum::Router::from(service)` is the whole wiring. Without it, call
1135/// [`handle`](AuthorizationService::handle) from whatever the host's own server hands it: it takes
1136/// an [`http::Request`] over any [`http_body::Body`] and answers with an
1137/// [`http::Response<Body>`](Response).
1138pub struct AuthorizationService<S: Storage, C: Clock> {
1139    inner: Arc<Inner<S, C>>,
1140}
1141
1142// The route table and nothing else. Every field of `Inner` beyond it is either a host-supplied
1143// closure (which has no useful representation) or bytes already published on the wire, so the
1144// paths are the only part a host debugging a 404 wants to see. Hand written rather than derived
1145// for the same reason `Clone` is, and because a derive would print the whole metadata document.
1146impl<S: Storage, C: Clock> std::fmt::Debug for AuthorizationService<S, C> {
1147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1148        f.debug_struct("AuthorizationService")
1149            .field("routes", &self.inner.routes)
1150            .finish_non_exhaustive()
1151    }
1152}
1153
1154// Hand written rather than derived: `#[derive(Clone)]` would demand `S: Clone` and `C: Clone`,
1155// which is a bound on the HOST's storage that nothing here needs, since the only field is an
1156// `Arc`.
1157impl<S: Storage, C: Clock> Clone for AuthorizationService<S, C> {
1158    fn clone(&self) -> Self {
1159        AuthorizationService {
1160            inner: Arc::clone(&self.inner),
1161        }
1162    }
1163}
1164
1165impl<S: Storage, C: Clock> AuthorizationService<S, C> {
1166    /// Answer one request.
1167    ///
1168    /// Generic over the request body so that a host on any HTTP server can call it: `hyper`,
1169    /// `axum`, a test harness holding a `String`. The body is read whole, up to 64 KiB, before it
1170    /// is parsed, which is what these endpoints require (client
1171    /// authentication for `client_secret_post` is IN the body, so nothing can be checked before
1172    /// it has all arrived).
1173    pub async fn handle<B>(&self, request: Request<B>) -> Response
1174    where
1175        B: http_body::Body,
1176    {
1177        let state = &*self.inner;
1178        let (parts, body) = request.into_parts();
1179        let method = parts.method;
1180        let headers = parts.headers;
1181        let uri = parts.uri;
1182
1183        // THE RAW WIRE PATH, matched byte for byte. The normalisation happens on the other side:
1184        // `ServiceBuilder::build` runs every route through `encode_route_path`, so the table is
1185        // already in the form a client sends. Decoding here instead would make `/%74oken` the
1186        // token endpoint and `/%72egister` the registration endpoint, which is a different string
1187        // to every reverse proxy, ingress rule and WAF in front of this service and the same one
1188        // to this service: a host restricting RFC 7591 registration to an internal network BY PATH
1189        // would be serving it to everyone. Only the captured RFC 7592 id is decoded, and only
1190        // after the route has been decided.
1191        //
1192        // "BYTE FOR BYTE" IS QUALIFIED BY ONE RULE, and only one: RFC 3986 s6.2.2.1 says the
1193        // hexadecimal digits of a percent-encoding are case INSENSITIVE and directs a normaliser
1194        // to prefer the uppercase form, so `%c3` and `%C3` are the same octet and any client
1195        // library, proxy or ingress in the path is entitled to convert between them. Both sides
1196        // are therefore brought to the uppercase form -- the table by `encode_route_path` at build
1197        // time, the wire path by `uppercase_escapes` here -- and compared verbatim after that.
1198        // This is NOT decoding and gives none of decoding's ground away: `%74oken` uppercases to
1199        // `%74oken`, which is still not `/token`, and every rule in front of this service that
1200        // matched the raw path is looking at a string this transformation cannot alter the
1201        // meaning of. It costs a scan for `%` per request and allocates only for a path that
1202        // carries a lowercase escape.
1203        let path = uppercase_escapes(uri.path());
1204        let route = match state.routes.resolve(&path) {
1205            Some(route) => route,
1206            None => return respond(StatusCode::NOT_FOUND, Body::empty()),
1207        };
1208
1209        // RFC 9110 s9.3.2: HEAD is GET with the body suppressed. Handled here, once, rather than
1210        // in eleven handlers: the response is produced exactly as it would have been for GET
1211        // (headers included, `Content-Length` above all) and only the bytes are dropped.
1212        let head = method == Method::HEAD;
1213        let method = match head {
1214            true => Method::GET,
1215            false => method,
1216        };
1217
1218        let mut response = self.dispatch(route, &method, headers, &uri, body).await;
1219        if head {
1220            // The LENGTH is kept and only the bytes are dropped. RFC 9110 s9.3.2 says a HEAD
1221            // response's header fields SHOULD be identical to the GET's, and `Content-Length` is
1222            // the one a cache and a health check actually read; answering zero would tell them
1223            // the representation is empty. Setting it here rather than leaving the full body for
1224            // the transport to suppress means a host that does not special-case HEAD still emits
1225            // a correct response instead of content the RFC forbids.
1226            let length = response.body().size_hint().exact().unwrap_or(0);
1227            *response.body_mut() = Body::empty();
1228            if let Ok(value) = HeaderValue::from_str(&length.to_string()) {
1229                response.headers_mut().insert(header::CONTENT_LENGTH, value);
1230            }
1231        }
1232        response
1233    }
1234
1235    /// The method check and the body read, then the handler.
1236    async fn dispatch<B>(
1237        &self,
1238        route: Route<'_>,
1239        method: &Method,
1240        headers: HeaderMap,
1241        uri: &Uri,
1242        body: B,
1243    ) -> Response
1244    where
1245        B: http_body::Body,
1246    {
1247        let state = &*self.inner;
1248        // Read the body only where a body is read. A GET whose sender attached one is not this
1249        // service's problem, and buffering it would be a memory cost with no reader.
1250        macro_rules! form_body {
1251            () => {
1252                match collect_body(body, MAX_BODY_BYTES).await {
1253                    Ok(bytes) => bytes,
1254                    Err(e) => return body_error(e),
1255                }
1256            };
1257        }
1258        match (route, method.as_str()) {
1259            (Route::Metadata, "GET") => metadata_handler(state),
1260            #[cfg(feature = "jwt")]
1261            (Route::Jwks, "GET") => jwks_handler(state),
1262            (Route::Authorize, "GET") => authorize_handler(state, &headers, uri).await,
1263            (Route::Token, "POST") => token_handler(state, &headers, &form_body!()).await,
1264            (Route::Device, "POST") => {
1265                device_authorization_handler(state, &headers, &form_body!()).await
1266            }
1267            (Route::Introspect, "POST") => introspect_handler(state, &headers, &form_body!()).await,
1268            (Route::Revoke, "POST") => revoke_handler(state, &headers, &form_body!()).await,
1269            #[cfg(feature = "par")]
1270            (Route::Par, "POST") => {
1271                pushed_authorization_handler(state, &headers, &form_body!()).await
1272            }
1273            (Route::Verification, "GET") => verification_page_handler(state, &headers, uri).await,
1274            (Route::Verification, "POST") => {
1275                verification_submit_handler(state, &headers, &form_body!()).await
1276            }
1277            (Route::Register, "POST") => register_handler(state, &headers, &form_body!()).await,
1278            // The captured segment arrives RAW, because the route was decided on the wire path
1279            // (see `handle`), and it is decoded here: a client id may contain characters a path
1280            // segment reserves, and `registration_client_uri` percent-encodes them into the URL
1281            // this server minted (RFC 7592 s3), so this is the other half of that round trip. It
1282            // is the ONE decode this module performs on a path, and it happens after the route is
1283            // settled, so it can never turn one route into another.
1284            (Route::Manage(client_id), "GET") => {
1285                read_registration_handler(state, &headers, &decode_path_segment(client_id)).await
1286            }
1287            (Route::Manage(client_id), "PUT") => {
1288                let id = decode_path_segment(client_id).into_owned();
1289                update_registration_handler(state, &headers, &id, &form_body!()).await
1290            }
1291            (Route::Manage(client_id), "DELETE") => {
1292                delete_registration_handler(state, &headers, &decode_path_segment(client_id)).await
1293            }
1294            // RFC 9110 s15.5.6: a 405 MUST carry `Allow`. Without it a client cannot tell a
1295            // wrong method from a route that does not exist.
1296            (route, _) => {
1297                let mut resp = respond(StatusCode::METHOD_NOT_ALLOWED, Body::empty());
1298                resp.headers_mut()
1299                    .insert(header::ALLOW, HeaderValue::from_static(allowed(&route)));
1300                resp
1301            }
1302        }
1303    }
1304}
1305
1306/// Why a request body could not be read.
1307enum BodyError {
1308    /// It exceeded [`MAX_BODY_BYTES`].
1309    TooLarge,
1310    /// The transport gave up: a truncated body, a broken connection, a bad chunk encoding.
1311    Incomplete,
1312}
1313
1314/// The answer to a body that could not be read.
1315///
1316/// Not an RFC 6749 s5.2 error body, and deliberately not: section 5.2 describes what the server
1317/// says about a REQUEST it managed to parse, and neither of these got that far. A 413 and a 400
1318/// are what an HTTP client (and every proxy between it and here) already understands.
1319fn body_error(e: BodyError) -> Response {
1320    match e {
1321        BodyError::TooLarge => text_response(
1322            StatusCode::PAYLOAD_TOO_LARGE,
1323            "request body exceeds this server's limit",
1324        ),
1325        BodyError::Incomplete => {
1326            text_response(StatusCode::BAD_REQUEST, "request body was not received")
1327        }
1328    }
1329}
1330
1331/// Read a request body whole, refusing at `limit` bytes.
1332///
1333/// The cap is checked TWICE and both checks are needed. The size hint catches a declared
1334/// `Content-Length` before a single byte is buffered, which is what makes a hostile
1335/// `Content-Length: 4000000000` cost nothing; the running total catches a chunked body that
1336/// declares nothing and just keeps sending, which is the case the first check cannot see.
1337async fn collect_body<B>(body: B, limit: usize) -> Result<Bytes, BodyError>
1338where
1339    B: http_body::Body,
1340{
1341    let hint = body.size_hint();
1342    if hint.lower() > limit as u64 {
1343        return Err(BodyError::TooLarge);
1344    }
1345    // Sized from the hint when there is one, so the common case (a form body with a
1346    // `Content-Length`) allocates exactly once. Clamped to the limit so the hint cannot itself be
1347    // the allocation primitive.
1348    let expected = hint.upper().unwrap_or(hint.lower()).min(limit as u64) as usize;
1349    let mut collected: Vec<u8> = Vec::with_capacity(expected);
1350
1351    // Pinned on the stack: `poll_frame` needs `Pin<&mut B>` and `B` is not required to be
1352    // `Unpin`, so boxing would be the only alternative and it would allocate on every request.
1353    let mut body = std::pin::pin!(body);
1354    loop {
1355        match std::future::poll_fn(|cx| body.as_mut().poll_frame(cx)).await {
1356            None => break,
1357            Some(Err(_)) => return Err(BodyError::Incomplete),
1358            Some(Ok(frame)) => {
1359                // Trailers carry no request content. `into_data` hands them back rather than
1360                // panicking, and they are dropped.
1361                if let Ok(mut data) = frame.into_data() {
1362                    if collected.len().saturating_add(data.remaining()) > limit {
1363                        return Err(BodyError::TooLarge);
1364                    }
1365                    while data.has_remaining() {
1366                        let chunk = data.chunk();
1367                        collected.extend_from_slice(chunk);
1368                        let n = chunk.len();
1369                        data.advance(n);
1370                    }
1371                }
1372            }
1373        }
1374    }
1375    Ok(Bytes::from(collected))
1376}
1377
1378// ---------------------------------------------------------------------------------------------
1379// The axum adapter, behind the `axum` cargo feature
1380// ---------------------------------------------------------------------------------------------
1381
1382/// Mount the service on axum.
1383///
1384/// This is the ENTIRE axum surface of this crate, and it is one function on purpose. axum is a
1385/// 0.x crate: its major has moved before and will move again, and every earlier version of this
1386/// module put `axum::Router` in the return type of the only way to use the `http` feature, which
1387/// meant a host on a different axum major could not enable the feature at all. Confining axum to
1388/// an adapter behind its own feature makes that a per-host decision instead of this crate's.
1389///
1390/// A `fallback` rather than a route per endpoint: the route table is DERIVED from the metadata
1391/// document at build time, so re-declaring it here in axum's syntax would create
1392/// a second table that could disagree with the first. A 404 from
1393/// [`AuthorizationService::handle`] is a path this server does not serve, which is exactly what a
1394/// fallback means.
1395///
1396/// # Why the request is answered on a spawned task
1397///
1398/// BECAUSE A CLIENT THAT HANGS UP MUST NOT BE ABLE TO STOP THE SERVER MID-SEQUENCE. hyper drops
1399/// the service future when the connection closes, and a dropped future does not fail: the code
1400/// after the `.await` it was suspended on simply never runs. `crate::server` has no transactions
1401/// (`Storage` deliberately offers none), so several of its sequences are an atomic TAKE followed
1402/// by a write, and every one of those arguments about which way the pair fails assumes that a
1403/// failure HAPPENS. The refresh rotation is the sharp case: `Storage::take_refresh_token` removes
1404/// the chain and the spent marker that arms RFC 9700 s4.14.2 reuse detection is written after it,
1405/// so a drop in between leaves the chain gone with no marker, which is the exact state that
1406/// ordering exists to prevent. The authorization code path has the same shape with its consumed
1407/// record. Neither is a race an attacker has to win by timing: whoever presents the credential is
1408/// whoever decides when to close the socket.
1409///
1410/// Awaiting a `JoinHandle` moves the cancellation to the RIGHT place. The client's disconnect
1411/// cancels this adapter's await on the handle; the spawned task keeps its own place in the runtime
1412/// and runs the store sequence to the end. Nothing else in this crate can do this, because the
1413/// `http` feature deliberately pulls in no runtime; the `axum` feature is the one place a runtime
1414/// is already present (`axum = ["http", "dep:axum", "dep:tokio"]`), so it is the one place this can
1415/// be contained. A HOST MOUNTING [`AuthorizationService::handle`] ITSELF OWNS THIS, and should
1416/// spawn for the same reason.
1417///
1418/// # What a host may notice
1419///
1420/// IN-FLIGHT WORK IS NO LONGER BOUNDED BY CONNECTIONS. That is the point of the spawn and it is
1421/// also its cost: a client that hangs up stops waiting but no longer stops the work, so axum's and
1422/// hyper's connection limits, and any accept-side bound the host set, no longer bound the tasks
1423/// this service is running. The bound becomes request RATE times handler latency, and NEITHER
1424/// FACTOR IS THIS CRATE'S TO SET.
1425///
1426/// The rate half is the stronger one: the limiter runs INSIDE [`AuthorizationService::handle`] and
1427/// refuses before the store is touched, so a refused request costs a spawn and nothing more. It is
1428/// still not a global ceiling — the budgets [`crate::rate_limit`] ships for the endpoints that
1429/// name a client are keyed per `client_id`, which RFC 6749 section 2.2 makes public, so a caller
1430/// spraying identifiers gets a budget apiece up to
1431/// [`crate::rate_limit::DEFAULT_MAX_TRACKED_CLIENTS`] counters before the rest share an overflow
1432/// counter.
1433///
1434/// The latency half is the host's outright. A handler makes a bounded NUMBER of store calls, but
1435/// each one is the host's [`crate::store::Storage`] and this crate sets no timeout anywhere, on
1436/// anything; the token path additionally awaits [`crate::jwt::Es256Signer`], which that trait's
1437/// own docs say may be a network round trip to a KMS. Nor is the latency all waiting:
1438/// a host-installed [`crate::client::SecretVerifier`] runs its KDF INLINE on the executor thread
1439/// polling the request — that trait prices argon2id at ordinary parameters at roughly 200 ms, paid
1440/// per token request and on the unknown-client path too — so it occupies a worker rather than
1441/// yielding it. A host that wants a hard
1442/// ceiling should take a semaphore permit before the spawn, or spawn into a `JoinSet` it owns, and
1443/// answer 503 when it cannot get one.
1444///
1445/// A PANIC in a handler no longer unwinds into hyper. It arrives here as a `JoinError` and is
1446/// answered with an empty 500, which is what the panicking connection produced anyway, minus the
1447/// connection dying with it. RUNTIME SHUTDOWN is the other `JoinError`: a task cancelled because
1448/// its runtime is going away answers the same 500. The two are not distinguished on the wire on
1449/// purpose, because they are the same news to the client (this request did not complete and it
1450/// does not know whether anything happened), and both are already visible to the host: a panic
1451/// through its own hook, a shutdown because it asked for one.
1452///
1453/// # Cost
1454///
1455/// One `tokio::spawn` per request, which is ONE allocation: measured with `tests/support/alloc.rs`
1456/// on aarch64-apple-darwin at 1 alloc and 128 bytes for a trivial task, the block sized by the task
1457/// header plus the handler future. `tests/allocation.rs` budgets the REQUEST path, which this does
1458/// not touch: nothing inside [`AuthorizationService::handle`] changes, and the token endpoint's own
1459/// budget there is two orders of magnitude larger than one task. It buys the store sequence the
1460/// right to finish.
1461///
1462/// The other half of the cost is not an allocation. Detaching the handler from the connection
1463/// means in-flight work is bounded by request RATE rather than by concurrent connections: a client
1464/// that disconnects immediately after sending no longer sheds any load, because the handler it
1465/// started runs to completion regardless. That is the same property that buys the store sequence
1466/// its right to finish, seen from the load side. A host that relied on disconnects for
1467/// backpressure needs a concurrency limit in front of this service — `tower::limit` or the
1468/// equivalent — and the rate limiter this crate already has does not substitute for one, because
1469/// it refuses attempts rather than bounding work already accepted.
1470#[cfg(feature = "axum")]
1471impl<S, C> From<AuthorizationService<S, C>> for axum::Router
1472where
1473    S: Storage + Send + Sync + 'static,
1474    C: Clock + Send + Sync + 'static,
1475{
1476    fn from(service: AuthorizationService<S, C>) -> axum::Router {
1477        axum::Router::new().fallback(move |request: axum::extract::Request| {
1478            let service = service.clone();
1479            async move {
1480                match tokio::spawn(async move { service.handle(request).await }).await {
1481                    // `Body` is already a complete `Bytes`, so this is a move, not a copy or a
1482                    // stream adapter.
1483                    Ok(response) => response.map(|body| axum::body::Body::from(body.into_bytes())),
1484                    // A panic, or a runtime being torn down. No body: there is nothing this
1485                    // service knows about the failure that a client could act on, and every
1486                    // endpoint here answers a different content type, so an invented JSON error
1487                    // would be a guess about which one this request wanted.
1488                    Err(_) => {
1489                        let mut response = axum::http::Response::new(axum::body::Body::empty());
1490                        *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
1491                        response
1492                    }
1493                }
1494            }
1495        })
1496    }
1497}
1498
1499// ---------------------------------------------------------------------------------------------
1500// Wire helpers
1501// ---------------------------------------------------------------------------------------------
1502
1503fn json_content_type() -> HeaderValue {
1504    // RFC 6749 s5.1: "application/json;charset=UTF-8".
1505    HeaderValue::from_static("application/json;charset=UTF-8")
1506}
1507
1508/// RFC 7517 s8.5.1 registers `application/jwk-set+json` for a JWK Set, which is what this is; a
1509/// verifier that only checks for a JSON suffix still accepts it.
1510#[cfg(feature = "jwt")]
1511fn jwks_content_type() -> HeaderValue {
1512    HeaderValue::from_static("application/jwk-set+json")
1513}
1514
1515fn html_content_type() -> HeaderValue {
1516    HeaderValue::from_static("text/html;charset=UTF-8")
1517}
1518
1519/// Serialize a wire type. A serialization failure cannot happen for these shapes (they are plain
1520/// structs of strings and numbers), but a library must not panic inside a host's request path, so
1521/// the fallback is a valid RFC 6749 s5.2 body rather than an `unwrap`.
1522fn json_body<T: Serialize>(value: &T) -> Vec<u8> {
1523    serde_json::to_vec(value).unwrap_or_else(|_| br#"{"error":"server_error"}"#.to_vec())
1524}
1525
1526/// Stamp the RFC 6749 s5.1 caching directives onto a token-plane response.
1527///
1528/// The bodies carry bearer credentials. A shared cache that stores one hands it to whoever asks
1529/// next, which is why the RFC makes this a MUST rather than advice. `Pragma: no-cache` is the
1530/// HTTP/1.0 belt to `no-store`'s braces, and the RFC names both.
1531fn no_store(headers: &mut HeaderMap) {
1532    headers.insert(
1533        header::CACHE_CONTROL,
1534        HeaderValue::from_static("no-store, no-cache, max-age=0"),
1535    );
1536    headers.insert(header::PRAGMA, HeaderValue::from_static("no-cache"));
1537}
1538
1539/// A successful JSON response on the token plane.
1540fn ok_json<T: Serialize>(value: &T) -> Response {
1541    let mut resp = respond(StatusCode::OK, json_body(value));
1542    let headers = resp.headers_mut();
1543    headers.insert(header::CONTENT_TYPE, json_content_type());
1544    no_store(headers);
1545    resp
1546}
1547
1548/// An RFC 6749 s5.2 error response.
1549///
1550/// `via_header` records whether the client presented credentials in the `Authorization` header,
1551/// and it is the ONLY thing that decides between 400 and 401. Section 5.2 mandates 401 exactly
1552/// when header authentication was attempted and failed, and says the server MAY use 401
1553/// otherwise. It does not, because RFC 9110 s15.5.2 requires every 401 to carry a challenge, and
1554/// challenging a client that never offered header credentials tells it to retry a scheme it did
1555/// not choose. So header failures get 401 plus `WWW-Authenticate`, and everything else gets 400.
1556fn error_response(err: &ErrorResponse, via_header: bool, challenge: &HeaderValue) -> Response {
1557    let mut status =
1558        StatusCode::from_u16(err.http_status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
1559    if status == StatusCode::UNAUTHORIZED && !via_header {
1560        status = StatusCode::BAD_REQUEST;
1561    }
1562    let mut resp = respond(status, json_body(err));
1563    let headers = resp.headers_mut();
1564    headers.insert(header::CONTENT_TYPE, json_content_type());
1565    no_store(headers);
1566    if status == StatusCode::UNAUTHORIZED {
1567        headers.insert(header::WWW_AUTHENTICATE, challenge.clone());
1568    }
1569    resp
1570}
1571
1572/// The RFC 8628 verification page, and the only HTML this server emits.
1573///
1574/// It carries more headers than any other response here because it is the only one a BROWSER
1575/// renders, and the only one whose defences a browser can be tricked into satisfying on the user's
1576/// behalf.
1577///
1578/// FRAMING. The page's CSRF defence is `Sec-Fetch-Site: same-origin` (see
1579/// `same_origin_submission`), and a document inside a cross-site iframe posting to its own origin
1580/// sends exactly that. So without a framing refusal the whole defence is decorative against a
1581/// clickjack: an attacker frames the page invisibly, starts a device flow of their own, and lands
1582/// the user's click on Approve. `frame-ancestors 'none'` is the standard's answer and
1583/// `X-Frame-Options: DENY` is the one older browsers obey; both are sent because they are
1584/// enforced by different code paths and neither supersedes the other everywhere.
1585///
1586/// CACHING. The body carries a live single-use CSRF token and the details of a third party's
1587/// pending grant. This was the one response in this file that did not call `no_store`, which
1588/// meant a shared cache, or a browser's back button, could re-serve another user's approval form.
1589///
1590/// The rest is the ordinary hardening for a page with no scripts, no styles, no images and one
1591/// same-origin form: `default-src 'none'` (nothing may be loaded), `form-action 'self'` (the
1592/// submission cannot be redirected off-origin by injected markup), `base-uri 'none'` (a `<base>`
1593/// cannot relocate the relative form action), `nosniff`, and `no-referrer` so the user code in
1594/// the deep-link URL is not handed to a third party.
1595fn html_response(status: StatusCode, body: String) -> Response {
1596    let mut resp = respond(status, body);
1597    let headers = resp.headers_mut();
1598    headers.insert(header::CONTENT_TYPE, html_content_type());
1599    no_store(headers);
1600    headers.insert(
1601        header::CONTENT_SECURITY_POLICY,
1602        HeaderValue::from_static(
1603            "default-src 'none'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'",
1604        ),
1605    );
1606    headers.insert(header::X_FRAME_OPTIONS, HeaderValue::from_static("DENY"));
1607    headers.insert(
1608        header::X_CONTENT_TYPE_OPTIONS,
1609        HeaderValue::from_static("nosniff"),
1610    );
1611    headers.insert(
1612        header::REFERRER_POLICY,
1613        HeaderValue::from_static("no-referrer"),
1614    );
1615    resp
1616}
1617
1618// ---------------------------------------------------------------------------------------------
1619// application/x-www-form-urlencoded, in both the body and the query string
1620// ---------------------------------------------------------------------------------------------
1621
1622fn hex_value(b: u8) -> Option<u8> {
1623    match b {
1624        b'0'..=b'9' => Some(b - b'0'),
1625        b'a'..=b'f' => Some(b - b'a' + 10),
1626        b'A'..=b'F' => Some(b - b'A' + 10),
1627        _ => None,
1628    }
1629}
1630
1631/// Decode one `application/x-www-form-urlencoded` component.
1632///
1633/// Borrows when there is nothing to decode, which is the common case for `grant_type`, `code`,
1634/// and every opaque token this server issues (hex and base64url need no escaping). Only a value
1635/// that actually contains `%` or `+` costs an allocation.
1636fn decode_component(raw: &str) -> Cow<'_, str> {
1637    percent_decode(raw, true)
1638}
1639
1640/// Decode ONE path segment: percent escapes only.
1641///
1642/// A `+` in a path segment is a literal plus (RFC 3986 s3.3 puts it in `sub-delims`); only
1643/// `application/x-www-form-urlencoded` gives it the "space" meaning. Decoding it as a space here
1644/// would rewrite the RFC 7592 s3 `registration_client_uri` this server itself minted, and a client
1645/// whose id contains a plus would find its own management URL pointing at a different client.
1646///
1647/// ONE SEGMENT, never the whole path. For a few days of the 0.9.1 audit this crate decoded the
1648/// entire request path before matching it, to make a raw non-ASCII issuer routable, and the price
1649/// was two defects at once: `/%74oken` became the token endpoint and `/%72egister` became the RFC
1650/// 7591 registration endpoint, under every reverse proxy, ingress rule and WAF that had matched
1651/// the RAW path and seen no such string; and an issuer spelled the way RFC 3986 section 3.3
1652/// requires (`https://as.example/tenant%20a`) stopped routing at all, because
1653/// [`endpoint_path`] holds the issuer verbatim. Both directions are fixed in the ROUTE TABLE
1654/// instead, by [`encode_route_path`]: the table is normalised into wire form once at build time
1655/// and the wire path is compared byte for byte, which is what every layer in front of this service
1656/// is also doing.
1657fn decode_path_segment(raw: &str) -> Cow<'_, str> {
1658    percent_decode(raw, false)
1659}
1660
1661/// Normalise a route-table path into the form a client puts on the wire.
1662///
1663/// The table is derived from the issuer AS THE HOST CONFIGURED IT (see [`endpoint_path`]), and a
1664/// host may legitimately configure either of two spellings for a path a client must escape: the
1665/// RFC 3986 section 3.3 one, `https://as.example/tenant%20a`, which is a legal URI, or a raw
1666/// non-ASCII one, `https://as.example/\u{e9}`, which is not but which this crate accepts and
1667/// `tests/issuer_origin_boundary.rs` pins as buildable. A client fetching from either sends the
1668/// same bytes: percent-encoded ones. So the table is brought to that form ONCE, here, and the
1669/// matcher never touches the wire path.
1670///
1671/// `%` IS LEFT ALONE, which is what makes the first spelling survive: an issuer that already
1672/// carries escapes passes through unchanged rather than being encoded a second time into `%2520`.
1673/// The cost is that a literal `%` in an issuer that is not an escape cannot be expressed, which is
1674/// a URI that is malformed under section 2.1 anyway.
1675///
1676/// Left alone EXCEPT FOR THE CASE OF ITS TWO HEX DIGITS, which are uppercased. RFC 3986 s6.2.2.1
1677/// makes those digits case insensitive and directs a normaliser to prefer the uppercase form, so
1678/// `https://as.example/caf%c3%a9` and `https://as.example/caf%C3%A9` are the same issuer and a
1679/// client, proxy or ingress that normalises sends the uppercase one whichever the document
1680/// carried. Uppercasing here is what makes the table CANONICAL rather than a copy of one host's
1681/// spelling; `uppercase_escapes` does the same to the wire path, and the comparison between the
1682/// two is still byte for byte. A `%` NOT followed by two hex digits is not an escape at all and
1683/// keeps the pass-through behaviour above.
1684///
1685/// Run once per route at build time, so its cost is not on any request path.
1686fn encode_route_path(path: &str) -> String {
1687    const HEX: &[u8; 16] = b"0123456789ABCDEF";
1688    let bytes = path.as_bytes();
1689    let mut out = String::with_capacity(path.len());
1690    let mut skip = 0usize;
1691    for (i, &b) in bytes.iter().enumerate() {
1692        if skip > 0 {
1693            skip -= 1;
1694            continue;
1695        }
1696        if b == b'%' {
1697            if let (Some(&h), Some(&l)) = (bytes.get(i + 1), bytes.get(i + 2)) {
1698                if hex_value(h).is_some() && hex_value(l).is_some() {
1699                    out.push('%');
1700                    out.push(h.to_ascii_uppercase() as char);
1701                    out.push(l.to_ascii_uppercase() as char);
1702                    skip = 2;
1703                    continue;
1704                }
1705            }
1706        }
1707        // RFC 3986 s3.3 `pchar` (unreserved / sub-delims / ":" / "@"), plus the separator itself
1708        // and the escape introducer.
1709        let verbatim = b.is_ascii_alphanumeric()
1710            || matches!(
1711                b,
1712                b'-' | b'.'
1713                    | b'_'
1714                    | b'~'
1715                    | b'!'
1716                    | b'$'
1717                    | b'&'
1718                    | b'\''
1719                    | b'('
1720                    | b')'
1721                    | b'*'
1722                    | b'+'
1723                    | b','
1724                    | b';'
1725                    | b'='
1726                    | b':'
1727                    | b'@'
1728                    | b'/'
1729                    | b'%'
1730            );
1731        if verbatim {
1732            out.push(b as char);
1733        } else {
1734            out.push('%');
1735            out.push(HEX[(b >> 4) as usize] as char);
1736            out.push(HEX[(b & 0x0f) as usize] as char);
1737        }
1738    }
1739    out
1740}
1741
1742/// The wire half of [`encode_route_path`]'s normalisation: uppercase the hex digits of every
1743/// percent-encoding in a path and change nothing else.
1744///
1745/// RFC 3986 s6.2.2.1 defines this as case normalisation and it is the only transformation this
1746/// service applies to a path before matching it. It is NOT decoding: the number of characters is
1747/// unchanged, `%74oken` stays `%74oken`, and every rule in front of this service that matched on
1748/// the raw path is matching a string this cannot alter. Doing it on both sides is what lets a host
1749/// spell its issuer's escapes either way and a client normalise or not: all four combinations meet
1750/// in the same canonical form, and a table normalised alone would have swapped one broken pairing
1751/// for another.
1752///
1753/// Borrows unless the path actually carries a lowercase escape, so an ASCII route (every route, in
1754/// every deployment that does not put an escape in its issuer) allocates nothing and pays one scan
1755/// for `%`.
1756fn uppercase_escapes(path: &str) -> Cow<'_, str> {
1757    let bytes = path.as_bytes();
1758    let needs = bytes.iter().enumerate().any(|(i, &b)| {
1759        b == b'%'
1760            && matches!(
1761                (bytes.get(i + 1), bytes.get(i + 2)),
1762                (Some(&h), Some(&l))
1763                    if hex_value(h).is_some()
1764                        && hex_value(l).is_some()
1765                        && (h.is_ascii_lowercase() || l.is_ascii_lowercase())
1766            )
1767    });
1768    if !needs {
1769        return Cow::Borrowed(path);
1770    }
1771    // Written as its own loop rather than as a call to `encode_route_path`: that function also
1772    // ESCAPES what is not a `pchar`, which is right for a path a host configured and wrong for one
1773    // that arrived on the wire, where anything outside the grammar is the client's problem and not
1774    // something this service should quietly rewrite into a route.
1775    //
1776    // Copied in RUNS between escapes rather than byte by byte, which keeps it correct for a
1777    // multi-byte character (`%` is ASCII, so every index this slices at is a character boundary)
1778    // as well as cheaper.
1779    let mut out = String::with_capacity(path.len());
1780    let mut i = 0;
1781    while i < bytes.len() {
1782        match (bytes[i], bytes.get(i + 1), bytes.get(i + 2)) {
1783            (b'%', Some(&h), Some(&l)) if hex_value(h).is_some() && hex_value(l).is_some() => {
1784                out.push('%');
1785                out.push(h.to_ascii_uppercase() as char);
1786                out.push(l.to_ascii_uppercase() as char);
1787                i += 3;
1788            }
1789            _ => {
1790                let start = i;
1791                i += 1;
1792                while i < bytes.len() && bytes[i] != b'%' {
1793                    i += 1;
1794                }
1795                out.push_str(&path[start..i]);
1796            }
1797        }
1798    }
1799    Cow::Owned(out)
1800}
1801
1802/// The shared decoder. Borrows when there is nothing to unescape, which is what keeps the common
1803/// case free.
1804fn percent_decode(raw: &str, plus_is_space: bool) -> Cow<'_, str> {
1805    if !raw
1806        .bytes()
1807        .any(|b| b == b'%' || (plus_is_space && b == b'+'))
1808    {
1809        return Cow::Borrowed(raw);
1810    }
1811    let bytes = raw.as_bytes();
1812    let mut out = Vec::with_capacity(bytes.len());
1813    let mut i = 0;
1814    while i < bytes.len() {
1815        match bytes[i] {
1816            b'+' if plus_is_space => {
1817                out.push(b' ');
1818                i += 1;
1819            }
1820            b'%' if i + 2 < bytes.len() => {
1821                match hex_pair(hex_value(bytes[i + 1]), hex_value(bytes[i + 2])) {
1822                    Some((h, l)) => {
1823                        out.push((h << 4) | l);
1824                        i += 3;
1825                    }
1826                    None => {
1827                        // A stray `%` is not an escape. Passing it through unchanged keeps the
1828                        // value intact for the comparison that will reject it anyway, rather
1829                        // than failing the whole request on a byte we do not need to understand.
1830                        out.push(b'%');
1831                        i += 1;
1832                    }
1833                }
1834            }
1835            b => {
1836                out.push(b);
1837                i += 1;
1838            }
1839        }
1840    }
1841    // Lossy on purpose: a form field that is not UTF-8 cannot match any registered client id,
1842    // token, or scope, so it will be refused a moment later on its merits. Rejecting the whole
1843    // request here would just replace a precise OAuth error with a vague one.
1844    Cow::Owned(match String::from_utf8(out) {
1845        Ok(s) => s,
1846        Err(e) => String::from_utf8_lossy(e.as_bytes()).into_owned(),
1847    })
1848}
1849
1850/// `Some((high, low))` only when both nibbles are hex.
1851fn hex_pair(a: Option<u8>, b: Option<u8>) -> Option<(u8, u8)> {
1852    match (a, b) {
1853        (Some(h), Some(l)) => Some((h, l)),
1854        _ => None,
1855    }
1856}
1857
1858type Pair<'a> = (Cow<'a, str>, Cow<'a, str>);
1859
1860/// A request carrying more parameters than [`MAX_FORM_PARAMETERS`], refused before it is decoded.
1861struct TooManyParameters;
1862
1863/// The answer to one of those.
1864///
1865/// A bare 413 rather than an RFC 6749 s5.2 error body, for exactly the reason [`body_error`] is
1866/// one: nothing has been parsed, so there is no `grant_type`, no authenticated client and no
1867/// validated redirect URI to shape a protocol error around, and 413 is what every proxy between
1868/// here and the caller already understands. It says PAYLOAD even when the parameters came from a
1869/// query string, because the payload being refused is the parameter list; 414 would assert the
1870/// URI was too long in BYTES, which it need not be.
1871fn too_many_parameters() -> Response {
1872    text_response(
1873        StatusCode::PAYLOAD_TOO_LARGE,
1874        "request carries too many parameters",
1875    )
1876}
1877
1878/// A refusal that happens BEFORE the request is parsed far enough to have an OAuth error code.
1879///
1880/// These are the only responses in this file that carry bytes without an RFC 6749 s5.2 JSON body:
1881/// the body cap and the parameter cap both fire on the raw request, where there is no `grant_type`
1882/// and no `client_id` to name, and inventing an OAuth error for them would be a claim about a
1883/// request this server never read. They still need a `Content-Type` (RFC 9110 s8.3) or the client
1884/// cannot decode the sentence explaining what happened — which is what they are for. The payloads
1885/// are fixed ASCII literals with no attacker-controlled substring, so `text/plain` is safe here in
1886/// a way it would not be for anything echoing input.
1887fn text_response(status: StatusCode, body: &'static str) -> Response {
1888    let mut resp = respond(status, body);
1889    resp.headers_mut().insert(
1890        header::CONTENT_TYPE,
1891        HeaderValue::from_static("text/plain;charset=UTF-8"),
1892    );
1893    resp
1894}
1895
1896/// Split a form body or query string into decoded pairs. A parameter with no `=` is kept with an
1897/// empty value, which is how a client spells "present but empty" and must not be mistaken for
1898/// absent.
1899/// Sized up front rather than grown. `Split` has no `size_hint`, so `collect` starts from nothing
1900/// and doubles: a six-parameter token body reallocates three times and memcpys 64 bytes per pair
1901/// each time. Counting the separators is one linear pass over bytes that are about to be walked
1902/// anyway, and it is an exact upper bound (empty segments are filtered out, so it can only
1903/// overshoot). This runs on EVERY routed request, which is what makes a free win worth taking.
1904///
1905/// THE SAME PASS ENFORCES [`MAX_FORM_PARAMETERS`], and it is the reason the count is taken here
1906/// rather than at the eight call sites: decoding is per parameter, so a cap any one caller could
1907/// forget to apply is not a cap. The loop returns at the separator that crosses the ceiling, so a
1908/// 64 KiB body of junk is refused after reading the first few hundred bytes of it and allocating
1909/// nothing at all. Separators rather than parameters is a conservative over-count (`a&&&&b` is two
1910/// parameters and five segments), which is the right direction for a bound whose only job is to
1911/// stop absurd requests: nothing legitimate sends empty segments.
1912fn parse_pairs(input: &str) -> Result<Vec<Pair<'_>>, TooManyParameters> {
1913    let mut separators = 0usize;
1914    for b in input.bytes() {
1915        if b == b'&' {
1916            separators += 1;
1917            if separators >= MAX_FORM_PARAMETERS {
1918                return Err(TooManyParameters);
1919            }
1920        }
1921    }
1922    let bound = separators + 1;
1923    let mut pairs = Vec::with_capacity(bound);
1924    pairs.extend(
1925        input
1926            .split('&')
1927            .filter(|part| !part.is_empty())
1928            .map(|part| match part.split_once('=') {
1929                Some((k, v)) => (decode_component(k), decode_component(v)),
1930                None => (decode_component(part), Cow::Borrowed("")),
1931            }),
1932    );
1933    Ok(pairs)
1934}
1935
1936/// The FIRST occurrence of a parameter.
1937///
1938/// RFC 6749 s3.1 says a parameter MUST NOT be sent more than once. First-wins rather than
1939/// last-wins is the deliberate choice: when two intermediaries disagree about which copy counts,
1940/// last-wins is the one that lets a smuggled duplicate override what the earlier layers saw.
1941fn param<'a>(pairs: &'a [Pair<'a>], name: &str) -> Option<&'a str> {
1942    pairs
1943        .iter()
1944        .find(|(k, _)| k == name)
1945        .map(|(_, v)| v.as_ref())
1946}
1947
1948/// A required parameter, or the RFC 6749 s5.2 `invalid_request` naming it.
1949///
1950/// The description is BORROWED from the table below rather than formatted, and the reason is the
1951/// rule `tests/allocation.rs` states on `refused_token_request_allocation_bound`: a refusal is
1952/// work an attacker sets the rate of, so a refusal that allocates is an allocation anyone who can
1953/// open a socket may ask for at whatever rate they like. Every other refusal in this file already
1954/// passes a literal into `error_description`, which is a `Cow<'static, str>`; this one formatted,
1955/// although `name` is a `&'static str` drawn from the finite set of parameters the endpoints
1956/// below actually demand. `src/tests/http.rs` reads that set out of this file's source and fails
1957/// if a call site's name is missing from the table, so the fallback cannot quietly become the
1958/// common case.
1959fn required<'a>(pairs: &'a [Pair<'a>], name: &'static str) -> Result<&'a str, ErrorResponse> {
1960    param(pairs, name).ok_or_else(|| {
1961        let description: Cow<'static, str> = match name {
1962            "code" => Cow::Borrowed("missing required parameter code"),
1963            "device_code" => Cow::Borrowed("missing required parameter device_code"),
1964            "refresh_token" => Cow::Borrowed("missing required parameter refresh_token"),
1965            "token" => Cow::Borrowed("missing required parameter token"),
1966            "subject_token" => Cow::Borrowed("missing required parameter subject_token"),
1967            "subject_token_type" => Cow::Borrowed("missing required parameter subject_token_type"),
1968            // Unreachable from this file today, and kept total rather than made a panic: a
1969            // refusal is the wrong place to introduce a way for the process to die.
1970            other => Cow::Owned(format!("missing required parameter {other}")),
1971        };
1972        ErrorResponse::new(ErrorCode::InvalidRequest).with_description(description)
1973    })
1974}
1975
1976/// Every `resource` parameter, in wire order (RFC 8707 s2 permits repetition, so this is the one
1977/// parameter [`param`]'s first-wins rule must not be applied to: dropping the second occurrence
1978/// would silently issue a token for half of what the client asked for).
1979fn resource_indicators(pairs: &[Pair<'_>]) -> Vec<String> {
1980    pairs
1981        .iter()
1982        .filter(|(k, _)| k == "resource")
1983        .map(|(_, v)| v.as_ref().to_string())
1984        .collect()
1985}
1986
1987/// RFC 9396 s5 for the two doors whose grant has NOWHERE to carry an authorization detail, in any
1988/// build: the RFC 8628 device authorization request and the RFC 8693 token exchange grant.
1989///
1990/// Every other door refuses in the core, gated on `not(feature = "rar")`, because the core has an
1991/// argument the parameter arrives in and can therefore see it. These two do not:
1992/// `device_authorization_with_credential` takes a scope and nothing else, and
1993/// [`crate::token_exchange::TokenExchangeRequest`] derives what it issues from the SUBJECT token,
1994/// so the parameter dies in this router unless this router answers it. That made the same POST to
1995/// the same `/token` URL refuse for `authorization_code` and silently drop for
1996/// `grant_type=...:token-exchange`.
1997///
1998/// UNGATED, unlike the core's refusals, and the difference is what is being refused. There the
1999/// answer turns on whether the build supports any detail TYPE; here it turns on the GRANT, which
2000/// has no field for a detail whether the type is supported or not. `AuthorizationServer::token`
2001/// already refuses to mint detail for a device grant under `rar` for exactly this reason; this is
2002/// that refusal moved to the door the client knocks on, where it can still be told which parameter
2003/// was wrong instead of receiving codes and discovering the omission at the resource server.
2004///
2005/// Checked BEFORE the credential and before the grant, like the DPoP proof this router refuses on
2006/// the same grant: a client asking for something this server cannot do is a wiring mistake, and
2007/// the refusal is only useful if it names the parameter rather than whatever was checked first.
2008fn refuse_authorization_details(pairs: &[Pair<'_>]) -> Option<ErrorResponse> {
2009    param(pairs, "authorization_details").map(|_| {
2010        // The VALUE is never echoed (RFC 6749 s5.2 restricts the charset, and this one is
2011        // attacker-supplied JSON); naming the parameter is what the developer who sent it needs.
2012        ErrorResponse::new(ErrorCode::InvalidAuthorizationDetails)
2013            .with_description("this server does not accept authorization_details on this grant")
2014    })
2015}
2016
2017/// Parse an optional `scope` parameter. A malformed scope is `invalid_scope` (RFC 6749 s5.2)
2018/// rather than `invalid_request`: the parameter was supplied, it is its VALUE that is not a
2019/// scope.
2020fn optional_scope(pairs: &[Pair<'_>]) -> Result<Option<ScopeSet>, ErrorResponse> {
2021    match param(pairs, "scope") {
2022        None => Ok(None),
2023        Some(s) => ScopeSet::parse(s).map(Some).map_err(|_| {
2024            ErrorResponse::new(ErrorCode::InvalidScope)
2025                .with_description("scope is not a space-delimited RFC 6749 s3.3 token list")
2026        }),
2027    }
2028}
2029
2030// ---------------------------------------------------------------------------------------------
2031// Client authentication (RFC 6749 s2.3)
2032// ---------------------------------------------------------------------------------------------
2033
2034/// Authenticated (or merely identified) client credentials from one request.
2035///
2036/// DELIBERATELY NOT `Debug`, and not by omission. Every field but `client_id` is a live credential
2037/// decoded straight off the wire (a shared secret, or an RFC 7523 assertion that is a bearer
2038/// credential for as long as it is unexpired), and this value is in scope in five handlers. A
2039/// derived `Debug` would put all of it verbatim into a host's logs the first time somebody wrote
2040/// `tracing::debug!(?creds)`. A redacting `Debug` would make that line compile and print something
2041/// safe; no `Debug` at all makes it fail to compile, which is the stronger guarantee and costs
2042/// nothing, because nothing in this crate prints this type.
2043struct Credentials {
2044    client_id: String,
2045    /// `None` for a public client, which has no secret to present.
2046    client_secret: Option<String>,
2047    /// RFC 7521 s4.2 `client_assertion_type`, verbatim.
2048    #[cfg(feature = "client-assertion")]
2049    client_assertion_type: Option<String>,
2050    /// RFC 7523 `client-assertion`, verbatim.
2051    #[cfg(feature = "client-assertion")]
2052    client_assertion: Option<String>,
2053}
2054
2055impl Credentials {
2056    /// The borrowed form the server takes. Borrowed rather than owned so that reading a credential
2057    /// off the wire costs the same as it did before these two parameters existed.
2058    fn credential(&self) -> crate::server::ClientCredential<'_> {
2059        crate::server::ClientCredential {
2060            client_secret: self.client_secret.as_deref(),
2061            #[cfg(feature = "client-assertion")]
2062            client_assertion_type: self.client_assertion_type.as_deref(),
2063            #[cfg(feature = "client-assertion")]
2064            client_assertion: self.client_assertion.as_deref(),
2065            // ALWAYS `None`, and it has to be. This router is handed a parsed request; it
2066            // does not terminate TLS and never sees the connection, so there is no
2067            // certificate here that anybody verified. RFC 8705 clients reach the server
2068            // through `ClientCredential::certificate` from a host that DID terminate the
2069            // connection. Reading one out of a proxy header here would be the exact
2070            // mistake `crate::mtls`'s trust boundary section warns about, and it would be
2071            // made on every deployment's behalf rather than on the one host that knows
2072            // whether its terminator can be trusted.
2073            #[cfg(feature = "mtls")]
2074            certificate: None,
2075        }
2076    }
2077}
2078
2079/// Whether the request offered HTTP Basic credentials at all. Decided before any parsing, because
2080/// it is what selects 401-with-challenge over 400 even when the parsing then fails.
2081fn basic_attempted(headers: &HeaderMap) -> bool {
2082    headers
2083        .get(header::AUTHORIZATION)
2084        .and_then(|v| v.to_str().ok())
2085        .is_some_and(|v| v.len() >= 6 && v[..6].eq_ignore_ascii_case("basic "))
2086}
2087
2088/// Decode `Authorization: Basic ...` into `(client_id, client_secret)`.
2089///
2090/// RFC 6749 s2.3.1 is specific and frequently got wrong: the client identifier and password are
2091/// each form-urlencoded FIRST, then joined with a colon and base64ed. A server that skips the
2092/// decode silently rejects every client whose secret contains a character the encoding escapes.
2093fn decode_basic(headers: &HeaderMap) -> Result<(String, String), ErrorResponse> {
2094    let malformed = || {
2095        ErrorResponse::new(ErrorCode::InvalidClient)
2096            .with_description("malformed HTTP Basic credentials (RFC 6749 s2.3.1)")
2097    };
2098    let raw = headers
2099        .get(header::AUTHORIZATION)
2100        .and_then(|v| v.to_str().ok())
2101        .ok_or_else(malformed)?;
2102    let encoded = raw.get(6..).ok_or_else(malformed)?.trim();
2103    let decoded = BASE64_STANDARD.decode(encoded).map_err(|_| malformed())?;
2104    let text = String::from_utf8(decoded).map_err(|_| malformed())?;
2105    // Split on the FIRST colon: RFC 7617 says the userid cannot contain one, so any later colon
2106    // belongs to the password.
2107    let (id, secret) = text.split_once(':').ok_or_else(malformed)?;
2108    Ok((
2109        decode_component(id).into_owned(),
2110        decode_component(secret).into_owned(),
2111    ))
2112}
2113
2114/// Resolve the client from the request, across all three methods this server advertises in
2115/// `token_endpoint_auth_methods_supported`.
2116///
2117/// RFC 6749 s2.3: "The client MUST NOT use more than one authentication method in each request."
2118/// Presenting Basic credentials AND body credentials is therefore refused outright rather than
2119/// silently resolved by precedence: a server that picks one is a server whose behaviour differs
2120/// from the next server's, and the ambiguity is exactly what a request-smuggling intermediary
2121/// would exploit.
2122fn credentials(headers: &HeaderMap, form: &[Pair<'_>]) -> Result<Credentials, ErrorResponse> {
2123    credentials_where(headers, form, false)
2124}
2125
2126/// [`credentials`] for the RFC 9126 PAR endpoint, where a form `client_id` alongside header
2127/// credentials is NOT a second authentication method.
2128///
2129/// This is the one endpoint where the distinction bites. RFC 9126 s2.1 says the pushed body
2130/// carries the authorization request parameters of RFC 6749 s4.1.1, in which `client_id` is
2131/// REQUIRED, AND that the client authenticates as it does at the token endpoint. A client using
2132/// `client_secret_basic` therefore MUST send both, and this crate's token-endpoint rule (any
2133/// `client_id` in the body alongside Basic is two methods) would make PAR unusable for every
2134/// confidential client that authenticates with a header. The RFC settles it: there the parameter
2135/// is a REQUEST parameter that happens to name the same client, and it carries no credential, so
2136/// it cannot be a second authentication method.
2137///
2138/// Nothing is loosened about actual credentials: a `client_secret` or an assertion alongside Basic
2139/// is still two methods and still refused. And the pushed `client_id` is not trusted either, it is
2140/// checked against the AUTHENTICATED client inside
2141/// [`AuthorizationServer::pushed_authorization_request`], which is RFC 9126 s2.1's own rule and
2142/// what stops a client lodging a request under a victim's identity.
2143#[cfg(feature = "par")]
2144fn pushed_request_credentials(
2145    headers: &HeaderMap,
2146    form: &[Pair<'_>],
2147) -> Result<Credentials, ErrorResponse> {
2148    credentials_where(headers, form, true)
2149}
2150
2151/// The shared body of the two above. `client_id_is_a_request_parameter` is the only difference.
2152fn credentials_where(
2153    headers: &HeaderMap,
2154    form: &[Pair<'_>],
2155    client_id_is_a_request_parameter: bool,
2156) -> Result<Credentials, ErrorResponse> {
2157    let basic = basic_attempted(headers);
2158    let body_id = param(form, "client_id");
2159    let body_secret = param(form, "client_secret");
2160
2161    // RFC 7523 s2.2 / RFC 7521 s4.2. Handled BEFORE the three older methods, because an assertion
2162    // is a complete client authentication on its own and s2.2 makes `client_id` OPTIONAL alongside
2163    // it: the assertion already names the client, so requiring the parameter would refuse a
2164    // conforming client over a redundancy.
2165    #[cfg(feature = "client-assertion")]
2166    if let Some(assertion) = param(form, "client_assertion") {
2167        // RFC 6749 s2.3: one authentication method per request. Basic credentials or a
2168        // `client_secret` alongside an assertion is two, and a server that resolves the ambiguity
2169        // by precedence is a server whose behaviour differs from the next one's.
2170        if basic || body_secret.is_some() {
2171            return Err(ErrorResponse::new(ErrorCode::InvalidRequest)
2172                .with_description("more than one client authentication method (RFC 6749 s2.3)"));
2173        }
2174        // UNVERIFIED, and only used to LOCATE the registration. The registration then decides the
2175        // algorithm and the key, and `verify_assertion` re-checks `iss`/`sub` against the client id
2176        // resolved here, so nothing is trusted on the strength of this read. A form `client_id`
2177        // wins when present, because that is the value the client explicitly asserted.
2178        let client_id = match body_id {
2179            Some(id) => id.to_string(),
2180            None => crate::client_assertion::unverified_subject(assertion)
2181                .ok_or_else(|| {
2182                    ErrorResponse::new(ErrorCode::InvalidClient)
2183                        .with_description("the client assertion names no client")
2184                })?
2185                .to_string(),
2186        };
2187        return Ok(Credentials {
2188            client_id,
2189            client_secret: None,
2190            client_assertion_type: param(form, "client_assertion_type").map(str::to_string),
2191            client_assertion: Some(assertion.to_string()),
2192        });
2193    }
2194
2195    match (basic, body_id, body_secret) {
2196        // Header credentials, and no credential in the body. The `client_id` that may sit
2197        // alongside them is IGNORED for authentication: whether it may be there at all was decided
2198        // by the caller, and where it may, the endpoint checks it against the authenticated client
2199        // itself rather than letting it select one.
2200        (true, None, None) | (true, Some(_), None) if client_id_is_a_request_parameter => {
2201            let (client_id, client_secret) = decode_basic(headers)?;
2202            Ok(Credentials {
2203                client_id,
2204                client_secret: Some(client_secret),
2205                #[cfg(feature = "client-assertion")]
2206                client_assertion_type: None,
2207                #[cfg(feature = "client-assertion")]
2208                client_assertion: None,
2209            })
2210        }
2211        (true, None, None) => {
2212            let (client_id, client_secret) = decode_basic(headers)?;
2213            Ok(Credentials {
2214                client_id,
2215                client_secret: Some(client_secret),
2216                #[cfg(feature = "client-assertion")]
2217                client_assertion_type: None,
2218                #[cfg(feature = "client-assertion")]
2219                client_assertion: None,
2220            })
2221        }
2222        (true, _, _) => Err(ErrorResponse::new(ErrorCode::InvalidRequest)
2223            .with_description("more than one client authentication method (RFC 6749 s2.3)")),
2224        // `client_secret_post`, and the bare `client_id` a public client sends (RFC 6749 s3.2.1:
2225        // a client that is not authenticating still identifies itself).
2226        (false, Some(id), secret) => Ok(Credentials {
2227            client_id: id.to_string(),
2228            client_secret: secret.map(str::to_string),
2229            #[cfg(feature = "client-assertion")]
2230            client_assertion_type: None,
2231            #[cfg(feature = "client-assertion")]
2232            client_assertion: None,
2233        }),
2234        // RFC 6749 s5.2 names this case explicitly under `invalid_client`: "no client
2235        // authentication included".
2236        (false, None, _) => Err(ErrorResponse::new(ErrorCode::InvalidClient)
2237            .with_description("no client authentication or client_id")),
2238    }
2239}
2240
2241// ---------------------------------------------------------------------------------------------
2242// Handlers
2243// ---------------------------------------------------------------------------------------------
2244
2245/// RFC 8414 s3.1. Served from the bytes produced when the router was built.
2246fn metadata_handler<S: Storage, C: Clock>(state: &Inner<S, C>) -> Response {
2247    let mut resp = respond(StatusCode::OK, state.metadata.clone());
2248    resp.headers_mut()
2249        .insert(header::CONTENT_TYPE, json_content_type());
2250    resp
2251}
2252
2253/// RFC 7517 s5: the JWK Set a resource server fetches to verify the RFC 9068 access tokens this
2254/// server signs. Served from the bytes produced when the router was built.
2255///
2256/// PUBLIC key parameters only, and no `Cache-Control: no-store`: unlike the token plane this body
2257/// carries no credential, and a key set that may not be cached would be re-fetched by every
2258/// verifier on every token, which is how rotation-capable deployments fall over.
2259#[cfg(feature = "jwt")]
2260fn jwks_handler<S: Storage, C: Clock>(state: &Inner<S, C>) -> Response {
2261    match &state.jwks {
2262        Some(bytes) => {
2263            let mut resp = respond(StatusCode::OK, bytes.clone());
2264            resp.headers_mut()
2265                .insert(header::CONTENT_TYPE, jwks_content_type());
2266            resp
2267        }
2268        // Unreachable: `build` routes this path only when it has the bytes.
2269        None => respond(StatusCode::NOT_FOUND, Body::empty()),
2270    }
2271}
2272
2273/// RFC 6749 s3.2, plus RFC 8628 s3.4 for the device grant.
2274async fn token_handler<S: Storage, C: Clock>(
2275    state: &Inner<S, C>,
2276    headers: &HeaderMap,
2277    body: &Bytes,
2278) -> Response {
2279    let via_header = basic_attempted(headers);
2280    let text = String::from_utf8_lossy(body);
2281    let form = match parse_pairs(&text) {
2282        Ok(form) => form,
2283        Err(TooManyParameters) => return too_many_parameters(),
2284    };
2285
2286    // grant_type is resolved BEFORE client authentication so that a request naming a grant this
2287    // server does not implement gets `unsupported_grant_type` rather than a client-auth error
2288    // about a parameter it never reached.
2289    let grant = match param(&form, "grant_type") {
2290        None => {
2291            return error_response(
2292                &ErrorResponse::new(ErrorCode::InvalidRequest)
2293                    .with_description("missing required parameter grant_type"),
2294                via_header,
2295                &state.challenge,
2296            )
2297        }
2298        // `GrantType::parse` and NOT `value.parse::<GrantType>()`: `FromStr`'s error carries an
2299        // owned copy of the caller's value, and the arm below discards it unread. That copy was
2300        // sized by an unauthenticated caller (one form parameter can be nearly the whole 64 KiB
2301        // body), which made a refusal cost the server a 60 KiB malloc and memcpy at whatever rate
2302        // the caller could open sockets. See `tests/refusal_cost.rs`.
2303        Some(value) => match GrantType::parse(value) {
2304            Some(g) => g,
2305            // The value is NOT echoed. RFC 6749 s5.2 restricts error_description to a charset
2306            // that excludes the double quote and backslash, and an attacker controls this string;
2307            // saying which grant was asked for is not worth having to sanitize it.
2308            None => {
2309                return error_response(
2310                    &ErrorResponse::new(ErrorCode::UnsupportedGrantType)
2311                        .with_description("this server does not implement the requested grant"),
2312                    via_header,
2313                    &state.challenge,
2314                )
2315            }
2316        },
2317    };
2318
2319    let mut creds = match credentials(headers, &form) {
2320        Ok(c) => c,
2321        Err(e) => return error_response(&e, via_header, &state.challenge),
2322    };
2323    // TAKEN, not cloned. `creds` has to outlive this because `creds.credential()` borrows the
2324    // secret fields below, but nothing reads `client_id` off it again, so moving the String out
2325    // and leaving an empty one behind saves an allocation on every request to this endpoint.
2326    let client_id = ClientId::new(std::mem::take(&mut creds.client_id));
2327    // NOT moved onto the TokenRequest variant any more: every credential this endpoint accepts now
2328    // travels together on the request CONTEXT, so there is one place a reader has to look to see
2329    // what the client presented, rather than one for secrets and another for everything else.
2330    let client_secret: Option<String> = None;
2331
2332    // RFC 9449 s4.3 (1): there must be exactly ONE `DPoP` header. Several is not a request this
2333    // server may pick a favourite from: an intermediary that appended one, or a client that sent
2334    // two, leaves it ambiguous which proof the client meant to bind the token to.
2335    //
2336    // RESOLVED HERE, before the grant is dispatched, and that placement is the fix for a silent
2337    // downgrade rather than tidiness. The RFC 8693 arm below RETURNS, so while this block sat
2338    // after the dispatch a `DPoP` header sent with `grant_type=token-exchange` was never read at
2339    // all: no duplicate check, no proof verification, and an issued token with no `jkt`. A client
2340    // that asked for a sender-constrained token got a bearer token and no way to find out.
2341    #[cfg(feature = "dpop")]
2342    let dpop_proof = {
2343        let mut values = headers.get_all(crate::dpop::DPOP_HEADER).iter();
2344        let first = values.next();
2345        if values.next().is_some() {
2346            // EMITTED like every other proof refusal. These two are refused HERE rather than in
2347            // `verify_proof`, which is the only reason they were silent through 0.9.0: a
2348            // deployment reading `DpopProofRefused` to tell its failure modes apart would have
2349            // seen nothing whatever for a client sending two headers, which is a client bug the
2350            // operator is the only one who can report back.
2351            state
2352                .server
2353                .hooks()
2354                .emit(|| crate::events::Event::DpopProofRefused {
2355                    failure: crate::dpop::DpopFailure::Malformed,
2356                });
2357            return error_response(
2358                &ErrorResponse::new(ErrorCode::InvalidDpopProof)
2359                    .with_description("more than one DPoP header (RFC 9449 s4.3)"),
2360                via_header,
2361                &state.challenge,
2362            );
2363        }
2364        match first.map(|v| v.to_str()) {
2365            None => None,
2366            Some(Ok(value)) => Some(value),
2367            // A header that is not visible ASCII cannot be a compact JWS, so this is a malformed
2368            // proof rather than an absent one, and answering "absent" would silently downgrade a
2369            // client that asked for a bound token to a bearer one.
2370            Some(Err(_)) => {
2371                state
2372                    .server
2373                    .hooks()
2374                    .emit(|| crate::events::Event::DpopProofRefused {
2375                        failure: crate::dpop::DpopFailure::Malformed,
2376                    });
2377                return error_response(
2378                    &ErrorResponse::new(ErrorCode::InvalidDpopProof)
2379                        .with_description("the DPoP header is not a compact JWS"),
2380                    via_header,
2381                    &state.challenge,
2382                );
2383            }
2384        }
2385    };
2386
2387    let request = match grant {
2388        GrantType::AuthorizationCode => {
2389            let code = match required(&form, "code") {
2390                Ok(v) => v.to_string(),
2391                Err(e) => return error_response(&e, via_header, &state.challenge),
2392            };
2393            TokenRequest::AuthorizationCode {
2394                client_id,
2395                client_secret,
2396                code,
2397                redirect_uri: param(&form, "redirect_uri").map(str::to_string),
2398                code_verifier: param(&form, "code_verifier").map(str::to_string),
2399            }
2400        }
2401        GrantType::ClientCredentials => {
2402            let scope = match optional_scope(&form) {
2403                Ok(s) => s,
2404                Err(e) => return error_response(&e, via_header, &state.challenge),
2405            };
2406            TokenRequest::ClientCredentials {
2407                client_id,
2408                client_secret,
2409                scope,
2410            }
2411        }
2412        GrantType::DeviceCode => {
2413            let device_code = match required(&form, "device_code") {
2414                Ok(v) => v.to_string(),
2415                Err(e) => return error_response(&e, via_header, &state.challenge),
2416            };
2417            TokenRequest::DeviceCode {
2418                client_id,
2419                client_secret,
2420                device_code,
2421            }
2422        }
2423        GrantType::RefreshToken => {
2424            let refresh_token = match required(&form, "refresh_token") {
2425                Ok(v) => v.to_string(),
2426                Err(e) => return error_response(&e, via_header, &state.challenge),
2427            };
2428            let scope = match optional_scope(&form) {
2429                Ok(s) => s,
2430                Err(e) => return error_response(&e, via_header, &state.challenge),
2431            };
2432            TokenRequest::RefreshToken {
2433                client_id,
2434                client_secret,
2435                refresh_token,
2436                scope,
2437            }
2438        }
2439        // RFC 8693 s2 shares the token endpoint with the RFC 6749 grants but NOT their
2440        // response body (s2.2.1 adds a REQUIRED member), so it answers from here rather
2441        // than producing a `TokenRequest`. Serving it matters beyond convenience: the RFC
2442        // 8414 document advertises the grant when this feature is on, and an advertised
2443        // grant this router refused would be exactly the lie that document exists to
2444        // avoid.
2445        #[cfg(feature = "token-exchange")]
2446        GrantType::TokenExchange => {
2447            // RFC 9449: this grant CANNOT bind the token it issues, so a presented proof is
2448            // REFUSED rather than ignored. `TokenExchangeRequest` carries no proof and
2449            // `crate::token_exchange` has nowhere to record a `jkt`, so honouring the header would
2450            // mean issuing an unbound token to a client that asked for a bound one and telling it
2451            // nothing: the silent downgrade that module argues against at length for the SUBJECT
2452            // token, applied to the ISSUED token by the same module. Loud beats quiet, and the
2453            // refusal is what an operator who turned DPoP on can actually see.
2454            #[cfg(feature = "dpop")]
2455            if dpop_proof.is_some() {
2456                // And it is REPORTED, for the same reason it is refused loudly: a client asking
2457                // this server for something it cannot do is a wiring mistake, and the operator who
2458                // turned DPoP on is the only party who can tell the client's author.
2459                //
2460                // `NotAcceptedHere`, NOT `Malformed`, which is what this said until the 0.9.1
2461                // audit: the proof has not been parsed at this point and is probably a perfectly
2462                // good JWS. Reporting it as malformed described the client's string instead of
2463                // this server's capability, and pointed the one person who could fix it at the one
2464                // person who could not.
2465                state
2466                    .server
2467                    .hooks()
2468                    .emit(|| crate::events::Event::DpopProofRefused {
2469                        failure: crate::dpop::DpopFailure::NotAcceptedHere,
2470                    });
2471                return error_response(
2472                    &ErrorResponse::new(ErrorCode::InvalidDpopProof).with_description(
2473                        "this server does not issue sender-constrained tokens through RFC 8693 \
2474                         token exchange",
2475                    ),
2476                    via_header,
2477                    &state.challenge,
2478                );
2479            }
2480            // The WHOLE credential, not the secret alone. RFC 8693 s2.1 authenticates the client
2481            // exactly as the other grants do, and `TokenExchange::exchange_token` REFUSES a client
2482            // that is not confidential: forwarding only `client_secret` first made every exchange
2483            // `invalid_client` (the `None` the other arms carry), and then, once the secret was
2484            // restored, still refused every client registered for `private_key_jwt` or
2485            // `client_secret_jwt`, whose credential arrives in `client-assertion`. Half a repair
2486            // is what left the second half invisible.
2487            return token_exchange_response(state, &form, client_id, &creds, via_header).await;
2488        }
2489    };
2490
2491    // RFC 8707 s2: `resource` is a parameter of the token request itself, independent of
2492    // `grant_type`, so it is collected once here rather than inside each arm above.
2493    let resources = resource_indicators(&form);
2494
2495    let context = crate::server::TokenRequestContext {
2496        credential: creds.credential(),
2497        resources: &resources,
2498        // RFC 9396 s2 makes this ONE JSON array, so `param`'s first-wins rule is the right
2499        // one here and a duplicate is a smuggled parameter rather than a second value.
2500        // That is the opposite of `resource`, which s2 of RFC 8707 explicitly allows to
2501        // repeat, and the difference is why the two are read differently.
2502        //
2503        // READ IN EVERY BUILD, not only under `rar`: a build that supports no authorization
2504        // detail type has to refuse the parameter (RFC 9396 s5), and a router that never read it
2505        // off the form left the endpoint nothing to refuse. See `TokenRequestContext`.
2506        authorization_details: param(&form, "authorization_details"),
2507        #[cfg(feature = "dpop")]
2508        dpop_proof,
2509    };
2510    match state.server.token_with_context(request, context).await {
2511        Ok(response) => ok_json(&response),
2512        Err(e) => error_response(&e, via_header, &state.challenge),
2513    }
2514}
2515
2516/// RFC 8693 s2: the token exchange grant.
2517///
2518/// The router serves the WIRE response only. A host that needs to know whether the exchange
2519/// was delegation or impersonation (s1.1), or that needs the s4.1 `act` claim to put into a
2520/// token of its own, calls [`crate::token_exchange::TokenExchange::exchange_token`] directly:
2521/// neither is a response parameter RFC 8693 defines, so neither belongs in this body.
2522#[cfg(feature = "token-exchange")]
2523async fn token_exchange_response<S: Storage, C: Clock>(
2524    state: &Inner<S, C>,
2525    form: &[Pair<'_>],
2526    client_id: ClientId,
2527    creds: &Credentials,
2528    via_header: bool,
2529) -> Response {
2530    // The three refusals below, written out rather than built from the parameter's name. There
2531    // are exactly three call sites and the name is a constant at each of them, so `format!` was
2532    // copying one of three fixed sentences onto the heap per refused request; this refusal happens
2533    // BEFORE the exchange is attempted, so before the presented client credential has been
2534    // checked, which makes its rate the caller's to choose.
2535    const SUBJECT_NOT_A_TOKEN_TYPE: &str =
2536        "subject_token_type is not a token type RFC 8693 s3 registers";
2537    const ACTOR_NOT_A_TOKEN_TYPE: &str =
2538        "actor_token_type is not a token type RFC 8693 s3 registers";
2539    const REQUESTED_NOT_A_TOKEN_TYPE: &str =
2540        "requested_token_type is not a token type RFC 8693 s3 registers";
2541
2542    fn token_type(
2543        refusal: &'static str,
2544        value: &str,
2545    ) -> Result<crate::token_exchange::TokenTypeIdentifier, ErrorResponse> {
2546        // The VALUE is not echoed, for the reason `grant_type` is not echoed above: RFC
2547        // 6749 s5.2 restricts error_description to a charset an attacker-supplied URN need
2548        // not respect, and naming the parameter is enough for the developer who sent it.
2549        //
2550        // And because it is not echoed, the parse must not COPY it either: `FromStr` builds an
2551        // `UnknownTokenTypeIdentifier` holding an owned, caller-sized `String` that this line then
2552        // throws away. `TokenTypeIdentifier::parse` is the same match with no payload.
2553        crate::token_exchange::TokenTypeIdentifier::parse(value)
2554            .ok_or_else(|| ErrorResponse::new(ErrorCode::InvalidRequest).with_description(refusal))
2555    }
2556
2557    // RFC 9396 s5, before anything else is parsed. This grant issues from the SUBJECT token, so
2558    // `TokenExchangeRequest` has no member an authorization detail could travel in and the
2559    // parameter would die here silently; the token endpoint's shared handling, which refuses it,
2560    // is downstream of the arm that called this function and never runs for this grant. See
2561    // `refuse_authorization_details`.
2562    if let Some(refusal) = refuse_authorization_details(form) {
2563        return error_response(&refusal, via_header, &state.challenge);
2564    }
2565
2566    let subject_token = match required(form, "subject_token") {
2567        Ok(v) => v,
2568        Err(e) => return error_response(&e, via_header, &state.challenge),
2569    };
2570    let subject_token_type = match required(form, "subject_token_type")
2571        .and_then(|v| token_type(SUBJECT_NOT_A_TOKEN_TYPE, v))
2572    {
2573        Ok(v) => v,
2574        Err(e) => return error_response(&e, via_header, &state.challenge),
2575    };
2576    let actor_token = param(form, "actor_token");
2577    let actor_token_type = match param(form, "actor_token_type")
2578        .map(|v| token_type(ACTOR_NOT_A_TOKEN_TYPE, v))
2579        .transpose()
2580    {
2581        Ok(v) => v,
2582        Err(e) => return error_response(&e, via_header, &state.challenge),
2583    };
2584    let requested_token_type = match param(form, "requested_token_type")
2585        .map(|v| token_type(REQUESTED_NOT_A_TOKEN_TYPE, v))
2586        .transpose()
2587    {
2588        Ok(v) => v,
2589        Err(e) => return error_response(&e, via_header, &state.challenge),
2590    };
2591    let scope = match optional_scope(form) {
2592        Ok(s) => s,
2593        Err(e) => return error_response(&e, via_header, &state.challenge),
2594    };
2595    // Both target parameters may repeat (RFC 8693 s2.1, RFC 8707 s2), so neither may go
2596    // through `param`'s first-wins rule: dropping the second occurrence would silently
2597    // narrow the request to half of what the client asked for.
2598    let resource = resource_indicators(form);
2599    let audience: Vec<String> = form
2600        .iter()
2601        .filter(|(k, _)| k == "audience")
2602        .map(|(_, v)| v.as_ref().to_string())
2603        .collect();
2604
2605    let request = crate::token_exchange::TokenExchangeRequest {
2606        client_id: &client_id,
2607        client_secret: creds.client_secret.as_deref(),
2608        // RFC 7521 s4.2 / RFC 7523 s2.2, forwarded rather than dropped: without these two an
2609        // assertion-authenticated confidential client cannot use this grant at all, and this is
2610        // the endpoint that advertises it.
2611        #[cfg(feature = "client-assertion")]
2612        client_assertion_type: creds.client_assertion_type.as_deref(),
2613        #[cfg(feature = "client-assertion")]
2614        client_assertion: creds.client_assertion.as_deref(),
2615        subject_token,
2616        subject_token_type,
2617        actor_token,
2618        actor_token_type,
2619        resource: &resource,
2620        audience: &audience,
2621        scope: scope.as_ref(),
2622        requested_token_type,
2623    };
2624    match crate::token_exchange::TokenExchange::exchange_token(&*state.server, &request).await {
2625        Ok(exchanged) => ok_json(&exchanged.response),
2626        Err(e) => error_response(&e, via_header, &state.challenge),
2627    }
2628}
2629
2630/// RFC 8628 s3.1: the device authorization request.
2631async fn device_authorization_handler<S: Storage, C: Clock>(
2632    state: &Inner<S, C>,
2633    headers: &HeaderMap,
2634    body: &Bytes,
2635) -> Response {
2636    let via_header = basic_attempted(headers);
2637    let text = String::from_utf8_lossy(body);
2638    let form = match parse_pairs(&text) {
2639        Ok(form) => form,
2640        Err(TooManyParameters) => return too_many_parameters(),
2641    };
2642
2643    // RFC 9396 s3 names the device authorization request as a place this parameter may be used,
2644    // and s5 requires refusing one this server will not honour. See
2645    // `refuse_authorization_details`: a `DeviceGrant` has no field for a detail, so accepting one
2646    // here mints a user code for a permission that can never reach the token.
2647    if let Some(refusal) = refuse_authorization_details(&form) {
2648        return error_response(&refusal, via_header, &state.challenge);
2649    }
2650
2651    let mut creds = match credentials(headers, &form) {
2652        Ok(c) => c,
2653        Err(e) => return error_response(&e, via_header, &state.challenge),
2654    };
2655    // TAKEN rather than cloned, for the reason the token endpoint gives: `creds` must
2656    // outlive this call because `creds.credential()` borrows out of it, but its
2657    // `client_id` is never read again.
2658    let client_id = ClientId::new(std::mem::take(&mut creds.client_id));
2659    let scope = match optional_scope(&form) {
2660        Ok(s) => s,
2661        Err(e) => return error_response(&e, via_header, &state.challenge),
2662    };
2663    match state
2664        .server
2665        .device_authorization_with_credential(&client_id, &creds.credential(), scope.as_ref())
2666        .await
2667    {
2668        Ok(response) => ok_json(&response),
2669        Err(e) => error_response(&e, via_header, &state.challenge),
2670    }
2671}
2672
2673/// RFC 9126 s2: the pushed authorization request endpoint.
2674///
2675/// It is on the TOKEN plane, not the authorization plane, and everything about this handler
2676/// follows from that: the client authenticates here exactly as it does at the token endpoint
2677/// (s2.1 step 1), so client authentication is resolved by the same [`credentials`] function and a
2678/// failure gets the same RFC 6749 s5.2 shape with the same 401-versus-400 rule; and the response
2679/// carries a capability handle, so it gets the same s5.1 caching directives. The one thing that
2680/// differs is the success status, which s2.2 states rather than suggests: 201, not 200.
2681#[cfg(feature = "par")]
2682async fn pushed_authorization_handler<S: Storage, C: Clock>(
2683    state: &Inner<S, C>,
2684    headers: &HeaderMap,
2685    body: &Bytes,
2686) -> Response {
2687    let via_header = basic_attempted(headers);
2688    let text = String::from_utf8_lossy(body);
2689    let form = match parse_pairs(&text) {
2690        Ok(form) => form,
2691        Err(TooManyParameters) => return too_many_parameters(),
2692    };
2693
2694    let mut creds = match pushed_request_credentials(headers, &form) {
2695        Ok(c) => c,
2696        Err(e) => return error_response(&e, via_header, &state.challenge),
2697    };
2698    // TAKEN rather than cloned, for the reason the token endpoint gives: `creds` must
2699    // outlive this call because `creds.credential()` borrows out of it, but its
2700    // `client_id` is never read again.
2701    let client_id = ClientId::new(std::mem::take(&mut creds.client_id));
2702    // The form EXACTLY as it arrived, with nothing filtered out. RFC 9126 s2.1 step 2 REFUSES a
2703    // pushed `request_uri` and s3 treats a `request` as a signed request object, and both of those
2704    // are decisions for the server: a router that quietly dropped either parameter would turn a
2705    // refusal the RFC requires into a silent acceptance of a different request. Borrowed out of
2706    // the parsed form, so passing it costs no allocation per parameter.
2707    let parameters: Vec<(&str, &str)> =
2708        form.iter().map(|(k, v)| (k.as_ref(), v.as_ref())).collect();
2709    match state
2710        .server
2711        .pushed_authorization_request_with_credential(&client_id, &creds.credential(), &parameters)
2712        .await
2713    {
2714        Ok(response) => {
2715            // s2.2: "with a 201 HTTP status code". Taken from the response type rather than
2716            // written here twice, so the wire status and the type's own answer cannot drift.
2717            let status =
2718                StatusCode::from_u16(response.http_status()).unwrap_or(StatusCode::CREATED);
2719            let mut resp = respond(status, json_body(&response));
2720            let h = resp.headers_mut();
2721            h.insert(header::CONTENT_TYPE, json_content_type());
2722            no_store(h);
2723            resp
2724        }
2725        Err(e) => error_response(&e, via_header, &state.challenge),
2726    }
2727}
2728
2729/// RFC 7662 s2.1: token introspection, for a caller that authenticates as a client.
2730async fn introspect_handler<S: Storage, C: Clock>(
2731    state: &Inner<S, C>,
2732    headers: &HeaderMap,
2733    body: &Bytes,
2734) -> Response {
2735    let via_header = basic_attempted(headers);
2736    let text = String::from_utf8_lossy(body);
2737    let form = match parse_pairs(&text) {
2738        Ok(form) => form,
2739        Err(TooManyParameters) => return too_many_parameters(),
2740    };
2741
2742    let mut creds = match credentials(headers, &form) {
2743        Ok(c) => c,
2744        Err(e) => return error_response(&e, via_header, &state.challenge),
2745    };
2746    // TAKEN rather than cloned, for the reason the token endpoint gives: `creds` must
2747    // outlive this call because `creds.credential()` borrows out of it, but its
2748    // `client_id` is never read again.
2749    let client_id = ClientId::new(std::mem::take(&mut creds.client_id));
2750    let token = match required(&form, "token") {
2751        Ok(v) => v,
2752        Err(e) => return error_response(&e, via_header, &state.challenge),
2753    };
2754    match state
2755        .server
2756        .introspection_response_with_credential(&client_id, &creds.credential(), token)
2757        .await
2758    {
2759        Ok(response) => ok_json(&response),
2760        Err(e) => error_response(&e, via_header, &state.challenge),
2761    }
2762}
2763
2764/// RFC 7009 s2.1: token revocation. Success is a 200 with an empty body (s2.2).
2765async fn revoke_handler<S: Storage, C: Clock>(
2766    state: &Inner<S, C>,
2767    headers: &HeaderMap,
2768    body: &Bytes,
2769) -> Response {
2770    let via_header = basic_attempted(headers);
2771    let text = String::from_utf8_lossy(body);
2772    let form = match parse_pairs(&text) {
2773        Ok(form) => form,
2774        Err(TooManyParameters) => return too_many_parameters(),
2775    };
2776
2777    let mut creds = match credentials(headers, &form) {
2778        Ok(c) => c,
2779        Err(e) => return error_response(&e, via_header, &state.challenge),
2780    };
2781    // TAKEN rather than cloned, for the reason the token endpoint gives: `creds` must
2782    // outlive this call because `creds.credential()` borrows out of it, but its
2783    // `client_id` is never read again.
2784    let client_id = ClientId::new(std::mem::take(&mut creds.client_id));
2785    let token = match required(&form, "token") {
2786        Ok(v) => v,
2787        Err(e) => return error_response(&e, via_header, &state.challenge),
2788    };
2789    // An unrecognised hint is IGNORED rather than refused. RFC 7009 s2.1 requires the server to
2790    // keep looking when the hint is wrong, and s2.2.1's `unsupported_token_type` is for a token
2791    // type the server cannot revoke at all; this server revokes both types it issues, so there is
2792    // nothing here it is unable to do.
2793    let hint = param(&form, "token_type_hint").and_then(|h| h.parse::<TokenTypeHint>().ok());
2794
2795    // The WHOLE credential, exactly as the other three protected endpoints in this module do.
2796    // Forwarding only `client_secret` dropped every other way a client can authenticate: an RFC
2797    // 7523 assertion arrives in `client-assertion`, not in `client_secret`, so an
2798    // assertion-authenticated confidential client was refused `invalid_client` at this endpoint
2799    // and could never revoke anything through this service. Same defect, same cause and same
2800    // invisibility as the RFC 8693 one `tests/wire_reachability.rs` was written after: an arm not
2801    // updated when credentials moved onto the request context, with a revocation suite that only
2802    // ever drove the library API.
2803    match state
2804        .server
2805        .revoke_with_credential(&client_id, &creds.credential(), token, hint)
2806        .await
2807    {
2808        Ok(()) => {
2809            let mut resp = respond(StatusCode::OK, Body::empty());
2810            no_store(resp.headers_mut());
2811            resp
2812        }
2813        Err(e) => error_response(&e, via_header, &state.challenge),
2814    }
2815}
2816
2817// ---------------------------------------------------------------------------------------------
2818// RFC 7591 registration and RFC 7592 management
2819// ---------------------------------------------------------------------------------------------
2820
2821/// The RFC 6750 s2.1 bearer token from the `Authorization` header, if the request carried one.
2822///
2823/// Used for BOTH credentials this pair of RFCs defines: the RFC 7591 s1.2 initial access token and
2824/// the RFC 7592 s2 registration access token. Both are access tokens presented the same way, so
2825/// there is one parser rather than two that could disagree.
2826fn bearer_token(headers: &HeaderMap) -> Option<&str> {
2827    let raw = headers
2828        .get(header::AUTHORIZATION)
2829        .and_then(|v| v.to_str().ok())?;
2830    // Case-insensitive scheme (RFC 9110 s11.1), then the token68 with its surrounding space
2831    // trimmed. An empty remainder is `None`: a header that names the scheme and supplies nothing
2832    // presented no credential.
2833    if raw.len() < 7 || !raw[..7].eq_ignore_ascii_case("bearer ") {
2834        return None;
2835    }
2836    let token = raw[7..].trim();
2837    (!token.is_empty()).then_some(token)
2838}
2839
2840/// Turn an RFC 7591 s3.2.2 / RFC 7592 s2 refusal into a response.
2841///
2842/// A 401 carries an RFC 6750 s3 `Bearer` challenge rather than the `Basic` one the token plane
2843/// uses: this endpoint authenticates with a bearer token, and telling a client to retry with a
2844/// scheme it cannot use here would be worse than saying nothing. Only the `Invalid` case has a
2845/// body, because it is the only one with something a client can act on; a 401 that described what
2846/// was wrong with the token would be describing somebody else's credential.
2847fn registration_error(failure: &crate::registration::RegistrationFailure) -> Response {
2848    let status =
2849        StatusCode::from_u16(failure.http_status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
2850    // The `Content-Type` goes on the arm that HAS a body. An empty octet stream is not a valid
2851    // `application/json` document (RFC 8259 s2), and announcing it as one turns every RFC 7592
2852    // 401, 404 and 500 into a decode exception in the client rather than into the status it meant
2853    // to report: `response.json()` raises before anything can read `response.status`.
2854    let mut resp = match failure {
2855        crate::registration::RegistrationFailure::Invalid(body) => {
2856            let mut resp = respond(status, json_body(body));
2857            resp.headers_mut()
2858                .insert(header::CONTENT_TYPE, json_content_type());
2859            resp
2860        }
2861        _ => respond(status, Body::empty()),
2862    };
2863    let headers = resp.headers_mut();
2864    no_store(headers);
2865    if status == StatusCode::UNAUTHORIZED {
2866        headers.insert(header::WWW_AUTHENTICATE, HeaderValue::from_static("Bearer"));
2867    }
2868    resp
2869}
2870
2871/// Parse an RFC 7591 s2 metadata document out of a request body.
2872///
2873/// A body that is not JSON at all is `invalid_client_metadata`: s3.2.2 has no code for "your body
2874/// was not JSON", and the client's problem is genuinely that the metadata it submitted is not
2875/// metadata this server can read.
2876///
2877/// The error half is BOXED. An `axum::Response` is a large value (a status, a header map and a
2878/// body), and the `Ok` half here is the common one, so an unboxed `Result` would make every
2879/// successful parse carry the error variant's footprint on the stack. That is what
2880/// `clippy::result_large_err` is pointing at, and boxing is the fix it asks for rather than a
2881/// lint to silence: the allocation happens only on the refusal path, which is the path that is
2882/// about to write a response to a socket anyway.
2883fn client_metadata(body: &Bytes) -> Result<crate::registration::ClientMetadata, Box<Response>> {
2884    serde_json::from_slice(body).map_err(|_| {
2885        Box::new(registration_error(
2886            &crate::registration::RegistrationFailure::Invalid(
2887                crate::registration::RegistrationErrorResponse::new(
2888                    crate::registration::RegistrationErrorCode::InvalidClientMetadata,
2889                    "the request body is not an RFC 7591 s2 client metadata JSON object",
2890                ),
2891            ),
2892        ))
2893    })
2894}
2895
2896/// RFC 7591 s3.1: the client registration request. Success is a 201 (s3.2.1).
2897async fn register_handler<S: Storage, C: Clock>(
2898    state: &Inner<S, C>,
2899    headers: &HeaderMap,
2900    body: &Bytes,
2901) -> Response {
2902    // THROTTLE FIRST, on the one endpoint of this service where there may be no credential to
2903    // look at instead. RFC 7591 s3.1 registration may be ANONYMOUS, so the trick
2904    // `update_registration_handler` uses below — authenticate, then parse — has nothing to work
2905    // with here; what it does have is `Attempt::ClientRegistration`, which is keyed on nothing and
2906    // therefore answerable before a single byte of the body means anything. Parsing up to
2907    // `MAX_BODY_BYTES` of a stranger's JSON before asking the only gate this endpoint has is the
2908    // shape `MAX_FORM_PARAMETERS`'s own comment argues against: a refusal is work an attacker sets
2909    // the rate of.
2910    //
2911    // Unlike the management plane's arrangement, the check is NOT repeated inside the server
2912    // method: `admit_registration` and `register_admitted_client` are the two halves of
2913    // `register_dynamic_client` precisely so that one HTTP request is one charge. The registration
2914    // budget is global and small (60 per window by default), so a second charge would halve a
2915    // host's configured ceiling rather than cost it a rounding error.
2916    if let Err(e) = state.server.admit_registration() {
2917        return registration_error(&e);
2918    }
2919    let metadata = match client_metadata(body) {
2920        Ok(m) => m,
2921        Err(response) => return *response,
2922    };
2923    match state
2924        .server
2925        .register_admitted_client(&metadata, bearer_token(headers))
2926        .await
2927    {
2928        Ok(info) => {
2929            // s3.2.1: "201 Created", and the body carries a client secret and a registration
2930            // access token, so the s5.1 caching rules of RFC 6749 apply exactly as they do to a
2931            // token response.
2932            let mut resp = respond(StatusCode::CREATED, json_body(&info));
2933            let h = resp.headers_mut();
2934            h.insert(header::CONTENT_TYPE, json_content_type());
2935            no_store(h);
2936            resp
2937        }
2938        Err(e) => registration_error(&e),
2939    }
2940}
2941
2942/// RFC 7592 s2.1: read a registration.
2943async fn read_registration_handler<S: Storage, C: Clock>(
2944    state: &Inner<S, C>,
2945    headers: &HeaderMap,
2946    client_id: &str,
2947) -> Response {
2948    let token = bearer_token(headers).unwrap_or_default();
2949    match state
2950        .server
2951        .read_registration(&ClientId::new(client_id), token)
2952        .await
2953    {
2954        Ok(info) => ok_json(&info),
2955        Err(e) => registration_error(&e),
2956    }
2957}
2958
2959/// RFC 7592 s2.2: replace a registration's metadata.
2960async fn update_registration_handler<S: Storage, C: Clock>(
2961    state: &Inner<S, C>,
2962    headers: &HeaderMap,
2963    client_id: &str,
2964    body: &Bytes,
2965) -> Response {
2966    // AUTHENTICATE FIRST, and unlike `register_handler` this handler can afford to. RFC 7591 s3.1
2967    // registration may be anonymous, so there is nothing to check before the body there; RFC 7592
2968    // management is credentialed on every request, and parsing up to `MAX_BODY_BYTES` of a
2969    // stranger's JSON before looking at the credential is the shape `MAX_FORM_PARAMETERS`'s own
2970    // comment argues against: a refusal is work an attacker sets the rate of. The read and delete
2971    // handlers already touched nothing before the token; this one parsed first, and that asymmetry
2972    // was the whole of the defect.
2973    //
2974    // The check is repeated inside `update_registration`, which is deliberate: this one is a
2975    // cheaper refusal, not the authority. The cost of the repeat is one storage read and one hash
2976    // on the SUCCESS path of an endpoint a deployment uses rarely, against a full JSON parse an
2977    // anonymous caller could buy at whatever rate it liked.
2978    let token = bearer_token(headers).unwrap_or_default();
2979    if let Err(e) = state
2980        .server
2981        .authenticate_registration(&ClientId::new(client_id), token)
2982        .await
2983    {
2984        return registration_error(&e);
2985    }
2986    let metadata = match client_metadata(body) {
2987        Ok(m) => m,
2988        Err(response) => return *response,
2989    };
2990    match state
2991        .server
2992        .update_registration(&ClientId::new(client_id), token, &metadata)
2993        .await
2994    {
2995        Ok(info) => ok_json(&info),
2996        Err(e) => registration_error(&e),
2997    }
2998}
2999
3000/// RFC 7592 s2.3: delete a registration. Success is a 204 with no body.
3001async fn delete_registration_handler<S: Storage, C: Clock>(
3002    state: &Inner<S, C>,
3003    headers: &HeaderMap,
3004    client_id: &str,
3005) -> Response {
3006    let token = bearer_token(headers).unwrap_or_default();
3007    match state
3008        .server
3009        .delete_registration(&ClientId::new(client_id), token)
3010        .await
3011    {
3012        Ok(()) => {
3013            let mut resp = respond(StatusCode::NO_CONTENT, Body::empty());
3014            no_store(resp.headers_mut());
3015            resp
3016        }
3017        Err(e) => registration_error(&e),
3018    }
3019}
3020
3021/// Which of the ways an authorization request may arrive this one used, validated.
3022///
3023/// Three, and the two that are not query text exist because query text is the problem: it travels
3024/// through the browser, its history, its `Referer` headers and every proxy in front of it, and
3025/// anything able to rewrite the URL can change it before this server sees it.
3026///
3027/// - RFC 6749 s4.1.1, the parameters in the query.
3028/// - RFC 9126 s4, `client_id` plus a `request_uri` this server minted at its own PAR endpoint.
3029/// - RFC 9101 s5.1, `client_id` plus a signed `request` object.
3030///
3031/// For the latter two, EVERY other query parameter is ignored. That is not this function's choice
3032/// to make and it is not made here: RFC 9101 s6.3 (which RFC 9126 s4 builds on) requires the
3033/// server to use only the parameters carried by the reference or the object "even if the same
3034/// parameter is provided in the query parameter", and the two server methods enforce it by not
3035/// accepting any others, so there is no code path in which an appended `scope` could win.
3036///
3037/// With neither feature compiled in this is the plain query path and nothing else, which is what
3038/// the crate did before either feature existed.
3039async fn resolve_authorization_request<S: Storage, C: Clock>(
3040    state: &Inner<S, C>,
3041    pairs: &[Pair<'_>],
3042) -> Result<crate::authorization::ValidatedAuthorizationRequest, AuthorizationError> {
3043    #[cfg(any(feature = "par", feature = "jar"))]
3044    {
3045        // A `request_uri` this server cannot resolve (the `par` feature is off) is an UNKNOWN
3046        // parameter, and RFC 6749 s3.1 says an unknown parameter is ignored. That is safe here
3047        // only because a server without PAR compiled in never minted one, so there is no handle
3048        // for the ignoring to downgrade.
3049        #[cfg(feature = "par")]
3050        let by_reference = param(pairs, "request_uri");
3051        #[cfg(not(feature = "par"))]
3052        let by_reference: Option<&str> = None;
3053        #[cfg(feature = "jar")]
3054        let by_value = param(pairs, "request");
3055        #[cfg(not(feature = "jar"))]
3056        let by_value: Option<&str> = None;
3057
3058        // RFC 9101 s5: "The client MUST NOT send both". Refused rather than resolved by
3059        // precedence, for the reason RFC 6749 s2.3 gives about two client authentication methods:
3060        // a server that picks one behaves differently from the next server, and that difference is
3061        // what a smuggling intermediary exploits.
3062        if by_reference.is_some() && by_value.is_some() {
3063            return Err(AuthorizationError::Direct(
3064                ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
3065                    "request and request_uri must not both be sent (RFC 9101 s5)",
3066                ),
3067            ));
3068        }
3069
3070        if by_reference.is_some() || by_value.is_some() {
3071            // RFC 9126 s4 and RFC 9101 s5 both make `client_id` REQUIRED alongside the handle or
3072            // the object, and it is load bearing rather than decorative: it selects the pushed
3073            // record whose binding is then checked (s2.2, and s7.5 is the swapping attack), or the
3074            // registered key the signature is verified with. Resolved once here rather than in a
3075            // closure per branch, because a closure whose `Ok` is a `&str` and whose `Err` is a
3076            // 128 byte `AuthorizationError` is exactly what `clippy::result_large_err` objects to.
3077            let client_id = match param(pairs, "client_id") {
3078                Some(id) => id,
3079                None => {
3080                    return Err(AuthorizationError::Direct(
3081                        ErrorResponse::new(ErrorCode::InvalidRequest)
3082                            .with_description("client_id is required (RFC 9126 s4, RFC 9101 s5)"),
3083                    ))
3084                }
3085            };
3086
3087            #[cfg(feature = "par")]
3088            if let Some(request_uri) = by_reference {
3089                return state
3090                    .server
3091                    .validate_pushed_authorization_request(client_id, request_uri)
3092                    .await;
3093            }
3094            #[cfg(feature = "jar")]
3095            if let Some(request_object) = by_value {
3096                return state
3097                    .server
3098                    .validate_signed_authorization_request(client_id, request_object)
3099                    .await;
3100            }
3101        }
3102    }
3103
3104    let request =
3105        AuthorizationRequest::from_pairs(pairs.iter().map(|(k, v)| (k.as_ref(), v.clone())));
3106    state.server.validate_authorization_request(&request).await
3107}
3108
3109/// RFC 6749 s4.1.1: the authorization endpoint.
3110async fn authorize_handler<S: Storage, C: Clock>(
3111    state: &Inner<S, C>,
3112    headers: &HeaderMap,
3113    uri: &Uri,
3114) -> Response {
3115    // WHEN THIS REQUEST ARRIVED, read before anything is looked up, and the instant the code
3116    // minted below is dated from.
3117    //
3118    // The decision this handler acts on is not always made during this handler. With a remembered
3119    // consent it was made when the user first approved, and the read that surfaces it happens
3120    // several awaits from here. Dating the code at ISSUANCE would let a standing approval outrank
3121    // a withdrawal recorded in between: the user clicks "remove this application" elsewhere, the
3122    // withdrawal cascades and records its barrier, this request resumes on its pre-withdrawal
3123    // snapshot, and a code dated NOW postdates the barrier — so the token is issued and its
3124    // refresh chain inherits the same instant and rotates long after the barrier is swept.
3125    //
3126    // Request entry is the latest instant this service can honestly claim: any withdrawal
3127    // recorded before it is one the consent read below would have seen, and any recorded after it
3128    // is later than this instant and refuses the write. See `UserApproval::granted_at`.
3129    let received_at = state.server.now();
3130
3131    let pairs = match parse_pairs(uri.query().unwrap_or_default()) {
3132        Ok(pairs) => pairs,
3133        Err(TooManyParameters) => return too_many_parameters(),
3134    };
3135
3136    let validated = match resolve_authorization_request(state, &pairs).await {
3137        Ok(v) => v,
3138        // RFC 6749 s4.1.2.1. `Direct` means the client or the redirect URI could not be
3139        // validated, so there is no address the server may safely send this to; it is rendered
3140        // to the user agent instead. `via_header` is false because the authorization endpoint has
3141        // no client authentication to challenge.
3142        Err(AuthorizationError::Direct(e)) => {
3143            return error_response(&e, false, &state.challenge);
3144        }
3145        Err(AuthorizationError::Redirect(r)) => return redirect(r.location()),
3146    };
3147
3148    // The resource owner. Without one there is nobody whose consent a code could represent.
3149    let subject = match state.subject(headers) {
3150        Some(s) => s,
3151        // Deliberately NOT an error redirect. `access_denied` at the client's redirect URI would
3152        // tell the client a user refused, when in truth no user was ever asked. A direct 403 says
3153        // that without lying to the client.
3154        //
3155        // TWO STATES, and they are not the same mistake, which is why they are no longer the same
3156        // sentence. Until the 0.9.1 audit both said "the host must supply a subject resolver", and
3157        // for the common one that is false: `SubjectResolver` documents `None` as "nobody is
3158        // logged in", so an ordinary signed-out browser navigation told a fully wired host to
3159        // install what it had already installed, and sent whoever read it to the wrong file.
3160        None => {
3161            return match state.subject.is_some() {
3162                true => {
3163                    unwired("no authenticated resource owner: nobody is signed in for this request")
3164                }
3165                false => unwired(
3166                    "no authenticated resource owner; the host must supply a subject resolver",
3167                ),
3168            }
3169        }
3170    };
3171
3172    // RFC 6749 s10.12: knowing WHO the user is does not establish that they agreed. Without a
3173    // approval seam this endpoint would mint a code on any cross-site top-level navigation a
3174    // logged-in user's browser is made to follow, so an unwired host refuses. This is a direct
3175    // 403 for the same reason as the missing subject above: no user refused, none was asked.
3176
3177    // What this user has already granted this client, handed to the resolver below. A storage
3178    // failure reads as "nothing remembered", which makes the host ask again: the failure mode of
3179    // this lookup has to be an extra prompt, never a skipped one.
3180    #[cfg(feature = "consent")]
3181    let remembered = state
3182        .server
3183        .remembered_consent(&validated.client_id, &subject)
3184        .await
3185        .unwrap_or(None);
3186
3187    let approval = match &state.approval {
3188        Some(resolver) => resolver(&ApprovalRequest {
3189            headers,
3190            subject: &subject,
3191            client_id: &validated.client_id,
3192            scope: &validated.scope,
3193            redirect_uri: &validated.redirect_uri,
3194            state: validated.state.as_deref(),
3195            resource: &validated.resource,
3196            #[cfg(feature = "rar")]
3197            authorization_details: &validated.authorization_details,
3198            uri,
3199            #[cfg(feature = "consent")]
3200            // Deref through the shared `Arc<ConsentRecord>` the storage seam now returns: the
3201            // resolver borrows for the length of the call and never needs the handle.
3202            remembered: remembered.as_deref(),
3203        }),
3204        None => {
3205            return unwired(
3206                "no approval step is configured; the host must supply an approval resolver \
3207                 (RFC 6749 s10.12)",
3208            )
3209        }
3210    };
3211    // Only ever set by the host's own `ApproveAndRemember`; see that variant's docs.
3212    #[cfg(feature = "consent")]
3213    let mut remember = false;
3214    match approval {
3215        ApprovalDecision::Approve => {}
3216        #[cfg(feature = "consent")]
3217        ApprovalDecision::ApproveAndRemember => remember = true,
3218        // A refusal is an answer the client is entitled to receive at its (validated) redirect
3219        // URI, which is exactly what RFC 6749 s4.1.2.1 `access_denied` is for.
3220        ApprovalDecision::Deny => return redirect(validated.denied().location()),
3221        ApprovalDecision::Respond(response) => return *response,
3222    }
3223
3224    // The host's report of how and when it authenticated this user, for RFC 9470 s4's parameters to
3225    // be enforced against. An unwired host reports `None`, which satisfies no requirement.
3226    #[cfg(feature = "consent")]
3227    let authentication = state.authentication.as_ref().and_then(|f| f(headers));
3228    // The requirement comes off the RESOLVED request, not off `pairs`. For a PAR or JAR request the
3229    // query holds only `client_id` plus the handle or the object, so reading `pairs` here dropped
3230    // `acr_values` and `max_age` entirely and silently disabled step-up for both (RFC 9126 s4, RFC
3231    // 9101 s6.3). A malformed `max_age` is now refused during validation, on the same redirect the
3232    // rest of the redirectable checks use.
3233    #[cfg(feature = "consent")]
3234    let issued = state
3235        .server
3236        .issue_authorization_code_with_authentication(
3237            // The assertion `UserApproval::granted_at` makes is exactly what this service has
3238            // just finished doing: the approval resolver returned `Approve` for THIS request, on
3239            // behalf of the subject the host's own resolver named. Nowhere else in this file may
3240            // mint one. It is dated from request entry rather than from now because the decision
3241            // may be a standing one; see `received_at` above.
3242            UserApproval::granted_at(&validated, subject.clone(), received_at),
3243            &validated.authentication_requirement,
3244            authentication.as_ref(),
3245        )
3246        .await;
3247    #[cfg(not(feature = "consent"))]
3248    let issued = state
3249        .server
3250        .issue_authorization_code(UserApproval::granted_at(&validated, subject, received_at))
3251        .await;
3252
3253    // AFTER issuance, and only on success: a consent records that the user granted something, and
3254    // nothing was granted if the code was refused.
3255    #[cfg(feature = "consent")]
3256    if remember && issued.is_ok() {
3257        // A failure to remember is not a failure to authorize. The user consented and the code is
3258        // already minted; turning that into an error would throw away an approval the user actually
3259        // gave, and the only consequence of the lost record is being asked again next time.
3260        let _ = state
3261            .server
3262            .record_consent(
3263                &validated.client_id,
3264                &subject,
3265                &validated.scope,
3266                &validated.resource,
3267                authentication,
3268            )
3269            .await;
3270    }
3271
3272    match issued {
3273        Ok(response) => redirect(response.location(&validated.redirect_uri)),
3274        Err(AuthorizationError::Direct(e)) => error_response(&e, false, &state.challenge),
3275        Err(AuthorizationError::Redirect(r)) => redirect(r.location()),
3276    }
3277}
3278
3279/// The authorization endpoint's answer when a seam the host had to wire is missing.
3280///
3281/// 403 with `access_denied` and a description naming the gap: the client learns the request was
3282/// not authorized, and the host's developer learns why without the server having invented a user
3283/// or a decision. Never a redirect, for the reason above.
3284fn unwired(why: &'static str) -> Response {
3285    let err = ErrorResponse::new(ErrorCode::AccessDenied).with_description(why);
3286    let mut resp = respond(StatusCode::FORBIDDEN, json_body(&err));
3287    let headers = resp.headers_mut();
3288    headers.insert(header::CONTENT_TYPE, json_content_type());
3289    no_store(headers);
3290    resp
3291}
3292
3293/// A 302 to `location`.
3294///
3295/// RFC 6749 s4.1.2 leaves the exact 3xx to the server; 302 is what the RFC's own examples show
3296/// and what every client understands.
3297///
3298/// `no_store`, because this is a credential-bearing response like any other on the token plane:
3299/// the `Location` of a successful authorization carries the authorization CODE and the `state`.
3300/// RFC 9111 s4.2.2 does not list 302 as heuristically cacheable, so a conforming shared cache will
3301/// not keep it — but `no-store` is what turns that from a hope about the intermediary into an
3302/// instruction, and every other credential-bearing constructor in this file already sends it.
3303///
3304/// THE FALLBACK IS REACHABLE, contrary to what this comment said until the 0.9.1 audit. The
3305/// appended parameters are percent-encoded by [`crate::authorization`], but the REGISTERED redirect
3306/// URI is pushed verbatim, so a URI with a space in it reaches `HeaderValue::from_str` and fails
3307/// it, AFTER the code has been minted and persisted.
3308///
3309/// WHICH DOOR IS OPEN was named wrongly here until 0.9.2, and the correction matters because it
3310/// tells the reader where to look. This said "only the RFC 7591 dynamic path validates it — a host
3311/// calling `register_client` directly supplies a bare `Vec<String>`".
3312/// [`crate::server::AuthorizationServer::register_client`] DOES validate, through the same
3313/// predicate the dynamic path uses (`crate::authorization::is_valid_resource_indicator`), and
3314/// `tests/authorization_code.rs` has held it to that since 0.9.1. Both of this crate's
3315/// registration entry points are therefore closed.
3316///
3317/// What is open is BELOW them: [`crate::store::Storage::put_client`] and
3318/// [`crate::store::Storage::compare_and_swap_client`] take a [`crate::client::Client`] as given,
3319/// they are public on a public trait, and `AuthorizationServer::store` hands the host the store to
3320/// call them on. A host that provisions clients by writing rows — directly, or by migrating a
3321/// legacy table, or by implementing `Storage` over a database it also writes from elsewhere — puts
3322/// a `redirect_uris` entry into circulation that no validator in this crate ever saw. That is the
3323/// path that ends here.
3324///
3325/// The 500 is the honest answer at that point; the fix belongs at provisioning, and the failure is
3326/// named here so that whoever meets it once knows where to look.
3327fn redirect(location: String) -> Response {
3328    match HeaderValue::from_str(&location) {
3329        Ok(value) => {
3330            let mut resp = respond(StatusCode::FOUND, Body::empty());
3331            let headers = resp.headers_mut();
3332            headers.insert(header::LOCATION, value);
3333            no_store(headers);
3334            resp
3335        }
3336        Err(_) => error_response(
3337            &ErrorResponse::new(ErrorCode::ServerError),
3338            false,
3339            &HeaderValue::from_static("Basic realm=\"oauth\""),
3340        ),
3341    }
3342}
3343
3344/// Constant-time equality, over SHA-256 digests so the loop bound does not depend on either
3345/// input's length.
3346///
3347/// A CSRF token is a secret the submitter is claiming to know, so comparing it with `==` leaks
3348/// the length of the match through timing exactly as a secret comparison would. This mirrors
3349/// `client::constant_time_eq`, which is private to that module; duplicating six lines is cheaper
3350/// than widening that function's visibility, and this one is exercised by its own test.
3351fn constant_time_eq(a: &str, b: &str) -> bool {
3352    let da = Sha256::digest(a.as_bytes());
3353    let db = Sha256::digest(b.as_bytes());
3354    let mut acc: u8 = 0;
3355    for i in 0..32 {
3356        acc |= da[i] ^ db[i];
3357    }
3358    acc == 0
3359}
3360
3361/// Whether the request body is `application/x-www-form-urlencoded`.
3362///
3363/// Required on the verification POST as defence in depth for RFC 6749 s10.12: it is one of the
3364/// three content types a cross-origin form or a no-preflight `fetch` may send, but demanding it
3365/// still removes every JSON or text body that could otherwise be smuggled here, and it costs a
3366/// conforming browser form nothing because that is exactly what a form sends.
3367fn is_form_urlencoded(headers: &HeaderMap) -> bool {
3368    headers
3369        .get(header::CONTENT_TYPE)
3370        .and_then(|v| v.to_str().ok())
3371        // Parameters are allowed (`; charset=utf-8`), so only the media type is compared.
3372        .map(|v| v.split(';').next().unwrap_or_default().trim())
3373        .is_some_and(|mime| mime.eq_ignore_ascii_case("application/x-www-form-urlencoded"))
3374}
3375
3376/// Whether this POST demonstrably came from a document on the issuer's own origin.
3377///
3378/// RFC 6749 s10.12, RFC 9700 s4.7. A browser sends `Origin` on every POST and `Sec-Fetch-Site` on
3379/// every request it makes from a document, so a genuine submission of the form this server
3380/// rendered carries at least one of them and both say "us". A cross-site forced submission
3381/// carries the ATTACKER's origin, which is the whole signal. Absence is refused rather than
3382/// waved through: on a browser-facing endpoint absence means a client that is not a browser, and
3383/// a request that is not from a browser has no session cookie worth forging.
3384fn same_origin(headers: &HeaderMap, origin: &str) -> bool {
3385    // `Sec-Fetch-Site` is the more precise of the two where it exists, so it is decisive when
3386    // present: `same-origin` is a submission from our own page, and anything else (including
3387    // `none`, a user-typed navigation) is not.
3388    if let Some(site) = headers.get("sec-fetch-site").and_then(|v| v.to_str().ok()) {
3389        return site.eq_ignore_ascii_case("same-origin");
3390    }
3391    headers
3392        .get(header::ORIGIN)
3393        .and_then(|v| v.to_str().ok())
3394        .is_some_and(|value| value.eq_ignore_ascii_case(origin))
3395}
3396
3397/// What the RFC 8628 section 5.1 throttle has already been told about the user code a page is
3398/// about to display.
3399///
3400/// ONE CODE ENTRY IS CHARGED ONCE, however many times a single request resolves it. A wrong code
3401/// posted with `action=approve` is resolved TWICE — once by
3402/// [`crate::server::AuthorizationServer::approve_device`], and again by the re-render that reports
3403/// the failure — and charging both halved every budget a host configured: a 200-unit-per-minute
3404/// limiter documented as allowing twenty wrong entries a minute allowed ten. The error was
3405/// fail-closed, which is why nothing noticed it. This enum is how the second resolution says "that
3406/// entry is already counted" without any handler having to remember the rule.
3407#[derive(Clone, Copy)]
3408enum CodeEntry {
3409    /// Nothing on this request has counted this entry yet, so the lookup counts it. Every
3410    /// separately-attackable entry point is this: the RFC 8628 s3.3.1 deep link, and the
3411    /// stage-one POST that types a code to see what it is for. Making those free would let an
3412    /// attacker walk the code space for nothing, which is a worse defect than the double charge.
3413    Uncharged,
3414    /// A handler earlier in THIS request already counted this exact entry, so the lookup must not
3415    /// count it a second time.
3416    AlreadyCharged,
3417    /// The throttle already REFUSED this entry earlier in this request. There is nothing to look
3418    /// up: a lookup would answer, for free, the one question the refusal exists to leave
3419    /// unanswered.
3420    Refused,
3421}
3422
3423/// The throttle refused to answer for this user code (RFC 8628 section 5.1).
3424///
3425/// A distinct outcome from "nothing pending matches", and the distinction is the point: they are
3426/// the same value to the ATTACKER (see [`THROTTLED_MESSAGE`]) but they are not the same value to
3427/// this code, which must not tell a user with a perfectly good code that it was not recognised.
3428struct Throttled;
3429
3430/// The pending grant behind an entered user code, plus the client's display name, for the
3431/// consent screen. `Ok(None)` when nothing pending matches.
3432///
3433/// Read through the public storage seam rather than through a server method, and deliberately
3434/// NOT treated as authoritative: expiry and state are re-checked inside
3435/// `AuthorizationServer::approve_device` against the server's own clock when the user actually
3436/// approves. This lookup exists to DISPLAY, so being one moment stale costs nothing.
3437async fn pending_grant<S: Storage, C: Clock>(
3438    state: &Inner<S, C>,
3439    entered_user_code: &str,
3440    entry: CodeEntry,
3441) -> Result<Option<(DeviceGrant, Option<String>)>, Throttled> {
3442    // THIS LOOKUP IS A GUESSING ORACLE, so it goes through the host's throttle exactly as
3443    // AuthorizationServer::pending_grant_by_user_code does. The response distinguishes a live
3444    // pending code from an unknown one perfectly (one renders the client and the scope, the other
3445    // says the code was not recognised), so without this a host that implemented the RateLimiter
3446    // seam correctly would still see nothing while an attacker walked the code space with GETs and
3447    // spent a single throttled POST on the one that hit.
3448    //
3449    // RFC 8628 s5.1 makes the user code's entropy adequate only IN COMBINATION WITH rate limiting
3450    // of code entry, and s5.4 names this exact URL, the verification_uri_complete deep link, as
3451    // the higher-risk entry point.
3452    //
3453    // `entry` decides whether THIS resolution is the one that pays; see [`CodeEntry`].
3454    let hooks = state.server.hooks();
3455    match entry {
3456        CodeEntry::Refused => return Err(Throttled),
3457        CodeEntry::Uncharged => {
3458            if hooks.check(Attempt::DeviceUserCodeEntry) == RateLimitDecision::Deny {
3459                return Err(Throttled);
3460            }
3461        }
3462        // The check is skipped along with the charge, and deliberately: the handler that charged
3463        // this entry was ALLOWED through, so asking the limiter again could only refuse a request
3464        // it has already accepted, halfway through answering it.
3465        CodeEntry::AlreadyCharged => {}
3466    }
3467    let normalized = normalize_user_code(entered_user_code);
3468    let grant = state
3469        .server
3470        .store()
3471        .find_device_grant_by_user_code(&normalized)
3472        .await
3473        .ok()
3474        .flatten()
3475        .filter(|g| g.state == DeviceGrantState::Pending);
3476    // Report the outcome, because a guessing attack shows up in FAILURES, not in traffic volume.
3477    // Only when this resolution is the one paying for the entry: a second report of one entry is
3478    // a second charge, which is the whole defect [`CodeEntry`] exists to describe.
3479    if matches!(entry, CodeEntry::Uncharged) {
3480        hooks.record(
3481            Attempt::DeviceUserCodeEntry,
3482            if grant.is_some() {
3483                AttemptOutcome::Succeeded
3484            } else {
3485                AttemptOutcome::Failed
3486            },
3487        );
3488    }
3489    let Some(grant) = grant else {
3490        return Ok(None);
3491    };
3492    let name = state
3493        .server
3494        .store()
3495        .get_client(&grant.client_id)
3496        .await
3497        .ok()
3498        .flatten()
3499        // Cloned out of the shared `Arc<Client>` (see `Storage::get_client`): this renders a human
3500        // facing verification page, so one string copy per page view is not a cost worth shaping
3501        // the storage seam around.
3502        //
3503        // A storage FAILURE here is deliberately not fatal to the page, and the reason is what the
3504        // page actually shows: `verification_page` renders the `client_id` and the scope whatever
3505        // happens, and falls back to the `client_id` when there is no name, because a registration
3506        // is entitled to have none and because a pretty name is the part a phishing registration
3507        // chooses. So a failed lookup degrades to exactly the page a nameless registration gets,
3508        // which still identifies the client (RFC 8628 s3.3) and still requires an affirmative
3509        // click. Refusing to render at all would mean an unrelated store hiccup ended a login the
3510        // user is in the middle of, and would do it on the ONE screen where the user is watching.
3511        .and_then(|c| c.name.clone());
3512    Ok(Some((grant, name)))
3513}
3514
3515/// What a user is told when the RFC 8628 section 5.1 throttle refused their code entry.
3516///
3517/// One string for both the POST and the GET path, because they owe the user the same answer. It
3518/// says nothing about whether the code was real: that is the question the throttle exists to stop
3519/// being asked, and a page that answered it for refused attempts would hand back the oracle the
3520/// refusal just took away.
3521const THROTTLED_MESSAGE: &str = "Too many attempts. Wait and try again.";
3522
3523/// Render the verification page for whatever `entered` resolves to, with a fresh CSRF token.
3524///
3525/// `status` and `message` are what the CALLER already worked out, and they win: the submit handler
3526/// has an outcome in hand and has chosen the status to match it. They are absent on the display
3527/// paths, which is where the lookup below gets to decide.
3528async fn render_verification<S: Storage, C: Clock>(
3529    state: &Inner<S, C>,
3530    headers: &HeaderMap,
3531    entered: &str,
3532    status: StatusCode,
3533    message: Option<&str>,
3534    entry: CodeEntry,
3535) -> Response {
3536    let csrf = match &state.verification {
3537        // RFC 6749 s10.12 is the AS's obligation, so an unwired host is served an explanation
3538        // and NO form. A form that works and is forgeable is worse than no form at all.
3539        VerificationProtection::Unwired => {
3540            return html_response(
3541                StatusCode::INTERNAL_SERVER_ERROR,
3542                verification_message(
3543                    "This server is not configured to accept device approvals. The host must \
3544                     supply CSRF tokens (RFC 6749 s10.12).",
3545                ),
3546            )
3547        }
3548        VerificationProtection::Tokens { issue, .. } => match issue(headers) {
3549            Some(token) => Some(token),
3550            // No session means no token can be bound to one, so there is nothing to render.
3551            None => {
3552                return html_response(
3553                    StatusCode::FORBIDDEN,
3554                    verification_message("You are not signed in."),
3555                )
3556            }
3557        },
3558        VerificationProtection::Disabled => None,
3559    };
3560
3561    let looked_up = match entered.is_empty() {
3562        true => Ok(None),
3563        false => pending_grant(state, entered, entry).await,
3564    };
3565    // A code that was typed but matches nothing pending is worth saying so, rather than rendering
3566    // a consent screen with nothing on it — but a code the THROTTLE refused matches nothing for a
3567    // completely different reason, and until 0.9.1 this page told those users their perfectly good
3568    // code was not recognised, at HTTP 200. The sibling POST path has always distinguished the two
3569    // (see `verification_submit_handler`); this is the deep-linked entry point RFC 8628 s5.4 warns
3570    // about, so it is the one that most wants the honest status.
3571    let (status, message) = match (message, &looked_up) {
3572        (Some(m), _) => (status, Some(m)),
3573        (None, Err(Throttled)) => (StatusCode::TOO_MANY_REQUESTS, Some(THROTTLED_MESSAGE)),
3574        (None, Ok(None)) if !entered.is_empty() => (status, Some("That code was not recognised.")),
3575        (None, _) => (status, None),
3576    };
3577    let grant = looked_up.unwrap_or(None);
3578    html_response(
3579        status,
3580        verification_page(entered, message, grant.as_ref(), csrf.as_deref()),
3581    )
3582}
3583
3584/// RFC 8628 s3.3: the page a user visits to enter the code shown on the device.
3585async fn verification_page_handler<S: Storage, C: Clock>(
3586    state: &Inner<S, C>,
3587    headers: &HeaderMap,
3588    uri: &Uri,
3589) -> Response {
3590    // `verification_uri_complete` (RFC 8628 s3.3.1) carries the code in the query so the user
3591    // does not retype it; prefilling is the entire point of that member. Prefilling is ALL it
3592    // does: RFC 8628 s5.4 (Remote Phishing) is explicit that this deep link removes the one
3593    // friction point that made the attack harder, so what it lands on has to be a page naming
3594    // the client and the scope, with the approval still one deliberate click away.
3595    let pairs = match parse_pairs(uri.query().unwrap_or_default()) {
3596        Ok(pairs) => pairs,
3597        Err(TooManyParameters) => return too_many_parameters(),
3598    };
3599    let prefill = param(&pairs, "user_code").unwrap_or_default();
3600    // A separately-attackable entry point, and the one RFC 8628 s5.4 singles out: this request has
3601    // charged nothing yet, so the lookup charges it.
3602    render_verification(
3603        state,
3604        headers,
3605        prefill,
3606        StatusCode::OK,
3607        None,
3608        CodeEntry::Uncharged,
3609    )
3610    .await
3611}
3612
3613/// The verification form's submission: the user has entered the code shown on their device.
3614async fn verification_submit_handler<S: Storage, C: Clock>(
3615    state: &Inner<S, C>,
3616    headers: &HeaderMap,
3617    body: &Bytes,
3618) -> Response {
3619    // Checked before the body is even parsed, and before the unprotected escape hatch is
3620    // consulted, because it is the one guard that costs a conforming browser nothing.
3621    if !is_form_urlencoded(headers) {
3622        return html_response(
3623            StatusCode::UNSUPPORTED_MEDIA_TYPE,
3624            verification_message("Expected an application/x-www-form-urlencoded submission."),
3625        );
3626    }
3627
3628    let text = String::from_utf8_lossy(body);
3629    let form = match parse_pairs(&text) {
3630        Ok(form) => form,
3631        Err(TooManyParameters) => return too_many_parameters(),
3632    };
3633    let user_code = param(&form, "user_code").unwrap_or_default();
3634
3635    // RFC 6749 s10.12, defence in depth, in the order that refuses soonest. A host that has
3636    // explicitly disabled these is not browser-facing and is answering for that itself.
3637    let protected = !matches!(state.verification, VerificationProtection::Disabled);
3638    if protected {
3639        if !same_origin(headers, &state.origin) {
3640            return html_response(
3641                StatusCode::FORBIDDEN,
3642                verification_message("That request did not come from this site."),
3643            );
3644        }
3645        let expected =
3646            match &state.verification {
3647                VerificationProtection::Tokens { consume, .. } => consume(headers),
3648                // No CSRF seam: nothing to compare against, so nothing is approved. The GET above
3649                // already refused to render a form, so this is the direct-POST path.
3650                VerificationProtection::Unwired => return html_response(
3651                    StatusCode::INTERNAL_SERVER_ERROR,
3652                    verification_message(
3653                        "This server is not configured to accept device approvals. The host must \
3654                         supply CSRF tokens (RFC 6749 s10.12).",
3655                    ),
3656                ),
3657                VerificationProtection::Disabled => None,
3658            };
3659        let presented = param(&form, "csrf_token").unwrap_or_default();
3660        // Constant time, and `expected` was consumed above, so a token works exactly once.
3661        let ok = expected.is_some_and(|e| constant_time_eq(&e, presented));
3662        if !ok {
3663            return html_response(
3664                StatusCode::FORBIDDEN,
3665                verification_message("That form has expired. Start again."),
3666            );
3667        }
3668    }
3669
3670    if user_code.is_empty() {
3671        // No code was entered, so there is nothing to look up and nothing to charge either way.
3672        return render_verification(
3673            state,
3674            headers,
3675            "",
3676            StatusCode::BAD_REQUEST,
3677            Some("Enter the code shown on your device."),
3678            CodeEntry::Uncharged,
3679        )
3680        .await;
3681    }
3682
3683    // RFC 8628 s3.3 requires an explicit confirmation step, so approval needs an affirmative
3684    // action and nothing else does it. A submission with no action re-renders the consent screen
3685    // for the code just entered, which is the second stage of the two-stage form: type a code,
3686    // see what it is for, THEN decide. The escape hatch keeps the old approve-by-default shape,
3687    // because the non-browser harness that needs it posts a bare user_code.
3688    let action = param(&form, "action").unwrap_or_default();
3689    let denied = action == "deny";
3690    let approved = action == "approve" || !protected;
3691    if !denied && !approved {
3692        // Stage one of the two-stage form: this request has resolved the code nowhere else, and a
3693        // POST of a guessed code is exactly what the throttle counts, so it pays here.
3694        return render_verification(
3695            state,
3696            headers,
3697            user_code,
3698            StatusCode::OK,
3699            None,
3700            CodeEntry::Uncharged,
3701        )
3702        .await;
3703    }
3704
3705    // Approval binds the grant to a USER, so the same rule as the authorization endpoint applies:
3706    // without an authenticated resource owner there is nobody to bind it to.
3707    let subject = match state.subject(headers) {
3708        Some(s) => s,
3709        None => {
3710            return html_response(
3711                StatusCode::FORBIDDEN,
3712                verification_message("You are not signed in."),
3713            )
3714        }
3715    };
3716
3717    // RFC 8628 s3.3 leaves the deny path to the implementation; offering it matters, because a
3718    // user who did not start the flow needs a way to say so.
3719    let outcome = if denied {
3720        state.server.deny_device(user_code).await
3721    } else {
3722        state.server.approve_device(user_code, subject).await
3723    };
3724
3725    match outcome {
3726        Ok(()) if denied => html_response(StatusCode::OK, verification_message("Request denied.")),
3727        Ok(()) => html_response(
3728            StatusCode::OK,
3729            verification_message("Approved. You can return to your device."),
3730        ),
3731        // These are NOT OAuth wire errors: RFC 8628 leaves the verification interaction to the
3732        // implementation, and the audience here is a human, not a client library.
3733        Err(e) => {
3734            let message = match e {
3735                DeviceApprovalError::UnknownUserCode => "That code was not recognised.",
3736                DeviceApprovalError::Expired => "That code has expired. Start again on the device.",
3737                DeviceApprovalError::NotPending => "That code has already been used.",
3738                DeviceApprovalError::Storage(_) => "Something went wrong. Try again.",
3739                // The host's own limiter refused this before the code was even looked up
3740                // (RFC 8628 section 5.1). Deliberately says nothing about whether the code was
3741                // real: that is the question the throttle exists to stop being asked.
3742                DeviceApprovalError::RateLimited => THROTTLED_MESSAGE,
3743            };
3744            let status = match e {
3745                DeviceApprovalError::Storage(_) => StatusCode::INTERNAL_SERVER_ERROR,
3746                // 429 is the honest status and the one a host's own reverse proxy metrics will
3747                // already be counting.
3748                DeviceApprovalError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
3749                _ => StatusCode::BAD_REQUEST,
3750            };
3751            // The CSRF token was consumed above, so this re-render mints a fresh one; without
3752            // that a mistyped code would leave the user with a form that can never be submitted.
3753            //
3754            // `approve_device`/`deny_device` resolved this exact code entry a few lines up, and
3755            // charged the throttle for it. The re-render must not charge it again — that is the
3756            // double charge `CodeEntry` documents — and when the throttle REFUSED, it must not
3757            // look the code up at all, because a refused attempt that still rendered the client
3758            // and the scope would be the oracle handed back.
3759            let entry = match e {
3760                DeviceApprovalError::RateLimited => CodeEntry::Refused,
3761                _ => CodeEntry::AlreadyCharged,
3762            };
3763            render_verification(state, headers, user_code, status, Some(message), entry).await
3764        }
3765    }
3766}
3767
3768/// Escape a value for an RFC 9110 s5.6.4 quoted-string: `\` and `"` become `\\` and `\"`.
3769///
3770/// Borrows when there is nothing to escape, which is every well-formed issuer, so the ordinary 401
3771/// costs no allocation. See the `challenge` construction for why a value this crate treats as a URL
3772/// is nonetheless escaped.
3773fn escape_quoted_string(value: &str) -> Cow<'_, str> {
3774    if !value.contains(['"', '\\']) {
3775        return Cow::Borrowed(value);
3776    }
3777    let mut out = String::with_capacity(value.len() + 8);
3778    for c in value.chars() {
3779        if c == '"' || c == '\\' {
3780            out.push('\\');
3781        }
3782        out.push(c);
3783    }
3784    Cow::Owned(out)
3785}
3786
3787/// Escape the five characters that can break out of HTML text or an attribute value. The user
3788/// code is echoed back into the form, and it arrived from the network.
3789fn escape_html(value: &str, out: &mut String) {
3790    for c in value.chars() {
3791        match c {
3792            '&' => out.push_str("&amp;"),
3793            '<' => out.push_str("&lt;"),
3794            '>' => out.push_str("&gt;"),
3795            '"' => out.push_str("&quot;"),
3796            '\'' => out.push_str("&#x27;"),
3797            other => out.push(other),
3798        }
3799    }
3800}
3801
3802const PAGE_HEAD: &str = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\">\
3803     <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\
3804     <title>Device authorization</title></head><body><h1>Device authorization</h1>";
3805
3806/// The minimal verification form. Deliberately unstyled and dependency-free: a host that wants a
3807/// branded page serves its own route and calls [`AuthorizationServer::approve_device`] directly.
3808///
3809/// Two stages, and the second is not optional. With no pending grant behind the entered code the
3810/// page can only ask for the code, so its single button says CONTINUE and approves nothing. With
3811/// a grant it names the client and the exact scope being handed over, and only then offers
3812/// Approve and Deny. RFC 8628 s3.3: "the authorization server SHOULD display information about
3813/// the device"; s5.4 is why the deep link may not skip it.
3814///
3815/// Everything variable here is escaped. The user code came off the network, and a client name is
3816/// registration data, which under RFC 7591 dynamic client registration is chosen by whoever
3817/// registered the client.
3818fn verification_page(
3819    prefill: &str,
3820    message: Option<&str>,
3821    grant: Option<&(DeviceGrant, Option<String>)>,
3822    csrf: Option<&str>,
3823) -> String {
3824    let mut html = String::with_capacity(768);
3825    html.push_str(PAGE_HEAD);
3826    if let Some(message) = message {
3827        html.push_str("<p>");
3828        escape_html(message, &mut html);
3829        html.push_str("</p>");
3830    }
3831    html.push_str("<form method=\"post\">");
3832    if let Some(token) = csrf {
3833        html.push_str("<input type=\"hidden\" name=\"csrf_token\" value=\"");
3834        escape_html(token, &mut html);
3835        html.push_str("\">");
3836    }
3837    match grant {
3838        None => {
3839            html.push_str(
3840                "<label for=\"user_code\">Code shown on your device</label>\
3841                 <input id=\"user_code\" name=\"user_code\" autocomplete=\"off\" \
3842                 spellcheck=\"false\" value=\"",
3843            );
3844            escape_html(prefill, &mut html);
3845            html.push_str("\"><button type=\"submit\">Continue</button>");
3846        }
3847        Some((grant, name)) => {
3848            // The name is a display convenience; the client_id is the identity, so it is shown
3849            // either way. A pretty name is exactly what a phishing registration would choose.
3850            html.push_str("<p>The application <strong>");
3851            escape_html(
3852                name.as_deref().unwrap_or_else(|| grant.client_id.as_str()),
3853                &mut html,
3854            );
3855            html.push_str("</strong> (<code>");
3856            escape_html(grant.client_id.as_str(), &mut html);
3857            html.push_str("</code>) is asking to access your account.</p><p>It will be allowed: ");
3858            let scope = grant.scope.to_string();
3859            if scope.is_empty() {
3860                html.push_str("no scopes");
3861            } else {
3862                escape_html(&scope, &mut html);
3863            }
3864            html.push_str("</p><p>Code on your device: <code>");
3865            escape_html(&grant.user_code, &mut html);
3866            html.push_str("</code></p><input type=\"hidden\" name=\"user_code\" value=\"");
3867            escape_html(&grant.user_code, &mut html);
3868            html.push_str(
3869                "\"><button type=\"submit\" name=\"action\" value=\"approve\">Approve</button>\
3870                 <button type=\"submit\" name=\"action\" value=\"deny\">Deny</button>",
3871            );
3872        }
3873    }
3874    html.push_str("</form></body></html>");
3875    html
3876}
3877
3878/// A page that is only a message: an outcome, or a refusal with no form to offer.
3879fn verification_message(message: &str) -> String {
3880    let mut html = String::with_capacity(256);
3881    html.push_str(
3882        "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\">\
3883         <title>Device authorization</title></head><body><p>",
3884    );
3885    escape_html(message, &mut html);
3886    html.push_str("</p></body></html>");
3887    html
3888}
3889
3890#[cfg(test)]
3891#[path = "tests/http.rs"]
3892mod tests;