Skip to main content

oauth_as/
server.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (C) 2026 Matthew Jackson
3
4//! The authorization server itself: configuration, the clock seam, and the grant state machines.
5//!
6//! Construction is the crate's ONLY allocation entry point (see the crate docs on zero cost until
7//! enabled): a host that never constructs [`AuthorizationServer`] pays nothing. There is no
8//! background task; every state transition happens inside a host-driven call.
9
10use std::fmt;
11use std::time::{Duration, SystemTime};
12
13use crate::authorization::{
14    AuthorizationCodeRecord, AuthorizationCodeState, AuthorizationError,
15    AuthorizationErrorRedirect, AuthorizationRequest, AuthorizationResponse, CodeChallengeMethod,
16    ValidatedAuthorizationRequest,
17};
18use crate::client::{Client, ClientId};
19#[cfg(feature = "client-assertion")]
20use crate::client_assertion::{verify_assertion, CLIENT_ASSERTION_TYPE};
21use crate::device::{
22    normalize_user_code, DeviceAuthorizationResponse, DeviceGrant, DeviceGrantState,
23};
24#[cfg(feature = "dpop")]
25use crate::dpop::verify_proof;
26use crate::error::{ErrorCode, ErrorResponse};
27use crate::events::{
28    Attempt, AttemptOutcome, ClientAuthFailure, Event, EventSink, Hooks, RateLimitDecision,
29    RateLimiter,
30};
31use crate::grant::GrantType;
32// The crate's ONE lower-case hex encoder (`client::SecretHash` is the other caller). Aliased to
33// the name this module used when it carried its own copy, which is also the name the measurement
34// in `crate::hex` is written against.
35use crate::hex::encode as hex_encode;
36#[cfg(feature = "jwt")]
37use crate::jwt::{AccessTokenClaims, AccessTokenFormat, Jwks};
38use crate::scope::ScopeSet;
39use crate::store::{Storage, StorageError};
40use crate::token::{
41    IntrospectionResponse, IssuedToken, RefreshTokenRecord, RefreshTokenState, TokenResponse,
42    TokenType, TokenTypeHint,
43};
44
45/// Seconds since the Unix epoch, for the RFC 7519 `exp` / `iat` style claims RFC 7662 reuses.
46/// A pre-epoch instant is not representable in that encoding, so it is reported as absent rather
47/// than wrapped into a misleading number.
48/// `base + span`, saturating instead of panicking.
49///
50/// `SystemTime + Duration` PANICS on overflow, and every caller adds a HOST-CONFIGURED `Duration`
51/// to `now`. `ServerConfig`'s TTL fields are public and nothing validates them, so a deployment
52/// that sets one from a config file holding an out-of-range value would panic on an ordinary
53/// request rather than fail at startup. Saturating yields a deadline no clock will reach, which
54/// for an expiry means "does not expire by time" — which is what the absurd configuration asked
55/// for.
56///
57/// The ATTACKER-supplied durations in this crate (`dpop`, `client_assertion`) already use
58/// `checked_add`, with the reasoning written beside them. This is the host-supplied half of the
59/// same rule, and it was missing.
60pub(crate) fn saturating_deadline(base: SystemTime, span: std::time::Duration) -> SystemTime {
61    if let Some(exact) = base.checked_add(span) {
62        return exact;
63    }
64    // Halve until it fits, accumulating what does: this lands within a second of the platform's
65    // ceiling without needing to know what that ceiling is.
66    let mut out = base;
67    let mut span = span;
68    while span > std::time::Duration::from_secs(1) {
69        span /= 2;
70        if let Some(next) = out.checked_add(span) {
71            out = next;
72        }
73    }
74    out
75}
76
77pub(crate) fn unix_seconds(t: SystemTime) -> Option<u64> {
78    t.duration_since(std::time::UNIX_EPOCH)
79        .ok()
80        .map(|d| d.as_secs())
81}
82
83/// The time source. Injectable so grant expiry and poll pacing are testable without sleeping;
84/// production hosts use [`SystemClock`].
85pub trait Clock: Send + Sync {
86    /// The current instant.
87    fn now(&self) -> SystemTime;
88}
89
90/// The real clock.
91#[derive(Debug, Clone, Copy, Default)]
92pub struct SystemClock;
93
94impl Clock for SystemClock {
95    fn now(&self) -> SystemTime {
96        SystemTime::now()
97    }
98}
99
100/// Server configuration. [`ServerConfig::new`] fills RFC-shaped defaults; every field is public so
101/// hosts override what they need.
102#[derive(Debug, Clone, PartialEq, Eq)]
103/// `#[non_exhaustive]`: this struct's field set VARIES WITH CARGO FEATURES, so a host that writes a
104/// full struct literal has a build that breaks the day anything in their dependency graph enables a
105/// feature they did not ask for. Construct with `new()` and assign the fields you want. This is the
106/// one attribute on this type that cannot be added after publication, because by then somebody's
107/// struct literal is in production.
108#[non_exhaustive]
109pub struct ServerConfig {
110    /// The issuer identifier (RFC 8414 `issuer`): the canonical `https` URL of this AS.
111    ///
112    /// RFC 8414 section 2 requires the `https` scheme in production. This crate does NOT enforce
113    /// it, because the same code has to be runnable over plain HTTP on loopback for conformance
114    /// runs and local development; enforcing transport security is the host's job, and the host
115    /// is the only party that knows whether it is behind a TLS terminator.
116    pub issuer: String,
117    /// Where a user goes to enter a device user code (RFC 8628 `verification_uri`).
118    pub verification_uri: String,
119    /// RFC 8414 `authorization_endpoint`. `None` derives `{issuer}/authorize`.
120    pub authorization_endpoint: Option<String>,
121    /// RFC 8414 `token_endpoint`. `None` derives `{issuer}/token`.
122    pub token_endpoint: Option<String>,
123    /// RFC 8628 `device_authorization_endpoint`. `None` derives `{issuer}/device_authorization`.
124    pub device_authorization_endpoint: Option<String>,
125    /// RFC 7662 `introspection_endpoint`. `None` derives `{issuer}/introspect`.
126    pub introspection_endpoint: Option<String>,
127    /// RFC 7009 `revocation_endpoint`. `None` derives `{issuer}/revoke`.
128    pub revocation_endpoint: Option<String>,
129    /// RFC 8414 `jwks_uri`. `None` (the default) means this server publishes no keys, which is
130    /// the truth for opaque access tokens.
131    pub jwks_uri: Option<String>,
132    /// RFC 7591 dynamic client registration. `None` is the DEFAULT and means registration is OFF:
133    /// no `registration_endpoint` is advertised, no route is served, and
134    /// [`AuthorizationServer::register_dynamic_client`] answers
135    /// [`crate::registration::RegistrationFailure::Disabled`].
136    ///
137    /// Turning it on is meant to be a sentence somebody wrote and a reviewer can find:
138    /// `config.registration = Some(Box::new(RegistrationConfig::new()))`. RFC 7591 section 5 is
139    /// why (an open registration endpoint lets anyone mint a client), and enabling it is still not
140    /// sufficient: a [`crate::registration::RegistrationPolicy`] must also be installed or every
141    /// registration is refused. See the [`crate::registration`] module docs.
142    ///
143    /// BOXED so that the overwhelmingly common `None` costs one null pointer on every
144    /// [`ServerConfig`] rather than the whole struct, and allocates nothing.
145    pub registration: Option<Box<crate::registration::RegistrationConfig>>,
146    /// RFC 9126 pushed authorization requests. `None` is the DEFAULT and means PAR is OFF: no
147    /// `pushed_authorization_request_endpoint` is advertised and
148    /// [`AuthorizationServer::pushed_authorization_request`] refuses.
149    ///
150    /// BOXED for the same reason as [`ServerConfig::registration`]: the overwhelmingly common
151    /// `None` costs one null pointer on every [`ServerConfig`] rather than the whole struct, and
152    /// allocates nothing.
153    #[cfg(feature = "par")]
154    pub par: Option<Box<crate::par::ParConfig>>,
155    /// RFC 9101 signed request objects. `None` is the DEFAULT and means JAR is OFF: a `request`
156    /// parameter is answered with `request_not_supported` rather than parsed.
157    #[cfg(feature = "jar")]
158    pub jar: Option<Box<crate::par::JarConfig>>,
159    /// draft-ietf-oauth-client-id-metadata-document-01 client identifier metadata documents. `None`
160    /// is the DEFAULT and means the mechanism is OFF: the RFC 8414 document says
161    /// `client_id_metadata_document_supported: false`.
162    ///
163    /// SETTING IT IS A CLAIM ABOUT THE HOST, not about this crate, and that is why it is a config
164    /// field rather than a constant derived from the cargo feature. This crate performs no fetch
165    /// (see [`crate::cimd`]), so compiling the feature in proves only that the VALIDATOR is
166    /// available; whether this deployment actually dereferences a client identifier URL is
167    /// something only the host knows. Deriving the advertised member from the feature would
168    /// publish a capability the build might always refuse, which is a defect shape this crate has
169    /// already shipped twice.
170    ///
171    /// BOXED for the same reason as [`ServerConfig::registration`].
172    #[cfg(feature = "cimd")]
173    pub cimd: Option<Box<crate::cimd::CimdPolicy>>,
174    /// RFC 8414 `scopes_supported`. `None` omits the member rather than claiming an empty
175    /// catalogue.
176    pub scopes_supported: Option<Vec<String>>,
177    /// The RFC 8707 resource indicators this server is WILLING to issue tokens for.
178    ///
179    /// `None` (the default) means no restriction, which is the pre-existing behaviour and is why
180    /// it is the default: turning refusal on by default would break every deployment already
181    /// using resource indicators. It is also why `None` is a real risk rather than a
182    /// neutral one, and the risk is worth stating here rather than in a changelog.
183    ///
184    /// `Option<Box<[Box<str>]>>` rather than `Vec<String>` so a host that never sets it pays ONE
185    /// pointer on every `ServerConfig`, not a 24 byte vector header. The list is written once at
186    /// construction and only ever iterated.
187    ///
188    /// RFC 8707 section 2 requires `invalid_target` when the server "is unwilling or unable to
189    /// issue an access token" for a requested resource. With this empty, the server has no notion
190    /// of unwilling: any syntactically valid absolute URI is accepted. Under the `jwt` feature the
191    /// requested resource then REPLACES the configured audience in the RFC 9068 `aud` claim, so
192    /// any client can obtain a token this server signed, carrying another resource server's
193    /// identifier in `aud`. That server verifies the signature against our JWKS, sees its own
194    /// identifier, and authorises.
195    ///
196    /// A deployment serving more than one resource server should set this.
197    pub allowed_resources: Option<Box<[Box<str>]>>,
198    /// Which registered clients are RESOURCE SERVERS, and which RFC 8707 resource identifiers each
199    /// one answers for. This is what opens the channel RFC 7662 section 1 describes, in which the
200    /// specification "allows authorized protected resources to query the authorization server";
201    /// empty (the default) means this server introspects for
202    /// the token's own client and nobody else, which is what it did through 0.9.1.
203    ///
204    /// A resource server is NOT a new kind of principal. It registers as an ordinary confidential
205    /// [`crate::Client`] and authenticates to the introspection endpoint with whatever this build
206    /// accepts from any client -- `client_secret_basic`, `client_secret_post`, RFC 7523
207    /// `private_key_jwt` or `client_secret_jwt`, RFC 8705 mutual TLS -- through the same
208    /// `authenticate_client` every other endpoint uses. Section 2.1 requires the endpoint be
209    /// protected; reusing the client credential machinery is how it is protected, and inventing a
210    /// second credential type would have meant a second thing to get constant-time comparison,
211    /// rotation and revocation right on.
212    ///
213    /// What this adds on top of authentication is AUTHORIZATION, and that is the part that is not
214    /// optional. Authenticating as a resource server must not mean reading every token in the
215    /// store: an introspection endpoint that answers any authenticated resource server about any
216    /// token is a token-scanning oracle, which is section 4's warning with a credential stapled to
217    /// it. So a resource server is answered about a token ONLY when the token's own
218    /// [`crate::IssuedToken::resource`] set names one of the identifiers registered here.
219    ///
220    /// AN EMPTY `resource` ON THE TOKEN NAMES NOBODY, AND IS REFUSED TO EVERY RESOURCE SERVER.
221    /// That is the same fail-open reading [`crate::jwt::Audience::names_a_resource_server`] exists
222    /// to refuse, arriving through the other door: a grant that requested no resource indicator is
223    /// restricted to nothing in particular, and reading "restricted to nothing in particular" as
224    /// "so anyone may ask about it" would hand every resource server in the deployment every token
225    /// that did not happen to use RFC 8707. The token's own client can still introspect it, which
226    /// is the pre-0.9.2 behaviour and is unchanged.
227    ///
228    /// # WHAT THIS COSTS YOUR RATE LIMITER, and what to set
229    ///
230    /// SETTING THIS CHANGES THE TRAFFIC SHAPE AT THE CLIENT-AUTHENTICATION BUDGET, and it is the
231    /// one consequence of registering a resource server that is not visible from anything else on
232    /// this page. An introspection is a client authentication like any other -- that is the whole
233    /// point of the paragraph above -- so it is charged
234    /// [`crate::rate_limit::ATTEMPT_COST`] against
235    /// [`crate::events::Attempt::ClientAuthentication`] keyed on the RESOURCE SERVER's
236    /// `client_id`. Through 0.9.1 that budget only ever carried a client asking about tokens it
237    /// had itself been issued, so its volume tracked issuance. A resource server introspects ONCE
238    /// PER CALL AT THE PROTECTED RESOURCE, at a rate set by that API's own clients.
239    ///
240    /// [`crate::rate_limit::DEFAULT_CLIENT_AUTHENTICATION_CAPACITY`] is 6000 a minute, which is
241    /// 100 a second, and it was derived from a client's token traffic. Left alone it becomes the
242    /// protected resource's request ceiling, per node.
243    ///
244    /// AND IT DOES NOT READ AS A THROTTLE WHEN IT BITES. RFC 7662 introspection over the ceiling
245    /// is refused with a bare `invalid_client`, the same answer a wrong secret gets, because a
246    /// distinct code would tell an attacker they had found a live client id. A resource server
247    /// that fails closed then refuses EVERY request it is handling, and its operator is looking at
248    /// what appears to be a credential problem. The [`crate::events::EventSink`] channel is where
249    /// the two are distinguishable:
250    /// [`crate::events::Event::ClientAuthenticationFailed`] carries
251    /// [`crate::events::ClientAuthFailure::RateLimited`] for a throttle and
252    /// [`crate::events::ClientAuthFailure::SecretMismatch`] for a credential that did not verify.
253    /// A deployment that registers resource servers should install one.
254    ///
255    /// So, two things:
256    ///
257    /// - Size the budget for the API, not for a client:
258    ///   [`crate::rate_limit::RateLimitConfig::with_client_authentication_capacity_for`] raises
259    ///   ONE registration and leaves every other `client_id` where it was. Raising
260    ///   `client_authentication_capacity` globally would also raise how many wrong secrets every
261    ///   other registration admits per window, each of which can cost the host an argon2id.
262    /// - CACHE THE INTROSPECTION RESPONSE at the resource server, which RFC 7662 section 4
263    ///   recommends and which is the only measure that changes the traffic shape rather than the
264    ///   ceiling. It costs a bounded delay before a revocation is observed.
265    ///
266    /// A host that implements [`crate::events::RateLimiter`] itself makes the same decision in its
267    /// own terms; there is no introspection-specific [`crate::events::Attempt`] variant to key on,
268    /// deliberately, and the module docs on [`crate::rate_limit`] say why.
269    ///
270    /// `Option<Box<[_]>>` rather than `Vec<_>` for the reason [`ServerConfig::allowed_resources`]
271    /// gives next door and with the same measurement behind it: the list is written once at
272    /// construction and only ever iterated, so the growable shape buys nothing, and a boxed slice
273    /// is 16 bytes against a vector header's 24 on every `ServerConfig` in every deployment.
274    /// MEASURED: `ServerConfig` 464 before this field, 488 as a `Vec`, 480 as this.
275    pub resource_servers: Option<Box<[ResourceServerRegistration]>>,
276    /// RFC 8414 `service_documentation`.
277    pub service_documentation: Option<String>,
278    /// RFC 9396 section 10 `authorization_details_types_supported`: the authorization
279    /// details types this deployment actually implements.
280    ///
281    /// `None` is the DEFAULT and means NO type is supported, so every `authorization_details`
282    /// request is refused with `invalid_authorization_details`. That is not conservatism for
283    /// its own sake, it is section 5: "The AS MUST refuse to process any unknown
284    /// authorization details type", and a server that has been told nothing about a type
285    /// cannot be said to know it. Compiling the `rar` feature in is therefore not the same
286    /// as turning it on; a host turns it on by naming its types here.
287    #[cfg(feature = "rar")]
288    pub authorization_details_types_supported: Option<Vec<String>>,
289    /// RFC 9728 section 4 `protected_resources`: the resource identifiers of the protected
290    /// resources this AS issues tokens for. `None` (the default) omits the member; see
291    /// [`crate::metadata::AuthorizationServerMetadata::protected_resources`], and note that
292    /// this is the AS half only. The DOCUMENT each of those resources publishes is
293    /// [`crate::resource_metadata::ProtectedResourceMetadata`], and publishing it is the
294    /// resource's own job, not this server's.
295    #[cfg(feature = "resource-metadata")]
296    pub protected_resources: Option<Vec<String>>,
297    /// What the client receives as its `access_token`. Defaults to [`AccessTokenFormat::Opaque`],
298    /// which is the behaviour of this crate without the `jwt` feature; setting
299    /// [`AccessTokenFormat::Jwt`] makes the wire token an RFC 9068 `at+jwt` while the AS-side
300    /// record is still persisted, so introspection and revocation are unchanged.
301    #[cfg(feature = "jwt")]
302    pub access_token_format: AccessTokenFormat,
303    /// Authorization code lifetime. RFC 6749 section 4.1.2 recommends a maximum of 10 minutes;
304    /// the default is 60 seconds, which is ample for a redirect round trip.
305    pub authorization_code_ttl: Duration,
306    /// Whether device authorization responses include `verification_uri_complete`
307    /// (`{verification_uri}?user_code={code}`). `false` by default.
308    ///
309    /// RFC 8628 section 5.4 (Remote Phishing) is why this is a decision and not a convenience
310    /// setting. The attack is that an attacker starts a device grant for their OWN client and mails
311    /// the victim the link ("click here to finish setting up your TV"); the victim, already signed
312    /// in, lands on a page that needs one click, and the attacker collects the tokens. Section 5.4
313    /// names TYPING THE CODE as the friction that makes this hard, and this member is precisely the
314    /// removal of that friction: the code arrives pre-filled from a URL the user did not compose.
315    ///
316    /// OFF by default as of 0.9.1, having been on. Section 3.3.1 makes the member OPTIONAL, so
317    /// omitting it is conformant and costs a deployment only the QR-code convenience, while
318    /// including it by default made every host that never read this paragraph pay for a capability
319    /// it did not ask for. That is the same posture the rest of this config takes:
320    /// [`ServerConfig::registration`] and the PAR and JAR blocks are all off until a host says
321    /// otherwise. A host that turns this ON should pair it with a verification page that names the
322    /// client and the scope and requires an affirmative action, which is what section 3.3 asks for
323    /// and what the `http` feature's page does.
324    pub include_verification_uri_complete: bool,
325    /// Device code and user code lifetime. Default 600 seconds.
326    pub device_code_ttl: Duration,
327    /// Initial minimum poll spacing (RFC 8628 `interval`). Default 5 seconds.
328    pub poll_interval: Duration,
329    /// How much a `slow_down` raises the required spacing. RFC 8628 section 3.5 mandates the
330    /// client add 5 seconds, which is the default.
331    pub slow_down_increment: Duration,
332    /// Access token lifetime. Default 3600 seconds.
333    pub access_token_ttl: Duration,
334    /// Whether user-approved grants also issue a refresh token. Default true.
335    pub issue_refresh_tokens: bool,
336    /// RFC 8693: whether a SENDER-CONSTRAINED subject token may be exchanged.
337    ///
338    /// `false` by default, which means the exchange is REFUSED with `invalid_request` when the
339    /// subject token carries an RFC 9449 DPoP or RFC 8705 mutual-TLS binding. See the "A
340    /// SENDER-CONSTRAINED subject token is REFUSED" section of the [`crate::token_exchange`] module
341    /// docs for the full argument; the short form is that the token this server would hand back
342    /// belongs to the EXCHANGING client, which does not hold the original client's key, so it can
343    /// only be a plain bearer token. Anyone able to authenticate as any client registered for this
344    /// grant could then post a stolen bound token and receive a spendable one, and the property the
345    /// deployment turned DPoP on to buy would be gone.
346    ///
347    /// # What turning it on gives up
348    ///
349    /// Exactly that property, and it is worth being blunt about it: with `true`, a sender-
350    /// constrained token becomes exchangeable for an UNBOUND bearer token, so a leaked bound token
351    /// is once again worth something to whoever finds it, via one request to this endpoint. The
352    /// binding is not propagated (a `cnf` naming a key the new holder cannot prove would be a
353    /// broken grant dressed as a secure one), it is DROPPED, and nothing downstream is told.
354    ///
355    /// It exists because 0.9.0 and earlier did exactly this silently, so a deployment that has
356    /// already built on the downgrade needs a way to keep running while it migrates. It is not a
357    /// tuning knob: a host that sets it has decided that its delegation topology is trusted enough
358    /// to hold the binding for it, and that decision belongs in a sentence somebody wrote and a
359    /// reviewer can find.
360    pub allow_sender_constrained_exchange: bool,
361    /// Allow an RFC 8693 exchange of a subject token that carries RFC 9396 `authorization_details`,
362    /// propagating those details onto a token issued to a DIFFERENT client. `false` by default, and
363    /// the default is the safe one.
364    ///
365    /// # Why this is off
366    ///
367    /// The exchange applies TWO ceilings to scope: the subject token's granted scope (RFC 6749
368    /// section 6 narrowing) and then the exchanging client's own
369    /// [`crate::client::Client::allowed_scopes`], because the issued token belongs to a different
370    /// principal. It applied NONE to `authorization_details`, which are strictly more specific:
371    /// RFC 9396 exists precisely because a scope token cannot say "transfer 50 euros to IBAN X".
372    ///
373    /// So the weaker rule was applied to the more dangerous grant. A downstream service registered
374    /// for `read`, which receives a payments client's token because forwarding the caller's token
375    /// is exactly what this grant is for, could exchange it asking for `read`, pass both scope
376    /// ceilings, and walk away with a token issued to ITSELF carrying the full payment
377    /// authorization, signed into the RFC 9068 claim, visible over RFC 7662 introspection, and with
378    /// a fresh lifetime that outlives the token it came from.
379    ///
380    /// # Why an opt-in rather than a per-client ceiling
381    ///
382    /// The correct ceiling is a per-client registration of the detail types a client may hold, the
383    /// analogue of `allowed_scopes`. [`crate::client::Client`] has no such field, and adding one is
384    /// a breaking change to a type hosts construct. Until it exists there is nothing to narrow
385    /// against, so the honest choice is to refuse and let a host that has reasoned about its own
386    /// delegation topology say so. Setting this to `true` accepts that any client permitted this
387    /// grant may inherit any detail any subject token carries.
388    pub allow_authorization_details_exchange: bool,
389    /// Absolute refresh chain lifetime. Rotation preserves the chain's original expiry rather than
390    /// sliding it, so this is a ceiling on the whole chain and not on one token.
391    ///
392    /// `None` (the default) means NO TIME EXPIRY AT ALL: a refresh chain established once lives
393    /// until something revokes it. `None` is the default because it is the pre-existing behaviour
394    /// and turning expiry on by default would sign every deployment's users out on an interval
395    /// nobody chose, so, exactly as with [`ServerConfig::allowed_resources`], `None` is a real risk
396    /// rather than a neutral one and the risk belongs here rather than in a changelog.
397    ///
398    /// What it costs: a refresh token exfiltrated once is a credential for the user's account
399    /// FOREVER, and rotation does not fix that. RFC 9700 section 4.14.2 reuse detection catches the
400    /// thief only if the legitimate client comes back and presents the token the thief already
401    /// spent; a thief who steals a chain the user has abandoned, or who simply rotates it faster
402    /// than the real client does, is never detected by anything and holds access indefinitely.
403    /// Setting this bounds that to a window, and OAuth 2.1 draft section 6.1 asks for either a
404    /// finite lifetime or rotation on the same grounds.
405    ///
406    /// A deployment whose users are humans, and whose refresh tokens sit on devices those humans
407    /// lose, should set this.
408    pub refresh_token_ttl: Option<Duration>,
409    /// How long a ROTATED (spent) refresh token is retained purely so that its reuse can be
410    /// detected, when its chain has no absolute expiry of its own. Default 30 days.
411    ///
412    /// Reuse detection (OAuth 2.1 draft section 6.1, RFC 9700 section 4.14.2) only works while the
413    /// superseded token is still recognisable, so this is the window in which a stolen-and-rotated
414    /// token still triggers revocation of its family. Past it the record is sweepable and a
415    /// presentation reads as an unknown token. When the chain HAS an absolute expiry, that expiry
416    /// is used instead: there is nothing left to protect once the chain itself is dead.
417    pub refresh_reuse_window: Duration,
418    /// RFC 9449: whether EVERY token request must carry a DPoP proof.
419    ///
420    /// `false` by default, which means "DPoP is available, and a client that wants a
421    /// sender-constrained token asks for one by presenting a proof". `true` is the FAPI 2.0
422    /// posture: it refuses every token request without a proof, which is a breaking change for
423    /// every existing client of the deployment and therefore a sentence somebody has to write on
424    /// purpose rather than a default anybody inherits.
425    #[cfg(feature = "dpop")]
426    pub require_dpop: bool,
427    /// User code length in symbols, excluding the display hyphen. Default
428    /// [`MIN_USER_CODE_LENGTH`] (about 34 bits over the 20-symbol alphabet, the RFC 8628 section
429    /// 6.1 example shape).
430    ///
431    /// Values below [`MIN_USER_CODE_LENGTH`] are CLAMPED UP at generation, not honoured. This is
432    /// not tuning: 4 symbols is about 160,000 possibilities, which is seconds of guessing against
433    /// an endpoint this library cannot rate limit, and 0 produces an empty code that every grant
434    /// collides on. Clamping rather than rejecting keeps a misconfiguration from becoming a
435    /// runtime failure at the one moment a user is standing in front of a device.
436    pub user_code_length: usize,
437}
438
439/// The floor [`ServerConfig::user_code_length`] is clamped up to: the RFC 8628 section 6.1 example
440/// shape, about 34 bits over the 20-symbol alphabet.
441///
442/// Section 6.1 is explicit that this entropy is adequate only IN COMBINATION WITH rate limiting of
443/// user-code entry. This library performs none and cannot: it never sees a request, only the host
444/// does. See [`AuthorizationServer::approve_device`].
445pub const MIN_USER_CODE_LENGTH: usize = 8;
446
447/// The largest number of RFC 8707 `resource` indicators one request may carry.
448///
449/// # Why there is a cap at all
450///
451/// Section 2 makes `resource` a REPEATABLE parameter, and it is accepted at the authorization
452/// endpoint, which takes no client credential: an unauthenticated caller chooses `n`. Validation is
453/// O(n) per element against [`ServerConfig::allowed_resources`] plus an O(n) dedup scan, so the
454/// cost is quadratic in a number the caller picks, and it is paid before anything has authenticated
455/// anybody. This is the same argument, and the same remedy, as
456/// [`crate::rar::MAX_AUTHORIZATION_DETAILS_ELEMENTS`]: the other repeatable array a request can
457/// carry. Leaving one capped and the other not was the defect.
458///
459/// # Why 16
460///
461/// It is the number of DISTINCT resource servers one access token may be good at. RFC 8707's own
462/// security considerations push the other way, toward narrow audiences, and a deployment that
463/// genuinely needs one token accepted at more than sixteen separate resource servers has an
464/// audience so wide that the indicator has stopped restricting anything. Sixteen is also what
465/// RFC 9396 already allows for `authorization_details`, so the two arrays a request can repeat are
466/// bounded alike and a reader does not have to hold two numbers.
467///
468/// # Why the scan stayed linear
469///
470/// At sixteen, a `HashSet<String>` is slower, not faster: it pays a SipHash of the whole string per
471/// lookup against at most sixteen comparisons that mostly fail on the length. The defect was never
472/// the scan, it was that nothing bounded `n`. A cap is the entire fix.
473///
474/// # It refuses, it does not truncate
475///
476/// Silently dropping the indicators past the cap would issue a token whose audience is not the one
477/// the client asked for, with nothing told to anybody: the exact failure `crate::rar` refuses for
478/// unknown members. `invalid_target` (section 2) is the honest answer.
479pub const MAX_RESOURCE_INDICATORS: usize = 16;
480
481/// Which of RFC 7662's two legitimate callers an introspection request is being answered as.
482///
483/// Not public: it is the shape of one decision inside
484/// [`AuthorizationServer::introspection_response_with_credential`], and a host that could name it
485/// would be able to depend on a split that exists to serve the response document rather than to
486/// describe the deployment.
487enum IntrospectionView {
488    /// The client the token was issued to. Sees the whole record, including every resource
489    /// identifier the grant was restricted to.
490    OwningClient,
491    /// A registered resource server, carrying the identifiers of ITS OWN that this token names.
492    /// Never empty: an empty intersection is not a resource-server view, it is `active: false`.
493    ResourceServer(Vec<String>),
494}
495
496/// The RFC 9396 details a RESOURCE SERVER may be told about, given the identifiers `mine` it is
497/// registered for and answers for.
498///
499/// Section 9.2: the details are "filtered and extended for the RS making the introspection
500/// request". This is the filtering half; this crate does no extending, because an extension would
501/// be an assertion about an API vocabulary it does not know (see [`crate::rar`] on `other`).
502///
503/// The rule, and why each arm is the safe one:
504///
505/// - an element with NO `locations` is KEPT. Section 2.2 makes the member optional, so its absence
506///   says nothing about where the element belongs; dropping it would withhold a detail the resource
507///   owner did approve from the only party in a position to enforce it, which is a fail-open move
508///   dressed as a privacy one.
509/// - an element whose `locations` names one of `mine` is kept, with its `locations` REDUCED to the
510///   intersection. Keeping the element whole would let a detail addressed to two resource servers
511///   hand each of them the other's URI, which is the disclosure this function exists to stop
512///   arriving one level down.
513/// - anything else is DROPPED: its `locations` names other services only, and a resource server
514///   that is not named in an element has no business acting on it or knowing it exists.
515///
516/// The reduction cannot produce an element with an EMPTY `locations`, because an empty intersection
517/// is the dropped arm. That matters beyond tidiness: empty is how this crate spells "the member was
518/// absent", so an element narrowed to nothing would read on the wire as one that was never located
519/// at all -- a widening performed by a filter.
520///
521/// Filtering everything away yields an EMPTY set, and
522/// [`crate::token::IntrospectionResponse::authorization_details`] omits the member rather than
523/// serializing `[]`, so the resource server sees exactly what it sees for a grant that carried no
524/// details at all. That is the "not granted" / "not for you" indistinguishability at its widest,
525/// and it is the intended shape: see this method's caller for why that direction is the harmless
526/// one.
527#[cfg(feature = "rar")]
528fn details_for_resource_server(
529    details: &crate::rar::AuthorizationDetails,
530    mine: &[String],
531) -> crate::rar::AuthorizationDetails {
532    crate::rar::AuthorizationDetails::from_elements(
533        details
534            .iter()
535            .filter_map(|detail| {
536                if detail.locations.is_empty() {
537                    return Some(detail.clone());
538                }
539                let locations: Vec<Box<str>> = detail
540                    .locations
541                    .iter()
542                    .filter(|at| mine.iter().any(|id| id.as_str() == &***at))
543                    .cloned()
544                    .collect();
545                (!locations.is_empty()).then(|| crate::rar::AuthorizationDetail {
546                    locations: locations.into_boxed_slice(),
547                    ..detail.clone()
548                })
549            })
550            .collect(),
551    )
552}
553
554/// One resource server, as [`ServerConfig::resource_servers`] declares it: the registered client
555/// identity it authenticates as, and the RFC 8707 resource identifiers it is the protected
556/// resource FOR.
557///
558/// The two halves answer two different questions and neither substitutes for the other.
559/// `client_id` answers "who is calling", and it is checked by the ordinary client authentication
560/// every endpoint uses, so a resource server needs a real credential and gets constant-time
561/// verification, rotation and revocation for free. `resources` answers "what may it ask about",
562/// and it is checked against the token's own [`crate::IssuedToken::resource`] set, so a resource
563/// server is told about tokens addressed to it and is told `{"active": false}` about every other
564/// token in the store.
565///
566/// Registering the same `client_id` twice is not an error and not special: the identifier sets are
567/// considered in order and a match in any of them is a match. It is simply a longer way of writing
568/// one entry with both lists concatenated.
569#[derive(Debug, Clone, PartialEq, Eq)]
570/// `#[non_exhaustive]`: this is a DEPLOYMENT POLICY object for a channel that will grow. A per-RS
571/// claim filter, a per-RS introspection policy and a `token_endpoint_auth_method` constraint are
572/// all plausible next fields, and each one would be a major-version event if a host could write a
573/// struct literal here. Its sibling [`crate::cimd::CimdPolicy`] is sealed for the same reason and
574/// states it plainly: "A host writes a full struct literal today and has a build that breaks on a
575/// patch release; `new()` plus assignment does not. The attribute cannot be added after
576/// publication, because by then the literal is in somebody's production tree."
577///
578/// It is sealed HERE rather than later because 0.9.2 is the release that introduces it. The
579/// `tests/host_api_shape.rs` gate does not catch this one and is not wrong to miss it -- that scan
580/// flags types whose field set VARIES WITH A CARGO FEATURE, and this one's does not. The rule the
581/// crate actually follows is broader than the gate that enforces part of it.
582#[non_exhaustive]
583pub struct ResourceServerRegistration {
584    /// The registered client this resource server authenticates as. It must be a CONFIDENTIAL
585    /// client: introspection refuses public clients (see
586    /// [`AuthorizationServer::introspection_response_with_credential`]), and naming a public client
587    /// here therefore registers a resource server that can never successfully call.
588    pub client_id: ClientId,
589    /// The RFC 8707 resource identifiers this server is the protected resource for. An entry with
590    /// an EMPTY list can never match any token, because matching requires naming an identifier the
591    /// token carries; it registers a resource server with no authority rather than one with
592    /// universal authority, which is the fail-closed direction.
593    pub resources: Vec<String>,
594}
595
596impl ResourceServerRegistration {
597    /// Declare `client_id` to be the resource server for `resources`.
598    pub fn new(
599        client_id: ClientId,
600        resources: impl IntoIterator<Item = impl Into<String>>,
601    ) -> Self {
602        Self {
603            client_id,
604            resources: resources.into_iter().map(Into::into).collect(),
605        }
606    }
607}
608
609/// The RFC 8693 section 4.1 actor an issuance carries, in a wrapper that is ZERO SIZED without the
610/// `token-exchange` feature.
611///
612/// Exactly the same device as [`GrantedAuthentication`] below and for exactly the same reason: one
613/// `issue` signature in every feature configuration, because a `cfg` on an argument cannot be
614/// matched by a `cfg` at the call site. Only the delegation branch of a token exchange ever fills
615/// it; every other grant passes the default, which is what `None` on the record means.
616#[derive(Default, Clone, PartialEq, Eq)]
617pub(crate) struct GrantedActor {
618    #[cfg(feature = "token-exchange")]
619    pub(crate) act: Option<Box<crate::token_exchange::ActClaim>>,
620}
621
622/// The host-reported authentication an issuance carries, in a wrapper that is ZERO SIZED without
623/// the `consent` feature.
624///
625/// It exists so [`AuthorizationServer::issue`] and its five call sites have ONE signature in every
626/// feature configuration. The alternative, a `cfg` on the argument, cannot be matched by a `cfg` at
627/// the call site, and duplicating five call sites under a `cfg` is five places to get it wrong in
628/// the configuration nobody builds locally. Same reason `Bound` exists for the RFC 9449 binding.
629#[derive(Default, Clone, PartialEq, Eq)]
630pub(crate) struct GrantedAuthentication {
631    #[cfg(feature = "consent")]
632    pub(crate) authentication: Option<Box<crate::consent::Authentication>>,
633}
634
635impl GrantedAuthentication {
636    /// What an authorization code carries into the token it mints.
637    #[cfg(feature = "consent")]
638    pub(crate) fn from_code(record: &AuthorizationCodeRecord) -> Self {
639        GrantedAuthentication {
640            authentication: record.authentication.clone(),
641        }
642    }
643
644    /// Without the feature there is no field to fill, and no field on the record to fill it from.
645    #[cfg(not(feature = "consent"))]
646    pub(crate) fn from_code(_record: &AuthorizationCodeRecord) -> Self {
647        GrantedAuthentication {}
648    }
649
650    /// What a refresh chain carries across a rotation: the ORIGINAL authentication, unchanged. See
651    /// [`crate::token::RefreshTokenRecord::authentication`] on why a rotation is not a new one.
652    #[cfg(feature = "consent")]
653    pub(crate) fn from_refresh(record: &RefreshTokenRecord) -> Self {
654        GrantedAuthentication {
655            authentication: record.authentication.clone(),
656        }
657    }
658
659    /// Without the feature, as above.
660    #[cfg(not(feature = "consent"))]
661    pub(crate) fn from_refresh(_record: &RefreshTokenRecord) -> Self {
662        GrantedAuthentication {}
663    }
664}
665
666/// A statement, by the HOST, that a resource owner saw one validated authorization request and
667/// agreed to it. The only thing [`AuthorizationServer::issue_authorization_code`] will mint from.
668///
669/// # Why this type exists
670///
671/// The `http` feature's `ServiceBuilder` REFUSES TO BUILD without a consent resolver, so a host on
672/// that path cannot reach code issuance without having written the word "approve". The direct API
673/// had no such step, and the direct API is the path this crate's DEFAULT BUILD invites: no HTTP
674/// surface, no listener, the host owning its own routes. `issue_authorization_code(&validated,
675/// "alice")` read like a lookup, compiled, passed the host's own tests, and shipped an
676/// authorization server that approved everything. The refusal existed on one of two supported
677/// adoption paths, which is the same as not existing.
678///
679/// # What it is, and what it is not
680///
681/// It is NOT a proof, and no type this crate could define would be one: this library has no user,
682/// no session and no screen, so "the user agreed" is a fact only the host holds. Nor is it weaker
683/// than the seam it mirrors. A host can wire `|_| ApprovalDecision::Approve` into the `http` path
684/// just as it can call [`UserApproval::granted`] here, so what BOTH seams buy is the same and is
685/// the whole of what a library at this boundary can buy: the approval becomes a sentence the host
686/// WROTE rather than a default it inherited, and the host that never considered RFC 6749 section
687/// 10.12 gets a compile error naming it instead of a working forgery endpoint.
688///
689/// It also closes a bug class the two-argument form left open. The approval BORROWS the request it
690/// approves, so there is no second request parameter left to disagree with it: a host cannot prompt
691/// for one request and issue for another, approving a `read` and minting a `read write`.
692///
693/// # Allocation
694///
695/// A borrow plus the `subject` String, which is the same one allocation the old
696/// `subject: impl Into<String>` argument made on its way into the record. Nothing on the token path
697/// changes, and nothing here is on it.
698pub struct UserApproval<'a> {
699    request: &'a ValidatedAuthorizationRequest,
700    subject: String,
701    /// When the decision this approval reports was MADE, if the host knows.
702    ///
703    /// `None` means "now", which is right for a host that prompted the user during this request
704    /// and wrong for one that is acting on a standing approval it read a moment ago. See
705    /// [`UserApproval::decided_at`] for why the difference is load bearing.
706    decided_at: Option<std::time::SystemTime>,
707}
708
709impl<'a> UserApproval<'a> {
710    /// The resource owner named by `subject` approved `request`.
711    ///
712    /// CALLING THIS IS AN ASSERTION. It says a real user was really asked about really this
713    /// request, in whatever the deployment's consent step is, and said yes. This crate cannot check
714    /// that and will not try; what it can do is refuse to mint anything until someone writes it.
715    ///
716    /// `subject` is the authenticated resource owner, in the host's own vocabulary for users, and
717    /// is what the issued code and every token it mints will carry.
718    pub fn granted(request: &'a ValidatedAuthorizationRequest, subject: impl Into<String>) -> Self {
719        UserApproval {
720            request,
721            subject: subject.into(),
722            decided_at: None,
723        }
724    }
725
726    /// The same approval, dated.
727    ///
728    /// WHAT THIS IS FOR, and it is not bookkeeping. A revocation barrier refuses a write whose
729    /// GRANT predates the revocation, and the grant instant a code carries is the instant of the
730    /// decision it rests on. For a host that prompted the user during this request those are the
731    /// same moment and [`UserApproval::granted`] is right. For a host acting on a STANDING
732    /// approval — a remembered consent it read at the top of the request — they are not, and
733    /// dating a months-old approval at this instant makes it outrank a withdrawal recorded in
734    /// between.
735    ///
736    /// The failure that costs is precise. The user opens the authorization page; the host reads
737    /// their standing consent; the user, elsewhere, clicks "remove this application", and the
738    /// withdrawal cascades away every token and records its barrier; the first request then
739    /// resumes and mints a code on the strength of the pre-withdrawal snapshot. Nothing refuses
740    /// the code — `put_authorization_code` is deliberately barrier-exempt — and at redemption a
741    /// code dated NOW postdates the barrier, so the token is issued and its refresh chain
742    /// inherits the same instant, rotating happily long after the barrier is swept.
743    ///
744    /// Pass the instant the decision was made: the standing record's own `granted_at`, or the
745    /// instant the request was received, whichever the host can honestly claim. Both are earlier
746    /// than any withdrawal that has not yet been read, which is the whole of what is needed.
747    pub fn granted_at(
748        request: &'a ValidatedAuthorizationRequest,
749        subject: impl Into<String>,
750        decided_at: std::time::SystemTime,
751    ) -> Self {
752        UserApproval {
753            request,
754            subject: subject.into(),
755            decided_at: Some(decided_at),
756        }
757    }
758
759    /// When the decision was made, if the host said. See [`UserApproval::granted_at`].
760    pub fn decided_at(&self) -> Option<std::time::SystemTime> {
761        self.decided_at
762    }
763
764    /// The request this approves.
765    pub fn request(&self) -> &'a ValidatedAuthorizationRequest {
766        self.request
767    }
768
769    /// The resource owner who approved it.
770    pub fn subject(&self) -> &str {
771        &self.subject
772    }
773}
774
775/// Hand-written: `subject` is a user identifier, and this crate does not print those into whatever
776/// caught a `{:?}`. Which REQUEST is being approved stays visible, because that is the whole of
777/// what anybody debugging an issuance needs.
778impl fmt::Debug for UserApproval<'_> {
779    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
780        f.debug_struct("UserApproval")
781            .field("client_id", &self.request.client_id)
782            .field("scope", &self.request.scope)
783            .field("subject", &"[redacted]")
784            .finish()
785    }
786}
787
788/// How many times user-code generation may redraw on a collision before giving up.
789///
790/// A collision at the floor length is a roughly one-in-a-hundred-billion event per live grant, so
791/// a run of this many is not chance: it is a store that is full, broken, or under an allocation
792/// flood. Bounded rather than unbounded because an endpoint that spins forever under load is a
793/// worse failure than one that answers `server_error`.
794const USER_CODE_GENERATION_ATTEMPTS: usize = 8;
795
796impl ServerConfig {
797    /// A config with RFC-shaped defaults; `issuer` and `verification_uri` have no sane default and
798    /// are required.
799    pub fn new(issuer: impl Into<String>, verification_uri: impl Into<String>) -> Self {
800        ServerConfig {
801            issuer: issuer.into(),
802            verification_uri: verification_uri.into(),
803            authorization_endpoint: None,
804            token_endpoint: None,
805            device_authorization_endpoint: None,
806            introspection_endpoint: None,
807            revocation_endpoint: None,
808            jwks_uri: None,
809            // OFF. See the field's own docs, and RFC 7591 section 5.
810            registration: None,
811            // OFF. PAR is a capability a host opts into, not a default: see the field's docs.
812            #[cfg(feature = "par")]
813            par: None,
814            #[cfg(feature = "jar")]
815            jar: None,
816            // OFF, and off means the RFC 8414 document says so: a host that has not wired the
817            // fetch must not advertise that it did. See the field's own docs.
818            #[cfg(feature = "cimd")]
819            cimd: None,
820            scopes_supported: None,
821            allowed_resources: None,
822            resource_servers: None,
823            service_documentation: None,
824            // OFF. An undeclared catalogue supports no types: see the field's own docs and
825            // RFC 9396 section 5.
826            #[cfg(feature = "rar")]
827            authorization_details_types_supported: None,
828            #[cfg(feature = "resource-metadata")]
829            protected_resources: None,
830            #[cfg(feature = "jwt")]
831            access_token_format: AccessTokenFormat::Opaque,
832            authorization_code_ttl: Duration::from_secs(60),
833            // OFF. RFC 8628 s5.4 remote phishing: see the field's own docs.
834            include_verification_uri_complete: false,
835            device_code_ttl: Duration::from_secs(600),
836            poll_interval: Duration::from_secs(5),
837            slow_down_increment: Duration::from_secs(5),
838            access_token_ttl: Duration::from_secs(3600),
839            issue_refresh_tokens: true,
840            // OFF: an exchange that drops a sender constraint is a decision, not a default. See the
841            // field's own docs for what turning it on gives up.
842            allow_sender_constrained_exchange: false,
843            allow_authorization_details_exchange: false,
844            refresh_token_ttl: None,
845            // 30 days: long enough that a chain abandoned by a client that later comes back with
846            // a stale token is still recognised as reuse rather than as noise.
847            refresh_reuse_window: Duration::from_secs(30 * 24 * 60 * 60),
848            #[cfg(feature = "dpop")]
849            require_dpop: false,
850            user_code_length: MIN_USER_CODE_LENGTH,
851        }
852    }
853}
854
855/// A parsed token-endpoint request (RFC 6749 section 3.2). The host parses the form body and the
856/// `Authorization` header into this; `client_secret` is `None` for public clients.
857///
858/// `Debug` is hand-written (see below) rather than derived. Every variant of this type is built
859/// directly out of an inbound request and every variant carries at least one credential: RFC 6749
860/// section 2.3.1 makes `client_secret` a password, and section 4.1.2, section 6 and RFC 8628
861/// section 3.4 each make the grant artifact (`code`, `refresh_token`, `device_code`) a bearer
862/// credential in its own right. This is the type a host is most likely to debug-print, since it is
863/// the request it just parsed, so a derived `Debug` here would be the single easiest way to end up
864/// with plaintext credentials in a host's logs.
865#[derive(Clone, PartialEq, Eq)]
866pub enum TokenRequest {
867    /// RFC 6749 section 4.1.3: `grant_type=authorization_code`, with the RFC 7636 `code_verifier`
868    /// that OAuth 2.1 makes mandatory.
869    AuthorizationCode {
870        /// The redeeming client.
871        client_id: ClientId,
872        /// The client secret, when the client is confidential.
873        client_secret: Option<String>,
874        /// The code from the authorization response (single use).
875        code: String,
876        /// The redirect URI the authorization request used; must match exactly.
877        redirect_uri: Option<String>,
878        /// The PKCE verifier for the challenge recorded against the code.
879        code_verifier: Option<String>,
880    },
881    /// RFC 6749 section 4.4: `grant_type=client_credentials`. Confidential clients only, and no
882    /// refresh token is issued (section 4.4.3: the client can simply request another token).
883    ClientCredentials {
884        /// The client acting on its own behalf.
885        client_id: ClientId,
886        /// The client secret. A public client has none, and cannot use this grant.
887        client_secret: Option<String>,
888        /// Optional narrowing scope.
889        scope: Option<ScopeSet>,
890    },
891    /// RFC 8628 section 3.4: `grant_type=urn:ietf:params:oauth:grant-type:device_code`.
892    DeviceCode {
893        /// The polling client.
894        client_id: ClientId,
895        /// The client secret, when the client is confidential.
896        client_secret: Option<String>,
897        /// The `device_code` from the device authorization response.
898        device_code: String,
899    },
900    /// RFC 6749 section 6: `grant_type=refresh_token`, with OAuth 2.1 rotation.
901    RefreshToken {
902        /// The refreshing client.
903        client_id: ClientId,
904        /// The client secret, when the client is confidential.
905        client_secret: Option<String>,
906        /// The refresh token being redeemed (single use).
907        refresh_token: String,
908        /// Optional narrowing scope; widening is `invalid_scope`.
909        scope: Option<ScopeSet>,
910    },
911}
912
913/// Hand-written so no credential reaches a debug format, while everything that identifies WHICH
914/// request this is stays visible: the variant name (so the grant type is readable), `client_id`
915/// (RFC 6749 section 2.2 makes it explicitly not a secret), `redirect_uri` and `scope`.
916///
917/// `client_secret` and `code_verifier` are `Option`s, and the Some/None distinction is kept: it is
918/// not a credential, it is the difference between "a secret was presented" and "none was", which
919/// is exactly what someone debugging an `invalid_client` (RFC 6749 section 5.2) or a missing-PKCE
920/// rejection needs, and it can be read off the request's shape without the value.
921impl fmt::Debug for TokenRequest {
922    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
923        // `Option<&str>` rather than a bare string, so `Some("[redacted]")` / `None` prints and
924        // the presence of the credential stays legible while its value does not.
925        fn redact_opt<T>(value: &Option<T>) -> Option<&'static str> {
926            value.as_ref().map(|_| "[redacted]")
927        }
928        match self {
929            TokenRequest::AuthorizationCode {
930                client_id,
931                client_secret,
932                code: _,
933                redirect_uri,
934                code_verifier,
935            } => f
936                .debug_struct("AuthorizationCode")
937                .field("client_id", client_id)
938                .field("client_secret", &redact_opt(client_secret))
939                .field("code", &"[redacted]")
940                .field("redirect_uri", redirect_uri)
941                .field("code_verifier", &redact_opt(code_verifier))
942                .finish(),
943            TokenRequest::ClientCredentials {
944                client_id,
945                client_secret,
946                scope,
947            } => f
948                .debug_struct("ClientCredentials")
949                .field("client_id", client_id)
950                .field("client_secret", &redact_opt(client_secret))
951                .field("scope", scope)
952                .finish(),
953            TokenRequest::DeviceCode {
954                client_id,
955                client_secret,
956                device_code: _,
957            } => f
958                .debug_struct("DeviceCode")
959                .field("client_id", client_id)
960                .field("client_secret", &redact_opt(client_secret))
961                // RFC 8628 section 3.4 redeems the device code with no further proof from a public
962                // client, so it is as much a bearer credential as an authorization code is.
963                .field("device_code", &"[redacted]")
964                .finish(),
965            TokenRequest::RefreshToken {
966                client_id,
967                client_secret,
968                refresh_token: _,
969                scope,
970            } => f
971                .debug_struct("RefreshToken")
972                .field("client_id", client_id)
973                .field("client_secret", &redact_opt(client_secret))
974                .field("refresh_token", &"[redacted]")
975                .field("scope", scope)
976                .finish(),
977        }
978    }
979}
980
981/// How a client is authenticating on one request.
982///
983/// A value of its own rather than more fields on every [`TokenRequest`] variant, for the same
984/// reason RFC 8707's `resource` is a separate argument: client authentication is a property of the
985/// REQUEST and is identical across every grant, so putting it on each variant would state the same
986/// thing four times, grow an enum every host copies around, and make each future grant repeat it
987/// again.
988///
989/// [`Default`] is a PUBLIC client: no secret, no assertion.
990///
991/// `Debug` is HAND-WRITTEN (below) and does not print the secret or the assertion. It derived one
992/// until 0.9.2, which made the guarantee on `crate::http`'s private `Credentials` -- "DELIBERATELY NOT
993/// `Debug` ... a derived `Debug` would put all of it verbatim into a host's logs the first time
994/// somebody wrote `tracing::debug!(?creds)`" -- last exactly as long as the one function call that
995/// converts the one into the other. And this is the worse of the two to leave open: it is PUBLIC
996/// API, so it is the value a host builds by hand for [`AuthorizationServer::token`], and a host
997/// that never touches `http::Credentials` reaches it anyway.
998#[derive(Clone, Copy, Default, PartialEq, Eq)]
999/// `#[non_exhaustive]`: `client-assertion` adds two fields and `mtls` adds a third, so this is four
1000/// different structs depending on the flag set. Three named constructors already cover the three
1001/// ways a client can authenticate ([`ClientCredential::secret`], [`ClientCredential::assertion`],
1002/// [`ClientCredential::certificate`]), and the RFC 8705 section 4 case of binding a token for a
1003/// client that authenticated some other way is a field assignment on top of one of them, which is
1004/// exactly what that field's own documentation already tells a host to do.
1005#[non_exhaustive]
1006pub struct ClientCredential<'a> {
1007    /// The RFC 6749 section 2.3.1 shared secret, from `Authorization: Basic` or from the form body.
1008    /// `None` for a public client, and `None` when an assertion is presented instead.
1009    pub client_secret: Option<&'a str>,
1010    /// RFC 7521 section 4.2 `client_assertion_type`. It MUST be
1011    /// [`crate::client_assertion::CLIENT_ASSERTION_TYPE`]; any other value is refused rather than
1012    /// ignored, because an assertion format this server does not implement is a credential it
1013    /// cannot check, and "cannot check" must never read as "checked out".
1014    #[cfg(feature = "client-assertion")]
1015    pub client_assertion_type: Option<&'a str>,
1016    /// RFC 7523 section 2.2 `client-assertion`: the signed JWT itself.
1017    #[cfg(feature = "client-assertion")]
1018    pub client_assertion: Option<&'a str>,
1019    /// The RFC 8705 client certificate the HOST has ALREADY VERIFIED for this connection.
1020    ///
1021    /// READ [`crate::mtls`]'s trust boundary section before setting this. This library
1022    /// never sees a socket, so it cannot validate a chain it did not negotiate: a host that
1023    /// fills this in from an unverified source (an unstripped `X-Client-Cert` header, a
1024    /// terminator that requests but does not require a certificate) has authenticated
1025    /// nobody, and every comparison this crate then makes is against a value the caller
1026    /// chose.
1027    ///
1028    /// It does two separate jobs, either of which can apply on its own:
1029    ///
1030    /// - section 2, AUTHENTICATION: a client registered with
1031    ///   [`crate::client::ClientAuth::Mtls`] is authenticated by this certificate and by
1032    ///   nothing else. Such a client cannot authenticate through a call that leaves this
1033    ///   `None`, which is the point: a host that forgets to pass the certificate gets
1034    ///   `invalid_client`, never a token.
1035    /// - section 3, BINDING: the issued access token is bound to this certificate whatever
1036    ///   the client's authentication method was, including a public client (section 4).
1037    ///   Binding is not conditional on a per-client flag, because a bound token is never
1038    ///   less safe than the unbound one it replaces, and a client that does not want
1039    ///   binding does not present a certificate.
1040    #[cfg(feature = "mtls")]
1041    pub certificate: Option<&'a crate::mtls::ClientCertificate<'a>>,
1042}
1043
1044/// Hand-written so neither the shared secret nor the assertion ever prints, in the same shape
1045/// [`crate::token::TokenResponse`] uses: the `Some`/`None` distinction is KEPT, because WHICH
1046/// credential a request presented is the diagnostic an operator actually needs and is not itself
1047/// secret, while the value is. `client_assertion_type` prints in full: RFC 7521 section 4.2 makes
1048/// it a fixed registered URN, so it identifies the mechanism rather than the holder. The
1049/// certificate prints through its own `Debug`, which is a public document by construction.
1050impl fmt::Debug for ClientCredential<'_> {
1051    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1052        fn redact_opt<T>(value: &Option<T>) -> Option<&'static str> {
1053            value.as_ref().map(|_| "[redacted]")
1054        }
1055        let mut out = f.debug_struct("ClientCredential");
1056        out.field("client_secret", &redact_opt(&self.client_secret));
1057        #[cfg(feature = "client-assertion")]
1058        out.field("client_assertion_type", &self.client_assertion_type);
1059        #[cfg(feature = "client-assertion")]
1060        out.field("client_assertion", &redact_opt(&self.client_assertion));
1061        #[cfg(feature = "mtls")]
1062        out.field("certificate", &self.certificate);
1063        out.finish()
1064    }
1065}
1066
1067impl<'a> ClientCredential<'a> {
1068    /// The credential of a client presenting a shared secret, or of a public client presenting
1069    /// none.
1070    pub fn secret(client_secret: Option<&'a str>) -> Self {
1071        ClientCredential {
1072            client_secret,
1073            #[cfg(feature = "client-assertion")]
1074            client_assertion_type: None,
1075            #[cfg(feature = "client-assertion")]
1076            client_assertion: None,
1077            #[cfg(feature = "mtls")]
1078            certificate: None,
1079        }
1080    }
1081
1082    /// The RFC 7523 credential: the assertion, and the type that names its format.
1083    #[cfg(feature = "client-assertion")]
1084    pub fn assertion(client_assertion_type: Option<&'a str>, client_assertion: &'a str) -> Self {
1085        ClientCredential {
1086            client_secret: None,
1087            client_assertion_type,
1088            client_assertion: Some(client_assertion),
1089            #[cfg(feature = "mtls")]
1090            certificate: None,
1091        }
1092    }
1093
1094    /// The RFC 8705 credential: the client certificate the host verified during the TLS
1095    /// handshake, and no secret at all.
1096    ///
1097    /// For a client that authenticates some OTHER way and still wants its token bound
1098    /// (RFC 8705 section 4, including a public client), set
1099    /// [`ClientCredential::certificate`] on the credential it is already using rather than
1100    /// replacing it with this one.
1101    #[cfg(feature = "mtls")]
1102    pub fn certificate(certificate: &'a crate::mtls::ClientCertificate<'a>) -> Self {
1103        ClientCredential {
1104            certificate: Some(certificate),
1105            ..ClientCredential::secret(None)
1106        }
1107    }
1108
1109    /// Fall back to the secret carried on the [`TokenRequest`] variant when the context named none,
1110    /// so a host may present it either way and neither is silently ignored.
1111    fn or_secret(mut self, secret: Option<&'a str>) -> Self {
1112        if self.client_secret.is_none() {
1113            self.client_secret = secret;
1114        }
1115        self
1116    }
1117}
1118
1119/// Everything about a token request that is not part of the grant itself.
1120///
1121/// Passed by reference to [`AuthorizationServer::token_with_context`]. Growing this struct is
1122/// cheap; growing [`TokenRequest`] is not, because a host copies that around and
1123/// `tests/allocation.rs` holds it to a size budget.
1124#[derive(Debug, Clone, Copy, Default)]
1125/// `#[non_exhaustive]`: `rar` and `dpop` each add a field, and this is the type a host assembles on
1126/// EVERY token request, so it is the single most likely struct literal in a host's codebase and the
1127/// most expensive one to break. "Growing this struct is cheap" above is only true while growing it
1128/// is not a semver-major change, which is what the attribute buys.
1129///
1130/// Build it with [`TokenRequestContext::new`] and assign what the request carried; `Default` is
1131/// still there for a request with no credential at all, though the credential is the one thing
1132/// every request has an answer for, which is why it is the constructor's only argument.
1133#[non_exhaustive]
1134pub struct TokenRequestContext<'a> {
1135    /// How the client is authenticating.
1136    pub credential: ClientCredential<'a>,
1137    /// The RFC 8707 `resource` parameters, in wire order.
1138    pub resources: &'a [String],
1139    /// The RFC 9396 `authorization_details` parameter, raw and unparsed.
1140    ///
1141    /// Here rather than on each [`TokenRequest`] variant for the reason `resources` is
1142    /// here: section 6 defines it as a parameter of the token REQUEST, independent of
1143    /// `grant_type`. What it MEANS does depend on the grant, and section 6 is what decides:
1144    /// `authorization_code` and `refresh_token` may narrow what the authorization request
1145    /// obtained and never widen it; `client_credentials` has no prior authorization request,
1146    /// so its details are checked against the supported types and used; and the device grant
1147    /// refuses any at all, because the RFC 8628 section 3.1 request cannot carry them in
1148    /// this crate and so granted nothing for a poll to narrow to.
1149    ///
1150    /// NOT FEATURE GATED, for the reason
1151    /// [`crate::authorization::AuthorizationRequest::authorization_details`] is not: a build
1152    /// without `rar` still has to be TOLD the parameter arrived, because refusing it is what
1153    /// RFC 9396 section 5 requires of exactly that build. Setting it in such a build makes the
1154    /// request an error rather than making the field meaningless.
1155    pub authorization_details: Option<&'a str>,
1156    /// The RFC 9449 `DPoP` request header, verbatim and unparsed.
1157    ///
1158    /// `None` means the client sent none, which is refused only when
1159    /// [`ServerConfig::require_dpop`] is set. When it is present and valid, the issued token is
1160    /// BOUND to the proof's key: `token_type` becomes `DPoP` and RFC 7662 introspection reports
1161    /// `cnf.jkt`.
1162    #[cfg(feature = "dpop")]
1163    pub dpop_proof: Option<&'a str>,
1164}
1165
1166impl<'a> TokenRequestContext<'a> {
1167    /// The context of a request that carried nothing but its client authentication, which is every
1168    /// request in a deployment that has enabled none of the parameters the other fields exist for.
1169    ///
1170    /// The RFC 8707 `resource` list, the RFC 9396 `authorization_details` and the RFC 9449 `DPoP`
1171    /// header are public fields on the returned value, so a host's token endpoint reads as the
1172    /// sequence of parameters it actually found on the wire.
1173    pub fn new(credential: ClientCredential<'a>) -> Self {
1174        TokenRequestContext {
1175            credential,
1176            resources: &[],
1177            authorization_details: None,
1178            #[cfg(feature = "dpop")]
1179            dpop_proof: None,
1180        }
1181    }
1182
1183    /// The RFC 8707 `resource` parameters the request carried, in wire order.
1184    pub fn with_resources(mut self, resources: &'a [String]) -> Self {
1185        self.resources = resources;
1186        self
1187    }
1188
1189    /// The RFC 9396 `authorization_details` parameter, raw and unparsed. Available in every
1190    /// build: without `rar` what it buys is a REFUSAL rather than a grant, which is what RFC 9396
1191    /// section 5 asks of a server that supports no detail type.
1192    pub fn with_authorization_details(mut self, authorization_details: &'a str) -> Self {
1193        self.authorization_details = Some(authorization_details);
1194        self
1195    }
1196
1197    /// The RFC 9449 `DPoP` request header, verbatim.
1198    #[cfg(feature = "dpop")]
1199    pub fn with_dpop_proof(mut self, dpop_proof: &'a str) -> Self {
1200        self.dpop_proof = Some(dpop_proof);
1201        self
1202    }
1203}
1204
1205/// What each grant helper needs about the REQUEST rather than about the grant.
1206///
1207/// One reference wide at every call site, which is actually SMALLER than the `Option<&str>` client
1208/// secret it replaces there. That is not incidental: these helpers are the token future, and
1209/// `tests/allocation.rs` fails if that future crosses tokio's 2048-byte debug boxing threshold.
1210pub(crate) struct Bound<'a> {
1211    /// The credential to authenticate with.
1212    pub(crate) cred: ClientCredential<'a>,
1213    /// The RFC 9449 section 6.1 thumbprint the issued token must be bound to, when the request
1214    /// carried a valid proof.
1215    #[cfg(feature = "dpop")]
1216    pub(crate) jkt: Option<&'a str>,
1217}
1218
1219impl<'a> Bound<'a> {
1220    /// A request authenticating with a shared secret and asking for no RFC 9449 binding.
1221    ///
1222    /// For the grant surfaces that reach `issue` from outside this module (RFC 8693 token
1223    /// exchange). They get an unbound token, which is honest: they have not been given a proof to
1224    /// bind one to. Wiring DPoP into them is a matter of threading a `Bound` in, not of changing
1225    /// anything here.
1226    ///
1227    /// `dead_code` because its only caller is behind another slice's cargo feature, and gating it
1228    /// on that feature by name would tie this module to a flag it has no other business knowing.
1229    #[allow(dead_code)]
1230    pub(crate) fn secret(client_secret: Option<&'a str>) -> Self {
1231        Bound {
1232            cred: ClientCredential::secret(client_secret),
1233            #[cfg(feature = "dpop")]
1234            jkt: None,
1235        }
1236    }
1237}
1238
1239/// Rejections for the host-driven verification-UI actions ([`AuthorizationServer::approve_device`]
1240/// / [`AuthorizationServer::deny_device`]). These are NOT wire errors: the RFC leaves the
1241/// verification interaction to the implementation, and the host renders these however its UI
1242/// wants.
1243///
1244/// `#[non_exhaustive]`, for the reason `lib.rs` gives for re-exporting it at all: a host's
1245/// verification UI is expected to MATCH on this, so a later release that has a new way to refuse an
1246/// entered code must be able to say so without that being a semver-major change for every host.
1247/// Every other host-facing failure enum in this crate carries the same attribute
1248/// ([`crate::registration::RegistrationFailure`], [`crate::events::ClientAuthFailure`],
1249/// [`crate::consent::AuthenticationRequirement`]); this one was the exception, and there was no argument
1250/// for the exception.
1251#[derive(Debug, Clone, PartialEq, Eq)]
1252#[non_exhaustive]
1253pub enum DeviceApprovalError {
1254    /// No live grant matches the entered code.
1255    UnknownUserCode,
1256    /// The grant existed but its lifetime has passed.
1257    Expired,
1258    /// The grant was already approved or denied.
1259    NotPending,
1260    /// The host's own [`crate::events::RateLimiter`] refused the attempt before the code was
1261    /// looked up at all. RFC 8628 section 5.1 makes throttling user-code entry a REQUIREMENT of
1262    /// the deployment, not an optimisation, so this is a first-class answer and not an error.
1263    RateLimited,
1264    /// The storage seam failed.
1265    Storage(StorageError),
1266}
1267
1268impl std::fmt::Display for DeviceApprovalError {
1269    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1270        match self {
1271            DeviceApprovalError::UnknownUserCode => f.write_str("unknown user code"),
1272            DeviceApprovalError::Expired => f.write_str("the code has expired"),
1273            DeviceApprovalError::NotPending => f.write_str("the code was already used"),
1274            DeviceApprovalError::RateLimited => f.write_str("too many attempts"),
1275            DeviceApprovalError::Storage(e) => write!(f, "{e}"),
1276        }
1277    }
1278}
1279
1280impl std::error::Error for DeviceApprovalError {}
1281
1282/// The RFC 8628 section 6.1 example alphabet: 20 consonants, chosen upstream to avoid vowels
1283/// (accidental words) and easily confused symbols.
1284const USER_CODE_ALPHABET: &[u8; 20] = b"BCDFGHJKLMNPQRSTVWXZ";
1285
1286/// Fresh OS randomness, hex encoded: `n` bytes of entropy, `2n` characters. Used for device codes
1287/// and tokens; 32 bytes = 256 bits, far past any brute-force horizon for a 10-minute artifact.
1288///
1289/// `None` means the OS would not give this process randomness. That is a real runtime condition —
1290/// an exhausted file descriptor table on the platforms where `getrandom` opens `/dev/urandom`, a
1291/// seccomp policy, a container without the syscall — and it is a condition every OTHER fallible
1292/// operation on these paths reports as [`ErrorCode::ServerError`] and returns from. Panicking
1293/// instead means a library aborting the HOST's request handler, and in a host built with
1294/// `panic = "abort"` it means taking the whole process down: an authorization server that stops
1295/// serving the requests it COULD still serve because one of them could not be given 32 bytes.
1296///
1297/// THERE IS NO PANICKING FORM ANY MORE, and its removal is the point. A `random_hex` that
1298/// `expect`ed survived here through 0.9.0 with a doc claiming its remaining call sites were
1299/// "outside the request path" — `crate::registration`'s minting and `crate::par`'s `request_uri`.
1300/// Both halves were false by the time the doc was read: PAR had already moved to this function,
1301/// and RFC 7591 dynamic registration is an ordinary unauthenticated `POST /register` route. A
1302/// function that cannot be called is the only reliable way to keep that from happening again.
1303///
1304/// The DRAW and the ENCODING are separate calls so that one `getrandom` call can feed several
1305/// artifacts. `getrandom::fill` is a SYSCALL, and the measurement that matters is that its cost is
1306/// almost entirely per CALL rather than per byte: 875 ns for one byte against 1025 ns for
1307/// thirty-two on the machine benches/README.md names. So the number of calls is the thing to
1308/// reduce, and a caller that needs two 32-byte artifacts should draw 64 bytes once rather than 32
1309/// bytes twice. See `issue`.
1310pub(crate) fn try_random_hex(n_bytes: usize) -> Option<String> {
1311    let mut buf = vec![0u8; n_bytes];
1312    getrandom::fill(&mut buf).ok()?;
1313    Some(hex_encode(&buf))
1314}
1315
1316/// The refusal a request-reachable randomness failure becomes, in the one spelling all three sites
1317/// use.
1318///
1319/// Modelled on [`storage_error`], and for the same reason: the host learns what happened through
1320/// its own logs, the wire gets the opaque RFC 6749 section 5.2 `server_error` and nothing about
1321/// this server's internals. It IS a server error in the section's sense — "the authorization
1322/// server encountered an unexpected condition that prevented it from fulfilling the request" — and
1323/// the client's correct response, retrying later, is the same one it would make to a store that
1324/// was briefly unavailable.
1325/// The fixed input [`AuthorizationServer::dummy_assertion_verify`] verifies over.
1326///
1327/// It is not a JWS signing input and does not need to be: an ES256 verification costs the same
1328/// whatever it is handed, and the string exists only so the operation is well defined.
1329#[cfg(feature = "client-assertion")]
1330const DUMMY_ASSERTION_SIGNING_INPUT: &str = "oauth-as dummy verification input";
1331
1332/// A real ES256 signature over [`DUMMY_ASSERTION_SIGNING_INPUT`], made once by a throwaway key.
1333///
1334/// IT IS A COST, NOT A CREDENTIAL, exactly as the dummy secret hash beside it is: the private half
1335/// was never kept, no registration names the public half below, and the value it signs is a
1336/// constant rather than a token request. What it buys is a verification that runs to completion —
1337/// a malformed signature or a point off the curve would be refused in the parse and cost a
1338/// fraction of the real work, which is the leak again.
1339#[cfg(feature = "client-assertion")]
1340const DUMMY_ASSERTION_SIGNATURE: [u8; 64] = [
1341    91, 15, 217, 171, 65, 158, 255, 105, 97, 207, 103, 199, 34, 188, 42, 123, 113, 63, 9, 92, 242,
1342    81, 20, 20, 147, 223, 209, 148, 122, 59, 212, 156, 132, 79, 44, 44, 108, 53, 228, 247, 251,
1343    153, 155, 251, 71, 102, 34, 231, 227, 160, 80, 16, 215, 84, 84, 74, 117, 3, 91, 5, 148, 20, 28,
1344    47,
1345];
1346
1347/// The public half of that throwaway key.
1348///
1349/// BUILT PER CALL, not cached in a `OnceLock`: this crate promises no global statics and no lazy
1350/// singletons (the crate docs say so, and `tests/allocation.rs` enforces it), and the four small
1351/// allocations a [`crate::jwt::PublicJwk`] costs are invisible beside the ES256 verification they
1352/// are there to feed — which is the whole point, since the KNOWN-id path this is matching pays
1353/// that verification too. The `Jwk` literal is the crate's own publishing shape, whose coordinates
1354/// are by construction the 32-byte base64url a verifier expects.
1355#[cfg(feature = "client-assertion")]
1356fn dummy_assertion_key() -> crate::jwt::PublicJwk {
1357    crate::jwt::Jwk {
1358        kty: "EC",
1359        crv: "P-256",
1360        x: "LIZkYOSRaSLc5uMxzlzV9pgt1ARaDl_3tZfRkt9mzFY".to_string(),
1361        y: "fBSzqWfCploda0TpKf3N56v6fk-fORAiVsXUmkWYWkw".to_string(),
1362        kid: "oauth-as-dummy-verification-key".to_string(),
1363        use_: "sig",
1364        alg: "ES256",
1365    }
1366    .to_public_jwk()
1367}
1368
1369fn randomness_error() -> ErrorResponse {
1370    ErrorResponse::new(ErrorCode::ServerError)
1371}
1372
1373/// WHAT THIS REQUEST'S PRESENTED CREDENTIAL HAS ALREADY BEEN CHARGED FOR, carried from wherever the
1374/// work happened to the ONE exit that refuses a client authentication.
1375///
1376/// This exists because the previous shape had no such carrier: every branch of
1377/// [`AuthorizationServer::authenticate_client`] was also an EXIT, so every branch had to remember to
1378/// charge the dummy verification the unknown-id path charges, and four consecutive audit rounds
1379/// found a branch that had forgotten. A flag that records what WAS spent, plus a single exit that
1380/// spends the remainder, cannot forget: adding a refusal adds a `return Ok(Refused(..))` that
1381/// carries this ledger unchanged, and the charge happens whether or not the author thought about it.
1382///
1383/// Both fields mean "a REAL verification of this kind has already been performed on this request",
1384/// so the exit owes the dummy for whichever kind the request PRESENTED and did not get. Nothing here
1385/// is a fact about the registration, which is the point: what a refusal costs must be a function of
1386/// what arrived on the wire, never of what the store holds.
1387#[derive(Default)]
1388struct CredentialCost {
1389    /// A secret verification ran through [`crate::client::ClientAuth::verify_with`].
1390    secret: bool,
1391    /// An RFC 7523 assertion verification was ATTEMPTED against a registration's keys.
1392    ///
1393    /// "Attempted" rather than "performed", and that is the residual documented as FOURTH on
1394    /// [`AuthorizationServer::authenticate_client`]: `verify_assertion` refuses a malformed
1395    /// assertion, and one whose `alg` is not the registration's, before it reaches any signature
1396    /// work. Setting the flag at the call preserves EXACTLY what this crate charged before this
1397    /// restructure, which is what makes the restructure reviewable as a mechanical change; closing
1398    /// that last gap needs `verify_assertion` to report whether it reached the signature, which is
1399    /// a change to a public function and is not this one.
1400    #[cfg(feature = "client-assertion")]
1401    assertion: bool,
1402}
1403
1404/// The outcome of examining a presented credential, BEFORE anything is charged, recorded or
1405/// emitted for it.
1406///
1407/// A value rather than a return: this is what lets the five refusal decisions in
1408/// [`AuthorizationServer::classify_client_credential`] be decisions instead of exits. Every one of
1409/// them hands back `Refused(failure)` and the single exit does the identical three things to all of
1410/// them — settle the credential's cost, record the failed attempt, tell the audit channel which
1411/// failure it was — before returning the one bare `invalid_client` RFC 6749 section 5.2 collapses
1412/// them into.
1413enum ClientAuthVerdict {
1414    /// The credential verified. The `Arc` is the registration the caller asked for.
1415    Authenticated(std::sync::Arc<Client>),
1416    /// The credential did not verify, for the reason the HOST's audit channel is told. The wire is
1417    /// told nothing beyond `invalid_client`.
1418    Refused(ClientAuthFailure),
1419}
1420
1421/// The rejection-sampling bound: the largest multiple of [`USER_CODE_ALPHABET`]'s length that fits
1422/// in a byte. Values at or above it are redrawn rather than folded, because folding them would
1423/// hand the low-index symbols extra probability.
1424const USER_CODE_REJECT_AT: u8 = 240;
1425
1426/// Map one random byte to a user-code symbol, or reject it for a redraw.
1427///
1428/// This is split out of [`random_user_code`] ON PURPOSE, and the reason is testability rather than
1429/// structure. The property that matters here is UNIFORMITY, and uniformity is not a property of any
1430/// single generated code: a test that can only look at sampled output has to argue statistically,
1431/// which means either a test that is flaky or a test that draws hundreds of thousands of samples to
1432/// notice a bias of a few percent. As a total function of one byte it can instead be checked
1433/// EXHAUSTIVELY over all 256 inputs, which settles the question outright (see
1434/// `src/tests/server.rs`).
1435///
1436/// The numbers are load bearing. 240 is the largest multiple of 20 below 256, so the accepted
1437/// values 0..=239 cover each of the 20 symbols exactly 12 times. Accepting one more value would
1438/// give symbol 0 a thirteenth preimage, an 8% excess over its peers, and RFC 8628 section 5.1 is
1439/// explicit that the user code's entropy is already only just sufficient (in combination with host
1440/// rate limiting) because the code is short enough for a human to type.
1441fn user_code_symbol(byte: u8) -> Option<u8> {
1442    if byte < USER_CODE_REJECT_AT {
1443        Some(USER_CODE_ALPHABET[(byte % 20) as usize])
1444    } else {
1445        None
1446    }
1447}
1448
1449/// A user code of `len` symbols over [`USER_CODE_ALPHABET`], unbiased via rejection sampling.
1450///
1451/// # The entropy is drawn in ONE call, not one per symbol
1452///
1453/// `getrandom::fill` is a SYSCALL and its cost is almost entirely per CALL rather than per byte:
1454/// MEASURED on the machine `benches/README.md` names, 875 ns for one byte and 1025 ns for
1455/// thirty-two. Drawing a byte at a time therefore made the call COUNT the entire cost, and sweeping
1456/// `user_code_length` showed a slope of roughly 960 to 1160 ns per SYMBOL. At the default eight
1457/// symbols that was about 8.2 us of `device_authorization`'s 9.27 us: 85% of an endpoint that takes
1458/// no credential from a public client, spent on syscall entry.
1459///
1460/// Being honest about the magnitude: on Linux with a vDSO `getrandom` (kernel 6.11 and glibc 2.42
1461/// or newer) the per-call cost is far lower, so the figures above are partly a macOS and BSD
1462/// `getentropy` number. THE CALL COUNT IS THE PORTABLE DEFECT, which is why this is worth doing
1463/// regardless of where it runs.
1464///
1465/// The uniformity argument is untouched. Every byte still goes through [`user_code_symbol`], values
1466/// at or above [`USER_CODE_REJECT_AT`] are still redrawn rather than folded, and the buffer is
1467/// simply refilled when it runs out, so a run of rejections costs another draw exactly as it did.
1468/// The buffer is a fixed stack array, so this adds no allocation: 64 bytes is enough that the
1469/// probability of needing a second draw for the default eight-symbol code is negligible (each byte
1470/// is accepted with probability 240/256), while staying small enough to sit in a frame.
1471///
1472/// `None` for the reason [`try_random_hex`] gives: this runs on RFC 8628 section 3.1's device
1473/// authorization endpoint, which is an ordinary request, and a library must not abort its host's
1474/// process because the OS momentarily would not hand over 64 bytes.
1475fn random_user_code(len: usize) -> Option<String> {
1476    let mut out = String::with_capacity(len);
1477    let mut buf = [0u8; 64];
1478    while out.len() < len {
1479        getrandom::fill(&mut buf).ok()?;
1480        for &byte in buf.iter() {
1481            if out.len() == len {
1482                break;
1483            }
1484            if let Some(symbol) = user_code_symbol(byte) {
1485                out.push(symbol as char);
1486            }
1487        }
1488    }
1489    Some(out)
1490}
1491
1492/// `WDJBMJHT` to `WDJB-MJHT`: hyphenate the middle for display when the length is even and at
1493/// least 4; otherwise the raw run is the display form.
1494fn display_user_code(raw: &str) -> String {
1495    // `% 2 == 0` rather than `is_multiple_of`, which did not stabilise until well after this
1496    // crate's supported floor. A library should compile on the oldest toolchain it reasonably can,
1497    // and this reads no worse.
1498    if raw.len() >= 4 && raw.len() % 2 == 0 {
1499        let mid = raw.len() / 2;
1500        format!("{}-{}", &raw[..mid], &raw[mid..])
1501    } else {
1502        raw.to_string()
1503    }
1504}
1505
1506/// Whether a `code_challenge` has the RFC 7636 section 4.2 S256 shape: the base64url (no padding)
1507/// encoding of a 32 byte digest, which is exactly 43 characters of the base64url alphabet.
1508///
1509/// Section 4.1's ABNF admits 43 to 128 characters generally, but that range covers the `plain`
1510/// method, where the challenge is the verifier itself. For S256 the length is fixed by the digest
1511/// size, so anything else was never produced by SHA-256 and cannot match any verifier.
1512fn challenge_is_well_formed(challenge: &str) -> bool {
1513    challenge.len() == 43
1514        && challenge
1515            .bytes()
1516            .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_'))
1517}
1518
1519/// The number of decimal digits `n` is written in, which is what `replay_key` has to reserve for
1520/// its length prefix. `0` is one digit, and there is no case with none.
1521#[cfg(any(feature = "client-assertion", feature = "dpop"))]
1522fn decimal_width(n: usize) -> usize {
1523    let mut width = 1;
1524    let mut rest = n / 10;
1525    while rest > 0 {
1526        rest /= 10;
1527        width += 1;
1528    }
1529    width
1530}
1531
1532/// The storage key one single-use identifier is claimed under.
1533///
1534/// NAMESPACED, and both halves matter. `kind` keeps an RFC 7523 assertion's `jti` from colliding
1535/// with an RFC 9449 proof's, which are different credentials with different lifetimes that a client
1536/// may well number from the same counter. `owner` (the client id for an assertion, the key
1537/// thumbprint for a proof) keeps one client from locking another out by choosing its `jti` values:
1538/// without it, an attacker could spend a victim's future `jti` values in advance, which is a denial
1539/// of service bought for the price of a refused request.
1540///
1541/// # Why the length prefix, and why a separator alone was not enough
1542///
1543/// The encoding is `kind ":" LEN(owner) ":" owner jti`, and it is INJECTIVE, which is a stronger
1544/// statement than "the parts are separated" and is the statement that matters:
1545///
1546/// - `kind` is one of this file's own two constants (`ca`, `dpop`), neither of which contains a
1547///   colon, so the first colon ends it;
1548/// - what follows is the decimal byte length of `owner`, digits only, so the next colon ends it;
1549/// - that length says exactly where `owner` stops and `jti` starts, whatever either of them
1550///   contains.
1551///
1552/// Every part is therefore recoverable from the key, so no two distinct `(kind, owner, jti)`
1553/// triples can produce the same one.
1554///
1555/// The previous encoding, `kind:owner:jti`, was NOT injective, and the counterexample is ordinary
1556/// rather than exotic. `ClientId::new` imposes no character restriction and URN-style client ids
1557/// are common, so a client registered as `urn` presenting the `jti` `client:foo:42` produced
1558/// `ca:urn:client:foo:42`, which is exactly what the client registered as `urn:client:foo` gets
1559/// for its `jti` `42`. Whoever claimed it first denied it to the other, so one client could spend
1560/// another's single-use slot and the victim's conforming assertion came back `invalid_client` as a
1561/// replay of something nobody sent. `tests/replay_key_collision.rs` runs that attack.
1562///
1563/// One allocation per assertion or proof, on a path that only exists when the feature is on: the
1564/// capacity below is exact, and `write!` into a `String` with room does not grow it.
1565#[cfg(any(feature = "client-assertion", feature = "dpop"))]
1566fn replay_key(kind: &str, owner: &str, jti: &str) -> String {
1567    use std::fmt::Write as _;
1568    let mut key = String::with_capacity(
1569        kind.len() + 1 + decimal_width(owner.len()) + 1 + owner.len() + jti.len(),
1570    );
1571    key.push_str(kind);
1572    key.push(':');
1573    // Infallible: `fmt::Write` for `String` cannot fail, and there is no error to handle.
1574    let _ = write!(key, "{}", owner.len());
1575    key.push(':');
1576    key.push_str(owner);
1577    key.push_str(jti);
1578    key
1579}
1580
1581fn storage_error(e: StorageError) -> ErrorResponse {
1582    // The host sees the real error through its own Storage impl; the wire gets the opaque code.
1583    let _ = e;
1584    ErrorResponse::new(ErrorCode::ServerError)
1585}
1586
1587/// The RFC 9396 authorization details flowing through one issuance: what a grant carries,
1588/// what a token request asked to narrow it to, and what the issued token ends up with.
1589///
1590/// A WRAPPER rather than the details themselves, and the reason is structural. `issue` and
1591/// the grant helpers have to have exactly ONE signature in every feature configuration: an
1592/// argument can carry a `cfg`, but the ARGUMENT AT THE CALL SITE cannot, so a gated
1593/// parameter would mean duplicating five call sites under `cfg` and giving the eight
1594/// existing arguments five more places to drift. This is the same reasoning `Bound` above
1595/// records for the RFC 9449 key binding.
1596///
1597/// Without `rar` this struct has no fields, so it is zero sized, every construction of it
1598/// compiles to nothing, and the default build's token future keeps the size
1599/// `tests/allocation.rs` pins. That gate exists because crossing tokio's 2048-byte debug
1600/// boxing threshold costs an allocation on every request that reaches the endpoint.
1601#[derive(Debug, Clone, Default, PartialEq, Eq)]
1602pub(crate) struct GrantedDetails {
1603    /// BOXED, and an `Option` so that the common case is a null pointer. The token future
1604    /// is 1824 bytes against tokio's 2048-byte debug boxing threshold, and this value is
1605    /// live in it at six points; carrying the details inline (three words) crossed the
1606    /// threshold and cost a 2 KB allocation on EVERY token request, which is exactly the
1607    /// regression `tests/allocation.rs` was written to catch, and it caught this one. One
1608    /// word costs 48 bytes of the future instead of 144, and the allocation behind the
1609    /// `Some` is paid only by a request that actually carries authorization details.
1610    #[cfg(feature = "rar")]
1611    inner: Option<Box<crate::rar::AuthorizationDetails>>,
1612}
1613
1614impl GrantedDetails {
1615    /// Wrap details read off a grant record, keeping the empty case a null pointer.
1616    #[cfg(feature = "rar")]
1617    fn of(details: &crate::rar::AuthorizationDetails) -> Self {
1618        GrantedDetails {
1619            inner: (!details.is_empty()).then(|| Box::new(details.clone())),
1620        }
1621    }
1622
1623    /// What an authorization code granted (RFC 9396 section 7: the details as approved by
1624    /// the resource owner and assigned to the token this code mints).
1625    fn of_code(record: &AuthorizationCodeRecord) -> Self {
1626        #[cfg(feature = "rar")]
1627        {
1628            GrantedDetails::of(&record.authorization_details)
1629        }
1630        #[cfg(not(feature = "rar"))]
1631        {
1632            let _ = record;
1633            GrantedDetails {}
1634        }
1635    }
1636
1637    /// What a refresh chain carries, which is what the previous leg narrowed it to.
1638    fn of_refresh(record: &RefreshTokenRecord) -> Self {
1639        #[cfg(feature = "rar")]
1640        {
1641            GrantedDetails::of(&record.authorization_details)
1642        }
1643        #[cfg(not(feature = "rar"))]
1644        {
1645            let _ = record;
1646            GrantedDetails {}
1647        }
1648    }
1649
1650    /// What an already-issued token carries, for a grant that continues it: the RFC 8693
1651    /// exchange, where the exchanged token inherits the subject token's details.
1652    ///
1653    /// `dead_code` for the same reason [`Bound::secret`] carries it: its only caller is
1654    /// behind another slice's cargo feature, and gating this on that feature by name would
1655    /// tie this module to a flag it has no other business knowing.
1656    #[allow(dead_code)]
1657    pub(crate) fn of_token(token: &IssuedToken) -> Self {
1658        #[cfg(feature = "rar")]
1659        {
1660            GrantedDetails::of(&token.authorization_details)
1661        }
1662        #[cfg(not(feature = "rar"))]
1663        {
1664            let _ = token;
1665            GrantedDetails {}
1666        }
1667    }
1668
1669    /// The owned details to write onto a record.
1670    #[cfg(feature = "rar")]
1671    fn into_details(self) -> crate::rar::AuthorizationDetails {
1672        self.inner.map(|d| *d).unwrap_or_default()
1673    }
1674
1675    /// Whether anything was asked for at all.
1676    ///
1677    /// `dead_code` for the same reason [`GrantedDetails::of_token`] carries it, and NOT because
1678    /// nothing calls it: `token_with_resources` uses it on the device-code branch to refuse
1679    /// `authorization_details` on a grant that never carried any. That call site is behind
1680    /// `#[cfg(feature = "rar")]`, so in a build without that feature this genuinely has no caller,
1681    /// and gating the allow on the feature by name would tie this wrapper to a flag whose whole
1682    /// purpose is to be invisible from here.
1683    #[allow(dead_code)]
1684    fn is_empty(&self) -> bool {
1685        #[cfg(feature = "rar")]
1686        {
1687            self.inner.is_none()
1688        }
1689        #[cfg(not(feature = "rar"))]
1690        {
1691            true
1692        }
1693    }
1694
1695    /// The details an issuance gets: `requested` may NARROW what `self` carries and may
1696    /// never widen it (RFC 9396 section 6). Delegated to [`crate::rar`], which is where the
1697    /// comparison rule and the argument for it live; this is the seam, not the rule.
1698    fn narrow(&self, requested: &GrantedDetails) -> Result<GrantedDetails, ErrorResponse> {
1699        #[cfg(feature = "rar")]
1700        {
1701            let requested = match &requested.inner {
1702                // Nothing asked for, so nothing to narrow: the grant passes through.
1703                None => return Ok(self.clone()),
1704                Some(requested) => requested.as_ref(),
1705            };
1706            // A grant carrying none narrows to nothing, and `narrow` refuses accordingly:
1707            // widening from nothing is still widening. The empty set allocates nothing.
1708            let empty = crate::rar::AuthorizationDetails::none();
1709            let granted = self.inner.as_deref().unwrap_or(&empty);
1710            Ok(GrantedDetails::of(&granted.narrow(requested)?))
1711        }
1712        #[cfg(not(feature = "rar"))]
1713        {
1714            let _ = requested;
1715            Ok(GrantedDetails {})
1716        }
1717    }
1718}
1719
1720/// The refresh chain an issuance CONTINUES: carried from the redeemed record to its replacement,
1721/// so that rotation preserves both the family (RFC 9700 section 4.14.2 revokes by grant) and the
1722/// absolute lifetime (a chain must not slide its own expiry forward every time it rotates).
1723pub(crate) struct RefreshChain {
1724    family_id: String,
1725    expires_at: Option<SystemTime>,
1726}
1727
1728/// The authorization server. Generic over the host's [`Storage`] and (for tests) the [`Clock`].
1729pub struct AuthorizationServer<S: Storage, C: Clock = SystemClock> {
1730    config: ServerConfig,
1731    store: S,
1732    clock: C,
1733    /// The RFC 8414 `token_endpoint` this server answers on, derived once from `config`.
1734    ///
1735    /// It exists as a field because it is compared against on every RFC 9449 proof and every RFC
1736    /// 7523 assertion (see [`AuthorizationServer::token_endpoint`]), and it is fixed for the life
1737    /// of the server: `config` is moved in here and there is no way to mutate it afterwards.
1738    /// `Box<str>` rather than `String` because it is never appended to, which keeps the field at
1739    /// 16 bytes instead of 24 on a struct `tests/allocation.rs` holds to a size budget.
1740    #[cfg(any(feature = "client-assertion", feature = "dpop"))]
1741    token_endpoint: Box<str>,
1742    /// The host's optional seams (audit sink, rate limiter, secret verifier). ONE pointer wide and
1743    /// null until the host installs something: see [`Hooks`] for why the three do not sit here as
1744    /// three separate fields.
1745    hooks: Hooks,
1746}
1747
1748impl<S: Storage> AuthorizationServer<S, SystemClock> {
1749    /// Construct with the real clock. This is the crate's allocation entry point: call it when
1750    /// (and only when) host config enables the AS.
1751    pub fn new(config: ServerConfig, store: S) -> Self {
1752        Self::with_clock(config, store, SystemClock)
1753    }
1754}
1755
1756impl<S: Storage, C: Clock> AuthorizationServer<S, C> {
1757    /// Construct with an injected clock (tests).
1758    pub fn with_clock(config: ServerConfig, store: S, clock: C) -> Self {
1759        // Derived HERE and not at each use, and derived exactly as
1760        // `AuthorizationServerMetadata::from_config` derives it, because a server whose own idea
1761        // of its token endpoint differs from the one it publishes refuses every conforming client.
1762        #[cfg(any(feature = "client-assertion", feature = "dpop"))]
1763        let token_endpoint: Box<str> = match &config.token_endpoint {
1764            Some(endpoint) => endpoint.as_str().into(),
1765            None => format!("{}/token", config.issuer.trim_end_matches('/')).into_boxed_str(),
1766        };
1767        AuthorizationServer {
1768            config,
1769            store,
1770            clock,
1771            #[cfg(any(feature = "client-assertion", feature = "dpop"))]
1772            token_endpoint,
1773            hooks: Hooks::new(),
1774        }
1775    }
1776
1777    /// Install the audit sink (RFC-agnostic; see [`crate::events`]). Builder-style so a host wires
1778    /// it at construction: `AuthorizationServer::new(cfg, store).with_event_sink(Box::new(sink))`.
1779    ///
1780    /// This crate logs nothing by itself. Without a sink, the two events that are evidence of
1781    /// compromise (authorization code replay, refresh token reuse) revoke silently, which means an
1782    /// operator learns about a stolen grant from a support ticket rather than from a log line.
1783    pub fn with_event_sink(mut self, sink: Box<dyn EventSink>) -> Self {
1784        self.hooks.install_event_sink(sink);
1785        self
1786    }
1787
1788    /// Install the rate limiter. THIS LIBRARY DOES NOT RATE LIMIT: RFC 8628 section 5.1 makes user
1789    /// code entropy adequate only IN COMBINATION WITH rate limiting of code entry, and only the
1790    /// host has a caller, an IP or a session to count against. See
1791    /// [`AuthorizationServer::approve_device`].
1792    pub fn with_rate_limiter(mut self, limiter: Box<dyn RateLimiter>) -> Self {
1793        self.hooks.install_rate_limiter(limiter);
1794        self
1795    }
1796
1797    /// Install the client secret verifier, for [`crate::client::SecretHash`] schemes this crate
1798    /// does not implement (argon2id, scrypt, an HSM). The built-in scheme needs no verifier and is
1799    /// never delegated to one.
1800    pub fn with_secret_verifier(
1801        mut self,
1802        verifier: Box<dyn crate::client::SecretVerifier>,
1803    ) -> Self {
1804        self.hooks.install_secret_verifier(verifier);
1805        self
1806    }
1807
1808    /// Install the RFC 7591 registration policy: who may create a client here.
1809    ///
1810    /// Required, not optional, for any host that sets [`ServerConfig::registration`]: with no
1811    /// policy installed every registration is refused, because an endpoint that mints clients and
1812    /// has been told nothing about who may use it is the abuse vector RFC 7591 section 5
1813    /// describes. See [`crate::registration::RegistrationPolicy`].
1814    pub fn with_registration_policy(
1815        mut self,
1816        policy: Box<dyn crate::registration::RegistrationPolicy>,
1817    ) -> Self {
1818        self.hooks.install_registration_policy(policy);
1819        self
1820    }
1821
1822    /// Install the RFC 9101 request object verification keys: which public key, under which
1823    /// algorithm, each client registered for signing request objects.
1824    ///
1825    /// Required, not optional, for a host that sets [`ServerConfig::jar`]: with no key source
1826    /// installed every `request` parameter is refused, because a server that cannot check a
1827    /// signature must not act on the claims under it. See [`crate::par::RequestObjectKeys`].
1828    #[cfg(feature = "jar")]
1829    pub fn with_request_object_keys(
1830        mut self,
1831        keys: Box<dyn crate::par::RequestObjectKeys>,
1832    ) -> Self {
1833        self.hooks.install_request_object_keys(keys);
1834        self
1835    }
1836
1837    /// Install the ES256 backend this server VERIFIES signatures with: RFC 9449 DPoP proofs, RFC
1838    /// 9101 request objects, RFC 7523 client assertions.
1839    ///
1840    /// Required unless `jwt-p256` is compiled in, which installs [`crate::jwt::P256Verifier`] as
1841    /// the default. With neither, every signed credential is REFUSED, exactly as an absent
1842    /// [`crate::par::RequestObjectKeys`] or an absent registration policy refuses: a server that
1843    /// cannot check a signature must never behave as though it had checked one.
1844    ///
1845    /// A verifier installed here WINS over the built-in one, because it was installed. That is the
1846    /// whole of the precedence rule, and it is why nothing in this crate's feature set is mutually
1847    /// exclusive: a dependency graph that unifies `jwt-p256` on cannot take a host's choice away.
1848    ///
1849    /// Run [`crate::signer_conformance`] against whatever you install here before you deploy it.
1850    // NO `#[must_use]`, for the crate-wide reason `tests/host_api_shape.rs` states and gates: this
1851    // is one of twenty-nine consuming builders, all of which move their receiver, so dropping the
1852    // result is a compile error at the next use of it rather than a setting silently lost. One
1853    // marked builder out of twenty-nine is the state that gate exists to refuse.
1854    #[cfg(feature = "jwt")]
1855    #[cfg_attr(docsrs, doc(cfg(feature = "jwt")))]
1856    pub fn with_es256_verifier(
1857        mut self,
1858        verifier: std::sync::Arc<dyn crate::jwt::Es256Verifier>,
1859    ) -> Self {
1860        self.hooks.install_es256_verifier(verifier);
1861        self
1862    }
1863
1864    /// The ES256 verifier this server will use, or `None` when it has none and must refuse.
1865    ///
1866    /// THE ONE PLACE the precedence rule lives: the host's installed verifier, else the built-in
1867    /// `jwt-p256` backend when that feature is compiled in, else nothing. Every caller
1868    /// (`verify_dpop`, the RFC 7523 assertion check, the RFC 9101 request object check) asks here
1869    /// and refuses on `None`, so there is exactly one definition of "no backend installed" and no
1870    /// path that can accidentally read it as "checked out".
1871    // Gated on the features that actually VERIFY rather than on `jwt`: a build that signs and
1872    // never checks anybody else's signature has no caller for this, and an uncalled resolver is
1873    // one more thing a reader has to work out is not reachable.
1874    #[cfg(any(feature = "dpop", feature = "jar", feature = "client-assertion"))]
1875    pub(crate) fn es256_verifier(&self) -> Option<&dyn crate::jwt::Es256Verifier> {
1876        match self.hooks.es256_verifier() {
1877            Some(installed) => Some(&**installed),
1878            #[cfg(feature = "jwt-p256")]
1879            None => Some(&crate::jwt::P256Verifier),
1880            #[cfg(not(feature = "jwt-p256"))]
1881            None => None,
1882        }
1883    }
1884
1885    /// The installed host seams, for a host that wants to emit its own events onto the same
1886    /// channel (a consent decision, say) or to consult its own limiter.
1887    pub fn hooks(&self) -> &Hooks {
1888        &self.hooks
1889    }
1890
1891    /// This server's clock, for the parts of the crate that live in other modules
1892    /// ([`crate::registration`]) and cannot reach the private field.
1893    pub(crate) fn now(&self) -> SystemTime {
1894        self.clock.now()
1895    }
1896
1897    /// How long a [`crate::store::RevocationBarrier`] this server records has to stand.
1898    ///
1899    /// A barrier exists to refuse a write from a request that was ALREADY HOLDING a record when
1900    /// the revocation ran. What bounds that is not the request's wall time, which nothing here can
1901    /// know, but the lifetime of what such a request could still write: no in-flight issuance can
1902    /// produce a credential that outlives the longest this server is configured to mint. So the
1903    /// deadline is now plus that longest lifetime, and a barrier that has stood for it has
1904    /// outlived everything it was recorded to refuse.
1905    ///
1906    /// All four durations are considered rather than just the access token TTL, and the
1907    /// `refresh_reuse_window` is in the list on purpose: a chain with NO absolute lifetime
1908    /// (`refresh_token_ttl: None`) has its spent records retained for exactly that window, so it
1909    /// is the longest-lived thing such a family produces.
1910    ///
1911    /// Erring long costs storage that [`crate::store::Storage::sweep_expired`] reclaims. Erring
1912    /// short costs the revocation itself, silently, which is the failure this whole mechanism
1913    /// exists to prevent, so the asymmetry is taken deliberately.
1914    /// Put a taken refresh record back, after a judgement that is NOT evidence of compromise.
1915    ///
1916    /// [`crate::store::Storage::take_refresh_token`] removes the record before anything about it
1917    /// has been judged, so every refusal that is not reuse has to restore it, or a client that
1918    /// merely asked for the wrong scope would lose its chain. Five refusal paths do that, and they
1919    /// all handle a refused write the same way, so they call this rather than each writing the
1920    /// handling out: the last time this crate had one operation at several seams, the copy that
1921    /// diverged was the one that failed open.
1922    ///
1923    /// A [`crate::store::WriteOutcome::RefusedRevoked`] here is neither an error nor a surprise.
1924    /// It means a revocation reached this family, client or consent while the record was out of
1925    /// the store, and the record STAYING gone is exactly what that revocation asked for. The
1926    /// caller's own refusal is the answer to the client either way. A genuine storage failure is
1927    /// still fatal, because then it is not known whether the chain survived.
1928    async fn restore_refresh_token(
1929        &self,
1930        record: crate::token::RefreshTokenRecord,
1931    ) -> Result<(), ErrorResponse> {
1932        let _outcome = self
1933            .store
1934            .put_refresh_token(record)
1935            .await
1936            .map_err(storage_error)?;
1937        Ok(())
1938    }
1939
1940    /// Take back an access token this server wrote and then decided not to hand out.
1941    ///
1942    /// Issuance is two writes and [`crate::store::Storage`] has no transaction spanning them (see
1943    /// the trait's own docs on why requiring cross-key atomicity would exclude most stores). A
1944    /// revocation landing between them leaves the first write standing, so issuance has to be able
1945    /// to reverse itself, and this is that reversal.
1946    ///
1947    /// FAILURE IS SWALLOWED, deliberately, and this is the one judgement in it. The caller is
1948    /// already returning an error and the client will never see the token; the alternative is to
1949    /// report a storage failure INSTEAD of the revocation, which tells the caller the wrong thing
1950    /// about why it was refused. What is left behind on a failed undo is one orphaned access token
1951    /// that [`crate::store::Storage::sweep_expired`] reclaims at its own expiry, and which no
1952    /// client holds the string for.
1953    async fn undo_issuance(&self, access_token: &str) {
1954        let _ = self.store.delete_token(access_token).await;
1955    }
1956
1957    /// The window a revocation happening NOW should record: the instant it happened, and the
1958    /// instant past which nothing it was entitled to kill can still be in flight.
1959    ///
1960    /// Both come from ONE reading of the clock. Taking `now` twice would let the two instants
1961    /// straddle a tick, and `recorded_at` is compared against by every subsequent write, so a
1962    /// `recorded_at` even fractionally later than the revocation's own effect is a grant
1963    /// wrongly refused.
1964    pub(crate) fn revocation_window(&self) -> crate::store::RevocationWindow {
1965        let longest = self
1966            .config
1967            .access_token_ttl
1968            .max(self.config.refresh_token_ttl.unwrap_or_default())
1969            .max(self.config.refresh_reuse_window)
1970            .max(self.config.authorization_code_ttl)
1971            .max(self.config.device_code_ttl);
1972        let recorded_at = self.clock.now();
1973        crate::store::RevocationWindow {
1974            recorded_at,
1975            until: saturating_deadline(recorded_at, longest),
1976        }
1977    }
1978
1979    /// Turn the freshly minted random token into what actually goes on the wire: itself when the
1980    /// format is opaque (the byte-for-byte pre-feature behaviour), or an RFC 9068 access token
1981    /// carrying it as `jti` when the host configured signing.
1982    ///
1983    /// SYNC, and it stops one step short of the signature, which is what the awkward return type
1984    /// buys. The host's [`crate::jwt::Es256Signer`] may be a network round trip, so signing is
1985    /// async; if this function were async instead, the whole [`AccessTokenClaims`] value below
1986    /// would live across that suspension point and join the token endpoint's coroutine frame,
1987    /// which `tests/allocation.rs` holds under tokio's 2048-byte debug boxing threshold. Splitting
1988    /// here means the claims are built and consumed on the sync side and only a `String` crosses
1989    /// the await. MEASURED: 1344 bytes before the seam, 1360 after; an async `wire_access_token`
1990    /// measured 1656.
1991    // Eight arguments, since the RFC 8705 binding is a property of the REQUEST rather than of the
1992    // grant. Same allow and same reason as `issue` below: a private function with one call site,
1993    // whose arguments would have to live across every await in the token future if they were
1994    // bundled into a struct to satisfy a lint.
1995    #[allow(clippy::too_many_arguments)]
1996    #[cfg(feature = "jwt")]
1997    fn access_token_signing_input(
1998        &self,
1999        client: &Client,
2000        subject: Option<&str>,
2001        scope: &ScopeSet,
2002        resource: &[String],
2003        details: &GrantedDetails,
2004        now: SystemTime,
2005        // The instant the caller has decided this token dies at, which is NOT always
2006        // `now + access_token_ttl`: see the `lifetime_ceiling` argument of
2007        // [`AuthorizationServer::issue`]. Handed in rather than recomputed here, because a signed
2008        // `exp` that disagrees with the stored expiry is a token two halves of one deployment
2009        // enforce differently.
2010        expires_at: SystemTime,
2011        jti: String,
2012        bound: &Bound<'_>,
2013        actor: &GrantedActor,
2014        // What the HOST reported about the resource owner's login, for RFC 9470 s6.1. Handed in
2015        // rather than read off the record being written next door, for the reason `expires_at` is:
2016        // the signed claim and the persisted record must be the one value stated twice, and the
2017        // record is written after this returns.
2018        authentication: &GrantedAuthentication,
2019    ) -> Result<Result<(&crate::jwt::JwtConfig, String), String>, ErrorResponse> {
2020        // Only the RFC 8705 binding is read out of it here; without that feature the
2021        // signed claim set does not depend on how the client authenticated.
2022        #[cfg(not(feature = "mtls"))]
2023        let _ = bound;
2024        // Likewise the RFC 9396 details, which reach the claim set only under `rar`. This is not
2025        // decoration: `http,jwt` without `rar` is exactly what the conformance server builds, and
2026        // neither a default build (where this function does not exist) nor `--all-features` (where
2027        // `details` IS read) compiles that combination. CI caught it; local testing had not.
2028        #[cfg(not(feature = "rar"))]
2029        let _ = details;
2030        let jwt = match &self.config.access_token_format {
2031            // `Err` is not a failure here: it is the OPAQUE arm, carrying the random string
2032            // through unchanged. Two arms of one `Result` rather than an `Option` plus a moved-out
2033            // `jti`, because the opaque path must not copy the string it already has.
2034            AccessTokenFormat::Opaque => return Ok(Err(jti)),
2035            AccessTokenFormat::Jwt(jwt) => jwt,
2036        };
2037        // Without the feature the wrapper is empty and genuinely unused here, exactly as `bound`
2038        // is without `dpop`; see the note at the top of `issue`.
2039        #[cfg(not(feature = "token-exchange"))]
2040        let _ = actor;
2041        // Same for the RFC 9470 report: without `consent` there is no field on the claim set to
2042        // fill and no field on the wrapper to fill it from.
2043        #[cfg(not(feature = "consent"))]
2044        let _ = authentication;
2045        let claims = AccessTokenClaims {
2046            // The SAME spelling the RFC 8414 document publishes, the RFC 9207 `iss` parameter
2047            // carries and introspection reports. `issuer_identifier` trims a trailing slash, and
2048            // the raw config value does not, so a host configuring "https://as.example/" used to
2049            // publish "https://as.example" everywhere except here. A resource server doing the
2050            // byte comparison RFC 9068 s4 and RFC 8414 s3.3 call for would then reject every
2051            // token this server signs, or be patched to compare loosely, which disables the
2052            // mix-up defence RFC 9207 exists to provide. One server, one identity, everywhere it
2053            // states it.
2054            iss: self.issuer_identifier().to_string(),
2055            exp: crate::jwt::unix_seconds(expires_at)
2056                .map_err(|_| ErrorResponse::new(ErrorCode::ServerError))?,
2057            // RFC 8707 s2 with RFC 9068 s2.2: when the client named the resource server(s) it
2058            // means to call, THAT is the audience, and the configured default is not. The default
2059            // is a deployment-wide statement made before any request arrived; the resource
2060            // indicator is this grant's own, and honouring the wider one would hand back a token
2061            // valid somewhere the client did not ask for and the user did not approve. Single
2062            // resource stays the plain-string form RFC 7519 s4.1.3 allows, because that is what
2063            // most resource servers actually parse.
2064            aud: match resource {
2065                [] => jwt.audience().clone(),
2066                [one] => crate::jwt::Audience::One(one.clone()),
2067                many => crate::jwt::Audience::Many(many.to_vec()),
2068            },
2069            // RFC 9068 section 2.2: `sub` is REQUIRED. Where there is no resource owner (a
2070            // client-only grant) the RFC's own answer is the client identifier, so the claim is
2071            // never absent and never invented.
2072            sub: subject
2073                .unwrap_or_else(|| client.client_id.as_str())
2074                .to_string(),
2075            client_id: client.client_id.as_str().to_string(),
2076            iat: crate::jwt::unix_seconds(now)
2077                .map_err(|_| ErrorResponse::new(ErrorCode::ServerError))?,
2078            jti,
2079            scope: (!scope.is_empty()).then(|| scope.to_string()),
2080            // RFC 9396 s9.1: the AS is RECOMMENDED to add the authorization details as a
2081            // top-level claim, so a resource server holding a JWT does not have to call
2082            // introspection to learn what the token actually authorizes. NOT filtered per
2083            // audience, which s9.1 also suggests: filtering means deciding which detail
2084            // belongs to which resource server, and only the API that defined the `type`
2085            // knows that (s6.1). A detail that names its own `locations` has already said
2086            // so, in a form the resource server can check for itself.
2087            #[cfg(feature = "rar")]
2088            authorization_details: details.clone().into_details(),
2089            // RFC 9470 s6.1, in the token itself. s6.2 (introspection) was the only channel this
2090            // crate answered on through 0.9.1, and it is the channel a JWT deployment does not
2091            // use: a resource server that verifies the signature locally never asks this server
2092            // anything, so a step-up it could not see in the claims was one it had to take the
2093            // client's word for. That is the failure the s3 challenge exists to prevent.
2094            //
2095            // The SAME conversion the introspection response uses, deliberately: `unix_seconds`
2096            // answers `None` for an instant before the epoch, so a host-reported `auth_time` this
2097            // server cannot state is stated by NEITHER channel rather than by one of them. Two
2098            // channels disagreeing about one token is worse than both being silent, and silence
2099            // here re-challenges (s3) rather than admitting anything.
2100            #[cfg(feature = "consent")]
2101            auth_time: authentication
2102                .authentication
2103                .as_ref()
2104                .and_then(|a| unix_seconds(a.auth_time)),
2105            #[cfg(feature = "consent")]
2106            acr: authentication
2107                .authentication
2108                .as_ref()
2109                .and_then(|a| a.acr.as_deref().map(str::to_string)),
2110            // EVERY binding the AS-side record carries, in the form a resource server can check
2111            // for itself without calling introspection at all. RFC 9449 s6.1 for `jkt`, RFC 8705
2112            // s3.1 for `x5t#S256`, built the same way introspection builds it so the two can never
2113            // disagree about what a token is bound to.
2114            #[cfg(any(feature = "dpop", feature = "mtls"))]
2115            cnf: {
2116                let cnf = crate::token::Confirmation {
2117                    #[cfg(feature = "dpop")]
2118                    jkt: bound.jkt.map(str::to_string),
2119                    #[cfg(feature = "mtls")]
2120                    x5t_s256: bound.cred.certificate.map(|c| *c.thumbprint()),
2121                };
2122                (!cnf.is_empty()).then_some(cnf)
2123            },
2124            // RFC 8693 s4.1, in the token itself. A JWT is typically validated offline by a
2125            // resource server that never introspects, so the record alone would not reach it.
2126            #[cfg(feature = "token-exchange")]
2127            act: actor.act.as_deref().cloned(),
2128        };
2129        // `claims` dies HERE, before the caller awaits the signature. See this function's doc.
2130        jwt.signing_input(&claims)
2131            .map(|input| Ok((&**jwt, input)))
2132            .map_err(|e| {
2133                // The host sees the real error through its own logging of the config it supplied; the
2134                // wire gets the opaque code, as with storage failures.
2135                let _ = e;
2136                ErrorResponse::new(ErrorCode::ServerError)
2137            })
2138    }
2139
2140    /// The RFC 7517 key set to serve at `jwks_uri`, or `None` when tokens are opaque. PUBLIC key
2141    /// parameters only.
2142    #[cfg(feature = "jwt")]
2143    pub fn jwks(&self) -> Option<Jwks> {
2144        match &self.config.access_token_format {
2145            AccessTokenFormat::Opaque => None,
2146            AccessTokenFormat::Jwt(jwt) => Some(jwt.jwks()),
2147        }
2148    }
2149
2150    /// The configured `jwks_uri`, or `None`. An RFC 8414 metadata document must advertise
2151    /// `jwks_uri` exactly when this is `Some`: advertising a key set for an AS that signs nothing
2152    /// is a lie, and signing without advertising leaves resource servers unable to verify.
2153    #[cfg(feature = "jwt")]
2154    pub fn jwks_uri(&self) -> Option<&str> {
2155        match &self.config.access_token_format {
2156            AccessTokenFormat::Opaque => None,
2157            AccessTokenFormat::Jwt(jwt) => jwt.jwks_uri(),
2158        }
2159    }
2160
2161    /// The configuration.
2162    pub fn config(&self) -> &ServerConfig {
2163        &self.config
2164    }
2165
2166    /// The RFC 8414 document THIS server would publish, which is the one a host should serve.
2167    ///
2168    /// Different from [`crate::metadata::AuthorizationServerMetadata::from_config`], and the
2169    /// difference is the point: `from_config` sees the configuration and nothing else, while some
2170    /// of what the document promises depends on a seam the host INSTALLED on the server. RFC 7523
2171    /// `private_key_jwt` is the case that forced this. It is ES256, so it is honest exactly when
2172    /// this server can check an ES256 signature, and that is a property of
2173    /// [`AuthorizationServer::with_es256_verifier`] plus the `jwt-p256` feature, neither of which
2174    /// a `&ServerConfig` can see. A method the document names and the token endpoint refuses
2175    /// every time is not a defect a client can work around: it did what it was told.
2176    ///
2177    /// `from_config` therefore advertises only what the CONFIGURATION alone establishes, and this
2178    /// adds back exactly what the installed seams establish. It is the direction that fails safe:
2179    /// a host that ignores this method under-advertises rather than inviting clients to use a
2180    /// method that cannot work. [`crate::http::ServiceBuilder::build`] uses this one.
2181    pub fn metadata(&self) -> crate::metadata::AuthorizationServerMetadata {
2182        #[allow(unused_mut)]
2183        let mut meta = crate::metadata::AuthorizationServerMetadata::from_config(&self.config);
2184        // Only the ES256-dependent members need adjusting, and only in a build that could verify
2185        // at all: the `cfg` is exactly the set `es256_verifier` is gated on, because those three
2186        // features are the three that advertise something an ES256 signature check has to back.
2187        #[cfg(any(feature = "client-assertion", feature = "jar", feature = "dpop"))]
2188        if self.es256_verifier().is_some() {
2189            meta.es256_verification_is_available();
2190        }
2191        meta
2192    }
2193
2194    /// This server's issuer identifier, in the ONE spelling it publishes.
2195    ///
2196    /// RFC 9207 section 2.4 has the client compare the `iss` authorization response parameter
2197    /// against the issuer it started the flow with, for EQUALITY, and the value it started from is
2198    /// the RFC 8414 `issuer` metadata member. `AuthorizationServerMetadata::from_config` trims a
2199    /// trailing slash off the configured issuer, so this trims it too: a host that wrote
2200    /// `https://as.example/` must not end up with two spellings of its own identity, because the
2201    /// mismatch would read to a conforming client as a mix-up attack in progress.
2202    // `pub(crate)` so `par.rs` can check an RFC 9101 request object's `aud` claim against the
2203    // ONE spelling this server publishes, rather than re-deriving it and risking a second one.
2204    pub(crate) fn issuer_identifier(&self) -> &str {
2205        self.config.issuer.trim_end_matches('/')
2206    }
2207
2208    /// Validate one RFC 8707 `resource` list from the wire into the owned form the grant records.
2209    ///
2210    /// Section 2 gives `invalid_target` as the answer for a value this server will not issue a
2211    /// token for, which covers both a malformed indicator and (at the token endpoint, see
2212    /// [`AuthorizationServer::narrow_resources`]) one that was never granted.
2213    pub(crate) fn validate_resources<'a>(
2214        &self,
2215        requested: impl IntoIterator<Item = &'a str>,
2216    ) -> Result<Vec<String>, ErrorResponse> {
2217        // The cap is checked as the list is walked rather than up front, because `requested` is an
2218        // iterator (both call sites hand in a borrowed `map`, so there is nothing to count without
2219        // collecting first, and collecting is the allocation the cap exists to bound). Refusing at
2220        // the moment the cap is exceeded means at most `MAX_RESOURCE_INDICATORS` elements are ever
2221        // examined however many were sent.
2222        let mut out = Vec::new();
2223        // Counted on the INPUT, not on `out`. Counting deduplicated survivors would leave a caller
2224        // free to send ten thousand copies of one URI: each still costs a full scan of `out`, so
2225        // the work is unbounded even though the result is small.
2226        let mut seen = 0usize;
2227        for value in requested {
2228            seen += 1;
2229            if seen > MAX_RESOURCE_INDICATORS {
2230                return Err(ErrorResponse::new(ErrorCode::InvalidTarget)
2231                    .with_description("too many resource indicators (RFC 8707 s2)"));
2232            }
2233            if !crate::authorization::is_valid_resource_indicator(value) {
2234                // The offending value is NOT echoed: RFC 6749 section 5.2 restricts
2235                // error_description to a charset an attacker-supplied URI need not respect, and
2236                // naming the parameter is enough for the developer who sent it.
2237                return Err(
2238                    ErrorResponse::new(ErrorCode::InvalidTarget).with_description(
2239                        "resource must be an absolute URI with no fragment (RFC 8707 s2)",
2240                    ),
2241                );
2242            }
2243            // SYNTAX IS NOT AUTHORISATION. RFC 8707 section 2 requires `invalid_target` when the
2244            // server "is unwilling or unable to issue an access token" for a named resource, and
2245            // until this check existed the server had no notion of unwilling: any well-formed URI
2246            // was accepted. That matters most with the `jwt` feature on, where the requested
2247            // resource REPLACES the configured audience in the RFC 9068 `aud` claim, so a client
2248            // registered for one resource server could name another and receive a token this
2249            // server had signed, with that other server's identifier in `aud`. The second server
2250            // fetches our JWKS, the signature verifies, `aud` names it, and it authorises on a
2251            // scope string the two happen to share. Scope-name collision between resource servers
2252            // is the ordinary case, not an exotic one.
2253            //
2254            // FACTORED OUT rather than written here, because RFC 8693 s2.1.1 `audience` names the
2255            // same thing as `resource` in a spelling that need not be a URI: it must skip the
2256            // syntax check above and must NOT skip this one. See `target_is_permitted`.
2257            self.target_is_permitted(value)?;
2258            // A repeated identical indicator is the same request twice, not two audiences.
2259            if !out.iter().any(|kept: &String| kept == value) {
2260                out.push(value.to_string());
2261            }
2262        }
2263        Ok(out)
2264    }
2265
2266    /// Whether this server is willing to issue a token naming `value` at all: the
2267    /// [`ServerConfig::allowed_resources`] check, on its own.
2268    ///
2269    /// SYNTAX IS NOT AUTHORISATION, and the two questions had been welded together. RFC 8707
2270    /// section 2 requires `invalid_target` when the server "is unwilling or unable to issue an
2271    /// access token" for a named target, and that is a statement about the DEPLOYMENT: it is how an
2272    /// operator decommissions a resource server, and until it existed any well-formed URI was
2273    /// accepted. It matters most with the `jwt` feature on, where the requested target REPLACES the
2274    /// configured audience in the RFC 9068 `aud` claim, so a client registered for one resource
2275    /// server could name another and receive a token this server had signed, with that other
2276    /// server's identifier in `aud`. The second server fetches our JWKS, the signature verifies,
2277    /// `aud` names it, and it authorises on a scope string the two happen to share. Scope-name
2278    /// collision between resource servers is the ordinary case, not an exotic one.
2279    ///
2280    /// SPLIT OUT OF [`AuthorizationServer::validate_resources`] because RFC 8693 section 2.1.1
2281    /// `audience` names the same target in a spelling that need not be a URI. It therefore has to
2282    /// skip the absolute-URI check and must not thereby skip this one, which is what it did through
2283    /// 0.9.1: an operator who decommissioned a resource server by removing it from the allowlist
2284    /// went on handing out signed tokens naming it, to any client holding a token whose grant
2285    /// recorded it, via `audience` on an exchange.
2286    ///
2287    /// An EMPTY allowlist keeps the previous behaviour rather than refusing everything, because
2288    /// refusing would break every deployment that already relies on resource indicators. That means
2289    /// this check protects only hosts that configure it, which is stated plainly on the config field
2290    /// rather than left for someone to discover.
2291    pub(crate) fn target_is_permitted(&self, value: &str) -> Result<(), ErrorResponse> {
2292        if let Some(allowed) = &self.config.allowed_resources {
2293            if !allowed.iter().any(|a| &**a == value) {
2294                // The offending value is NOT echoed, for the reason `validate_resources` gives:
2295                // RFC 6749 section 5.2 restricts `error_description` to a charset an
2296                // attacker-supplied value need not respect.
2297                return Err(ErrorResponse::new(ErrorCode::InvalidTarget)
2298                    .with_description("this server does not issue tokens for that resource"));
2299            }
2300        }
2301        Ok(())
2302    }
2303
2304    /// The resource set an issuance actually gets: the request may NARROW what the grant carries,
2305    /// never widen it (RFC 8707 section 2).
2306    ///
2307    /// This is deliberately the same shape as the RFC 6749 section 6 scope rule that
2308    /// [`AuthorizationServer::refresh_token`] applies, because it is the same argument: the user
2309    /// approved a specific thing, and a later leg of the same grant asking for MORE than that is
2310    /// either a client bug or an escalation attempt, and the AS cannot tell which.
2311    ///
2312    /// A grant that named no resource has nothing to narrow, so a token request that names one is
2313    /// widening from nothing and is refused. Answering it any other way would let the token
2314    /// endpoint mint an audience the authorization request never obtained.
2315    ///
2316    /// # On the O(requested * granted) scan
2317    ///
2318    /// It needs no cap of its own, and deliberately does not have one, but the reason is now a
2319    /// count rather than the claim it used to make. That claim was that BOTH sides came through
2320    /// [`AuthorizationServer::validate_resources`], and it stopped being true when RFC 8693 token
2321    /// exchange started calling this: `granted` is still a validated grant record, but `requested`
2322    /// there is `resource` values (validated) PLUS section 2.1.1 `audience` values, which skip the
2323    /// URI-syntax check by design. What still bounds both sides is a CAP EACH:
2324    /// [`MAX_RESOURCE_INDICATORS`] on the resource list, and
2325    /// [`crate::token_exchange::MAX_AUDIENCE_VALUES`], deliberately the same number, on the
2326    /// audience list. So the worst case is 16 * 32 string comparisons, most of which fail on the
2327    /// length, and a third constant here would be a third number to keep in step with the other two
2328    /// for no gain.
2329    pub(crate) fn narrow_resources(
2330        granted: &[String],
2331        requested: &[String],
2332    ) -> Result<Vec<String>, ErrorResponse> {
2333        if requested.is_empty() {
2334            return Ok(granted.to_vec());
2335        }
2336        for want in requested {
2337            if !granted.iter().any(|g| g == want) {
2338                return Err(ErrorResponse::new(ErrorCode::InvalidTarget)
2339                    .with_description("resource was not granted by the authorization request"));
2340            }
2341        }
2342        Ok(requested.to_vec())
2343    }
2344
2345    /// [`AuthorizationServer::narrow_resources`], then the allowlist ON WHAT SURVIVES.
2346    ///
2347    /// THE ALLOWLIST IS A PROPERTY OF THE ISSUED SET, NOT OF THE REQUESTED ONE, and that is the
2348    /// whole of this function. [`AuthorizationServer::target_is_permitted`] was consulted only
2349    /// about values a REQUEST named, and `narrow_resources` returns the grant's recorded list
2350    /// verbatim when the request names none, which is the ordinary case: a refresh sending no
2351    /// `resource`, an RFC 8693 exchange sending neither `resource` nor `audience`. So the values
2352    /// that reached the issued token on that path were never checked at all.
2353    ///
2354    /// What that cost is exactly what `target_is_permitted`'s own doc describes for the `audience`
2355    /// spelling, reached by a shorter road: an operator decommissions a resource server by removing
2356    /// it from [`ServerConfig::allowed_resources`], and every grant that had already recorded it
2357    /// goes on minting tokens naming it. Under `jwt` the issued set REPLACES the configured
2358    /// audience in the RFC 9068 `aud` claim, so those are freshly SIGNED tokens naming a server the
2359    /// operator believes they switched off, and with `refresh_token_ttl` defaulting to `None` the
2360    /// chain that mints them never expires.
2361    ///
2362    /// # Refused when NAMED, dropped when INHERITED, and the difference is what the client asked
2363    ///
2364    /// A request that NAMES a decommissioned target is asking for something this server will not
2365    /// issue, and RFC 8707 section 2's answer to that is `invalid_target`. That is what
2366    /// `narrow_resources` above already delivers, through the same
2367    /// [`AuthorizationServer::target_is_permitted`] every named value goes through.
2368    ///
2369    /// A request that names NOTHING has asked for whatever the grant still supports, so a target
2370    /// the server has since retired is DROPPED rather than made fatal. Refusing there would mean an
2371    /// operator retiring one resource server instantly breaks every live chain that ever recorded
2372    /// it, including clients that only ever call the ones still standing, over a value none of them
2373    /// mentioned. That is a bigger outage than the decommissioning itself, and it is avoidable.
2374    ///
2375    /// UNLESS NOTHING SURVIVES, which is the one case that must refuse. An empty resource list does
2376    /// not mean "no audience restriction" further down: [`AuthorizationServer::issue`] falls back
2377    /// to the configured [`crate::jwt::JwtConfig`] audience when the list is empty, so silently
2378    /// emptying a grant that DID name resources would WIDEN the token to this deployment's default
2379    /// audience, which is the opposite of what dropping was meant to do.
2380    pub(crate) fn narrow_and_permit(
2381        &self,
2382        granted: &[String],
2383        requested: &[String],
2384    ) -> Result<Vec<String>, ErrorResponse> {
2385        let issued = Self::narrow_resources(granted, requested)?;
2386        if !requested.is_empty() {
2387            // Every value here is one the request named, so each has already been through
2388            // `target_is_permitted` at its own endpoint, and naming a retired one is fatal there.
2389            return Ok(issued);
2390        }
2391        let permitted: Vec<String> = issued
2392            .into_iter()
2393            .filter(|value| self.target_is_permitted(value).is_ok())
2394            .collect();
2395        if permitted.is_empty() && !granted.is_empty() {
2396            return Err(
2397                ErrorResponse::new(ErrorCode::InvalidTarget).with_description(
2398                    "this server no longer issues tokens for any resource this grant names",
2399                ),
2400            );
2401        }
2402        Ok(permitted)
2403    }
2404
2405    /// The storage seam, so the host can administer its own store.
2406    ///
2407    /// The one administrative operation this crate REQUIRES of the host is eviction:
2408    /// [`Storage::sweep_expired`] must be called on some host-chosen schedule, because nothing in
2409    /// this crate ever evicts anything on its own. There is no background task here and there will
2410    /// not be one (see the crate docs on zero cost until enabled), so a host that never sweeps has
2411    /// a store that only grows: consumed authorization codes and spent refresh records are
2412    /// retained ON PURPOSE until their expiry (that retention is what makes replay and reuse
2413    /// detectable), and expired access tokens and abandoned device grants are simply never looked
2414    /// at again. Anything else the host wants to do here, such as listing, is its own store's
2415    /// business and not this trait's.
2416    pub fn store(&self) -> &S {
2417        &self.store
2418    }
2419
2420    /// Register (or replace) a client the HOST provisioned: no policy is consulted, no credential
2421    /// is minted, and whatever is handed in is what the store holds.
2422    ///
2423    /// This is the out-of-band half of registration. RFC 7591 dynamic client registration is the
2424    /// other half and it is built:
2425    /// [`AuthorizationServer::register_dynamic_client`] layers on this one, adding the
2426    /// [`crate::registration::RegistrationPolicy`] check, the minted `client_id` and secret, and
2427    /// the RFC 7592 management credential. A host calling THIS method is asserting that the
2428    /// registration was authorised somewhere it can point to.
2429    ///
2430    /// # What IS checked, despite "no policy is consulted"
2431    ///
2432    /// Every `redirect_uris` entry, against the same rule RFC 7591 registration applies (RFC 6749
2433    /// section 3.1.2: an absolute URI with no fragment, and nothing outside printable ASCII, which
2434    /// RFC 3986 requires of a URI anyway). This is not policy: it is whether the value can work at
2435    /// all, and the two ways of creating a client used to disagree about it, with the DIRECT one —
2436    /// the one a default build has, since `http` and dynamic registration are optional — being the
2437    /// permissive half.
2438    ///
2439    /// The reason it is worth a refusal here rather than being left to fail later is WHERE it fails
2440    /// later, which is three layers away from its cause. A redirect URI containing a space passes
2441    /// the authorization endpoint's exact-string match, the host's resolver approves, an
2442    /// authorization code is MINTED AND PERSISTED, and only then does building the `Location`
2443    /// header fail: the user sees a 500, the client is never reached, and the code sits in storage
2444    /// until it expires while every retry does it again. Refusing at registration turns that into
2445    /// one error, at startup, in front of the person who wrote the value.
2446    ///
2447    /// A [`StorageError`] rather than a new error type, and it is the honest reading rather than a
2448    /// convenience: the answer is that this registration cannot be stored as given. The message
2449    /// names the offending URI, because the caller is the operator who just supplied it.
2450    pub async fn register_client(&self, client: Client) -> Result<(), StorageError> {
2451        for uri in &client.redirect_uris {
2452            // The SAME function `crate::registration`'s `redirect_uri_is_registerable` delegates
2453            // to, called directly rather than copied: two implementations of one rule is how the
2454            // two registration paths came to disagree in the first place.
2455            if !crate::authorization::is_valid_resource_indicator(uri) {
2456                return Err(StorageError::new(format!(
2457                    "redirect_uri {uri:?} is not registerable: RFC 6749 s3.1.2 requires an \
2458                     absolute URI with no fragment, and the authorization endpoint matches it by \
2459                     exact string, so a registration this server cannot reproduce is a client that \
2460                     can never complete a flow"
2461                )));
2462            }
2463        }
2464        // A registration whose `default_scopes` exceed its `allowed_scopes` is NOT refused here,
2465        // and that is a decision rather than an omission. It is a real state an operator reaches by
2466        // the control this crate tells them to reach for: `tests/registration_narrowing.rs` narrows
2467        // `allowed_scopes` alone, exactly as an operator correcting an over-broad registration
2468        // does, and refusing that would turn a security control into an error message. What made
2469        // the disagreement dangerous was the GRANT it produced, not the registration itself, and
2470        // that is closed at the point of issuance instead: see
2471        // `AuthorizationServer::granted_default_scope`, which trims the default to the allowance so
2472        // no grant is ever minted that the rotation ceiling would later destroy.
2473        self.store.put_client(client).await
2474    }
2475
2476    /// Verify `presented` against a stored verifier NOBODY holds the secret for, and throw the
2477    /// answer away.
2478    ///
2479    /// The point is the elapsed time, not the result: see the timing section on
2480    /// [`AuthorizationServer::authenticate_client`], which is the only caller. The scheme comes
2481    /// from the installed [`crate::client::SecretVerifier`] when it offers one, so the dummy costs
2482    /// what that host's real registrations cost; otherwise it is the crate's own `sha256-hex`,
2483    /// which prices the built-in scheme exactly and any other scheme not at all.
2484    ///
2485    /// `None` presented does no work, and that is not an omission FOR A SECRET: `verify_with`
2486    /// answers `false` for a confidential registration with no secret presented without verifying
2487    /// anything either, so doing nothing here is what MATCHES the known-id path rather than what
2488    /// diverges from it. It is NOT a general claim, and reading it as one is what left the RFC
2489    /// 7523 path uncovered through 0.9.0: a `private_key_jwt` request carries an assertion and no
2490    /// secret at all, so on that path the known id paid an ES256 verification and the unknown id
2491    /// paid nothing. [`AuthorizationServer::dummy_assertion_verify`] is that half.
2492    ///
2493    /// [`std::hint::black_box`] because the whole call is dead code by every rule the optimiser
2494    /// has: a pure function whose result is discarded. Without it the hashing this exists to spend
2495    /// is exactly what a release build is entitled to delete.
2496    fn dummy_verify(&self, presented: Option<&str>) {
2497        let presented = match presented {
2498            Some(p) => p,
2499            None => return,
2500        };
2501        let verifier = self.hooks.secret_verifier();
2502        let hash = match verifier.and_then(|v| v.dummy_hash()) {
2503            Some(hash) => hash,
2504            // The crate's own, over a constant that is not a secret and does not need to be: no
2505            // registration names this hash, so nothing authenticates by presenting the string it
2506            // was built from. It is a cost, not a credential.
2507            None => crate::client::SecretHash::sha256(
2508                "oauth-as dummy verification input; no registration is stored under this",
2509            ),
2510        };
2511        let dummy = crate::client::ClientAuth::ConfidentialSecretHash { hash };
2512        let _ = std::hint::black_box(dummy.verify_with(Some(presented), verifier));
2513    }
2514
2515    /// [`AuthorizationServer::dummy_verify`]'s twin for RFC 7523 client assertions: one ES256
2516    /// verification through the installed seam, over a key nobody registered, answer discarded.
2517    ///
2518    /// WHY IT IS NEEDED SEPARATELY. A `private_key_jwt` request carries a `client_assertion` and
2519    /// NO `client_secret` — `authenticate_by_assertion` refuses a request carrying both — so
2520    /// `dummy_verify` was handed `None` and returned immediately, while a KNOWN id on the same
2521    /// request went on to a real ES256 verification, which `crate::jwt` prices at about 133
2522    /// microseconds. The probed id is attacker-chosen and free: `crate::http` reads it from the
2523    /// UNSIGNED `sub` of the assertion when the form carries no `client_id`, and a garbage
2524    /// signature never reaches `claim_replay_id`, so the probe is repeatable and averageable while
2525    /// per-id throttling sees exactly one request per candidate — the same shape the secret case
2526    /// above describes.
2527    ///
2528    /// WHICH VERIFICATION, and why it is not always the ES256 one. RFC 7523 has TWO client
2529    /// authentication methods and this crate implements both: `private_key_jwt` is ES256 through
2530    /// the installed seam, and `client_secret_jwt` is an HS256 HMAC over the registered secret,
2531    /// which `verify_assertion` performs itself and which therefore needs no seam at all. So the
2532    /// real cost of a known id depends on the deployment, and the dummy tracks it: an ES256
2533    /// verification where a verifier is installed, and an HS256 tag verification where none is,
2534    /// which is exactly the shape of a `client_secret_jwt`-only deployment. This used to return
2535    /// early on a missing verifier on the grounds that "the real path refuses without verifying
2536    /// anything either", and that was true only of `private_key_jwt`: a `client_secret_jwt`
2537    /// deployment with no ES256 backend had its known ids paying an HMAC while unknown ids paid
2538    /// nothing.
2539    ///
2540    /// WHAT REMAINS, stated rather than left to be discovered: a deployment running BOTH methods
2541    /// can still be timed to tell which method a KNOWN, EXISTING client is registered for, because
2542    /// an HMAC and an ES256 verification are three orders of magnitude apart and no single dummy
2543    /// can be both. That is a much weaker fact than the one this closes: it says nothing about
2544    /// whether an id exists, so it is not an enumeration primitive, and it is only readable for an
2545    /// id the attacker already knows is registered.
2546    ///
2547    /// WHAT IT COSTS, stated rather than left to be found: a probe that could have reached a
2548    /// verification now buys one of this server's time whether or not its id exists. That is a
2549    /// denial-of-service consideration, it was already true for every KNOWN id, and the
2550    /// [`RateLimiter`] is charged for the attempt either way. The aim of the whole mechanism is
2551    /// that the two ids cost the same; see the residuals section on
2552    /// [`AuthorizationServer::authenticate_client`] for where they still do not, because that
2553    /// claim has now been false in three different ways across three audit rounds and stating it
2554    /// without the exceptions is what let each one survive.
2555    #[cfg(feature = "client-assertion")]
2556    fn dummy_assertion_verify(&self) {
2557        match self.es256_verifier() {
2558            Some(verifier) => {
2559                let _ = std::hint::black_box(verifier.verify(
2560                    &dummy_assertion_key(),
2561                    DUMMY_ASSERTION_SIGNING_INPUT.as_bytes(),
2562                    &DUMMY_ASSERTION_SIGNATURE,
2563                ));
2564            }
2565            // The `client_secret_jwt` cost. The secret is the signing input itself, which is a
2566            // constant: as with the ES256 signature above this is a COST and not a credential, and
2567            // an HMAC costs the same whatever key it is handed.
2568            None => {
2569                let _ = std::hint::black_box(crate::jwt::verify_hs256(
2570                    DUMMY_ASSERTION_SIGNING_INPUT.as_bytes(),
2571                    DUMMY_ASSERTION_SIGNING_INPUT.as_bytes(),
2572                    &DUMMY_ASSERTION_SIGNATURE[..32],
2573                ));
2574            }
2575        }
2576    }
2577
2578    /// Whether this request COULD have reached an assertion verification had its client id
2579    /// existed, which is the only condition under which charging
2580    /// [`AuthorizationServer::dummy_assertion_verify`] makes the two ids cost the same.
2581    ///
2582    /// It mirrors, exactly, the three refusals `authenticate_by_assertion` makes before it decodes
2583    /// a byte: an assertion has to be present, the RFC 7521 section 4.2 `client_assertion_type` has
2584    /// to be the one this server implements, and RFC 6749 section 2.3 forbids a `client_secret`
2585    /// alongside. Charging on the presence of the assertion ALONE, which is what this used to do,
2586    /// reopened the leak pointing the other way: a known id sending a garbage
2587    /// `client_assertion_type` was refused in nanoseconds while an unknown id sending the same
2588    /// bytes paid a full ES256 verification, so one request per candidate separated "registered"
2589    /// from "not registered" again. The two conditions have to be kept in step; if a refusal is
2590    /// ever added there before the verification, it belongs here too.
2591    #[cfg(feature = "client-assertion")]
2592    fn assertion_could_be_verified(cred: &ClientCredential<'_>) -> bool {
2593        cred.client_assertion.is_some()
2594            && cred.client_assertion_type == Some(CLIENT_ASSERTION_TYPE)
2595            && cred.client_secret.is_none()
2596    }
2597
2598    /// Authenticate a client for a token-plane call: unknown id and failed secret verification
2599    /// collapse into the same `invalid_client` so an attacker cannot probe which ids exist.
2600    ///
2601    /// The WIRE keeps that collapse. The host's audit channel does NOT: an installed
2602    /// [`crate::events::EventSink`] is told which of the two it actually was, because the host is
2603    /// not the attacker and "a thousand unknown client ids" and "a thousand wrong secrets for one
2604    /// real client" are different incidents.
2605    ///
2606    /// The host's [`RateLimiter`] is asked FIRST, before the store is touched, so a refused
2607    /// attempt costs nothing and reveals nothing (RFC 9700 section 4.13 on credential stuffing at
2608    /// the token endpoint).
2609    ///
2610    /// # The collapse is a TIMING property too, and how far this goes
2611    ///
2612    /// Answering both cases with the same code is worth nothing if the two take visibly different
2613    /// wall times, and the unknown-id path is naturally the cheaper one: there is no secret to
2614    /// verify. So this function performs a DUMMY verification when the id is unknown, through the
2615    /// same [`crate::client::ClientAuth::verify_with`] every real authentication goes through.
2616    ///
2617    /// What that covers exactly:
2618    ///
2619    /// - [`crate::client::ClientAuth::ConfidentialSecretHash`] in the built-in `sha256-hex`
2620    ///   scheme, and [`crate::client::ClientAuth::ConfidentialSecret`], which this crate can price
2621    ///   for itself because it performs the comparison itself.
2622    /// - A HOST scheme, whenever the installed [`crate::client::SecretVerifier`] implements
2623    ///   [`crate::client::SecretVerifier::dummy_hash`]. This is the case that matters, because a
2624    ///   host scheme is the expensive one.
2625    /// - RFC 7523 CLIENT ASSERTIONS, through
2626    ///   [`AuthorizationServer::dummy_assertion_verify`]: a request presenting a
2627    ///   `client_assertion` for an unknown id pays one verification, which is what the known id
2628    ///   pays. This was uncovered through 0.9.0 because such a request carries no
2629    ///   `client_secret`, so the secret dummy above had nothing to do and the doc for that
2630    ///   `None` case read as though nothing needed doing.
2631    /// - THE ASSERTION CASE IN BOTH DIRECTIONS, which the first fix for it did not. The dummy is
2632    ///   charged only when the request could have reached a verification at all
2633    ///   (`assertion_could_be_verified`), and a KNOWN id whose registration does not use
2634    ///   assertions pays it too before `authenticate_by_assertion` returns `WrongPrincipal`.
2635    ///   Without the second half the mechanism ran backwards: an id registered for
2636    ///   `client_secret_basic` refused in nanoseconds while an unknown id sending identical bytes
2637    ///   paid 133 microseconds, which separates "registered, but not this way" from "not
2638    ///   registered" in one un-throttleable request per candidate.
2639    ///
2640    /// - THE KNOWN-ID PATHS THAT DID NO VERIFICATION AT ALL, which is the same rule pointing the
2641    ///   other way and had three separate instances. An expired `client_secret_expires_at` returns
2642    ///   before every credential branch; `verify_with` answers `false` in nanoseconds for a
2643    ///   `Public` or `ConfidentialAssertion` registration handed a posted secret; a mutual-TLS
2644    ///   registration refuses on a thumbprint comparison. Each of those was FASTER than the
2645    ///   unknown-id path, which pays `dummy_verify` through the host's scheme, so "fast" positively
2646    ///   identified a registered id. Each is charged the dummy by the one exit below.
2647    ///
2648    /// - THE REFUSALS THAT PRECEDE THE ASSERTION VERIFICATION, which the fix for the case above
2649    ///   introduced. `authenticate_by_assertion` refuses three requests before it decodes a byte,
2650    ///   and all three are charged `dummy_verify` by the same exit, rather than through a comment
2651    ///   asking two functions to be kept in step. They were not kept in step: a request carrying an
2652    ///   assertion AND a `client_secret` was refused by a known id for free while an unknown id
2653    ///   paid the host's secret scheme, so the fix for the previous bullet reopened the bullet
2654    ///   before it. See the comment at the top of that function.
2655    ///
2656    /// # ONE EXIT, and why the list above is a history rather than a checklist
2657    ///
2658    /// Every bullet above was fixed at its own SITE, and each fix was found incomplete by the next
2659    /// audit round: round 7 added a dummy, round 8 found the dummy made an unknown id cost MORE
2660    /// than a known one, round 9 found three known-id paths costing nothing, and round 10 found
2661    /// that round 9's own guard had two refusal sites it did not reach. The diagnosis recorded here
2662    /// after round 10 was that the costly half is not the branching — it is that THE BRANCHES WERE
2663    /// ALSO THE EXITS. Every refusal returned from where it was decided, so every refusal had to
2664    /// remember to charge, and that is the obligation that was forgotten four times.
2665    ///
2666    /// So this function no longer refuses anywhere. It asks
2667    /// [`AuthorizationServer::classify_client_credential`] for a [`ClientAuthVerdict`], a VALUE, and
2668    /// there is exactly one place that turns a `Refused` into a wire answer. That place charges
2669    /// [`AuthorizationServer::settle_credential_cost`] unconditionally, records the failed attempt
2670    /// and emits the failure, in that order, for every refusal there is or ever will be. What a
2671    /// refusal costs is therefore a function of what the request PRESENTED (through
2672    /// [`CredentialCost`], which records only what was really spent) and of nothing the store holds,
2673    /// which is the property the four rounds above were each trying to reach one site at a time.
2674    ///
2675    /// A reviewer checks this by COUNTING: one `ClientAuthVerdict::Refused` arm, one
2676    /// `settle_credential_cost` call on it, and no `ErrorCode::InvalidClient` built anywhere in the
2677    /// credential path except there. Adding a refusal means adding a `return Ok(Refused(..))`, which
2678    /// cannot skip the charge because it does not do the charging.
2679    ///
2680    /// THE RATE-LIMIT GATE IS NOT ONE OF THOSE REFUSALS and is deliberately kept out of the count.
2681    /// It is answered before the store is touched, from a public `client_id` and nothing else, so it
2682    /// cannot vary with any fact about a registration — and charging a dummy verification there is
2683    /// exactly the amplification [`crate::rate_limit::CLIENT_AUTHENTICATION_FAILURE_CEILING_DIVISOR`]
2684    /// exists to bound: past the failure ceiling a denial is what an attacker can buy at
2685    /// [`crate::rate_limit::ATTEMPT_COST`] apiece, thousands per window per client id, and each one
2686    /// would then buy the host's argon2id as well. The gate refuses free, on purpose, and it is
2687    /// separated into [`AuthorizationServer::admit_client_authentication`] so that the credential
2688    /// decision below has one exit rather than nearly one.
2689    ///
2690    /// # What it does NOT cover, stated rather than left to be discovered
2691    ///
2692    /// FIRST, a host scheme whose verifier returns `None` from `dummy_hash`. This crate cannot
2693    /// invent a well-formed argon2id or bcrypt encoding to hand such a verifier, and one in the
2694    /// wrong scheme would be rejected on inspection in microseconds, which is the leak again. A
2695    /// deployment using a slow custom scheme SHOULD implement that method; until it does, the wire
2696    /// answer is still collapsed but the wall time is not. That residual is unfixable HERE, unlike
2697    /// the assertion one above, which is why the assertion one was closed rather than written down
2698    /// beside it.
2699    ///
2700    /// SECOND, WHICH RFC 7523 METHOD an id that is known to exist is registered for, in a
2701    /// deployment running both. `private_key_jwt` costs an ES256 verification and
2702    /// `client_secret_jwt` costs an HMAC, three orders of magnitude apart, and one dummy cannot be
2703    /// both; [`AuthorizationServer::dummy_assertion_verify`] picks whichever matches the
2704    /// deployment. This is not an enumeration primitive: it says nothing about whether an id
2705    /// exists, and it is readable only for one the attacker already knows does.
2706    ///
2707    /// THIRD, and in the same class: a `ConfidentialSecret` registration compares its secret with
2708    /// [`crate::client::constant_time_eq`], two SHA-256 digests, while the dummy an unknown id pays
2709    /// goes through the INSTALLED verifier's scheme. In a deployment that installs argon2id and
2710    /// still holds plaintext-secret registrations those differ by milliseconds, so such a
2711    /// registration is distinguishable from an unknown id. A deployment that stores hashes
2712    /// throughout, which is what [`crate::client::SecretHash`] exists for and what RFC 9700 section
2713    /// 4.13 expects, has nothing here to read. The structural fix above is what closes it properly;
2714    /// charging a second dummy on top of the real comparison would only make the same registration
2715    /// distinguishable in the other direction.
2716    ///
2717    /// FOURTH, and FOUND BY THIS RESTRUCTURE rather than closed by it, because closing it is not a
2718    /// mechanical change. A registration that DOES authenticate by assertion, handed an assertion
2719    /// that `verify_assertion` refuses before any signature work — one over
2720    /// [`crate::client_assertion::MAX_ASSERTION_BYTES`], one that is not a compact JWS, or one whose
2721    /// `alg` is not the registration's — pays nothing, while an unknown id sending the same bytes
2722    /// pays [`AuthorizationServer::dummy_assertion_verify`]. That separates "registered for RFC 7523
2723    /// with these keys" from "not registered", which is a narrower fact than the ones above (it is
2724    /// only readable for the assertion-registered subset) but it is the same shape. Closing it needs
2725    /// `verify_assertion` to say whether it reached the signature; deciding it from the returned
2726    /// [`crate::client_assertion::AssertionFailure`] variant would be exactly the "kept in step by a
2727    /// comment" coupling this restructure exists to remove. `CredentialCost::assertion` records
2728    /// where that boundary currently is.
2729    ///
2730    /// RFC 8705 mutual TLS is not on this list because there is nothing to price: the HOST
2731    /// verified the certificate before this crate saw it, and what happens here is a thumbprint
2732    /// comparison.
2733    pub(crate) async fn authenticate_client(
2734        &self,
2735        client_id: &ClientId,
2736        cred: &ClientCredential<'_>,
2737    ) -> Result<std::sync::Arc<Client>, ErrorResponse> {
2738        let attempt = Attempt::ClientAuthentication {
2739            client_id: client_id.as_str(),
2740        };
2741        self.admit_client_authentication(attempt, client_id)?;
2742
2743        // WHAT WAS SPENT, carried to the exit. Nothing reads it except `settle_credential_cost`,
2744        // and nothing writes it except the two places that perform a real verification.
2745        let mut paid = CredentialCost::default();
2746        // THE DECISION, as a value. Its `?` is a STORAGE failure and not a refusal: it is
2747        // `server_error`, it says nothing about whether the id exists (the store answered nothing at
2748        // all), and it is the same propagation every other endpoint in this crate makes.
2749        let verdict = self
2750            .classify_client_credential(client_id, cred, &mut paid)
2751            .await?;
2752        match verdict {
2753            ClientAuthVerdict::Authenticated(client) => {
2754                self.hooks.record(attempt, AttemptOutcome::Succeeded);
2755                Ok(client)
2756            }
2757            // THE ONE EXIT. Every refusal in the credential path arrives here, and this is the only
2758            // place any of them costs, records or reports anything.
2759            ClientAuthVerdict::Refused(failure) => {
2760                // UNCONDITIONAL, and the order is the one every site used before: charge, record,
2761                // emit. What is charged depends on what the request PRESENTED and on what has
2762                // already been spent for it — never on which branch decided the refusal.
2763                self.settle_credential_cost(cred, &paid);
2764                self.hooks.record(attempt, AttemptOutcome::Failed);
2765                self.hooks.emit(|| Event::ClientAuthenticationFailed {
2766                    client_id: client_id.as_str(),
2767                    failure,
2768                });
2769                // The one bare `invalid_client` (RFC 6749 section 5.2). The host's audit channel was
2770                // told which refusal it was one line up; the wire is told nothing, because the
2771                // difference between "no such client", "expired", "wrong secret" and "wrong kind of
2772                // credential" is exactly what tells a caller that an id is real.
2773                Err(ErrorResponse::new(ErrorCode::InvalidClient))
2774            }
2775        }
2776    }
2777
2778    /// The host's [`RateLimiter`] gate, asked FIRST, before the store is touched.
2779    ///
2780    /// SEPARATE FROM THE CREDENTIAL DECISION on purpose; see "ONE EXIT" on
2781    /// [`AuthorizationServer::authenticate_client`]. It refuses on a public `client_id` and nothing
2782    /// else, so it cannot vary with a fact about a registration, and it must stay FREE: a denial is
2783    /// what an attacker can buy in bulk once the failure ceiling is reached, so charging a dummy
2784    /// verification here would sell them the host's password hashing at
2785    /// [`crate::rate_limit::ATTEMPT_COST`] apiece.
2786    fn admit_client_authentication(
2787        &self,
2788        attempt: Attempt<'_>,
2789        client_id: &ClientId,
2790    ) -> Result<(), ErrorResponse> {
2791        if self.hooks.check(attempt) == RateLimitDecision::Deny {
2792            self.hooks.emit(|| Event::ClientAuthenticationFailed {
2793                client_id: client_id.as_str(),
2794                failure: ClientAuthFailure::RateLimited,
2795            });
2796            // The same `invalid_client` a wrong secret gets. A distinct code would tell an
2797            // attacker that they had found a live client id and merely hit the throttle.
2798            return Err(ErrorResponse::new(ErrorCode::InvalidClient));
2799        }
2800        Ok(())
2801    }
2802
2803    /// PAY FOR WHAT THIS REQUEST PRESENTED AND DID NOT GET, at the one exit that refuses.
2804    ///
2805    /// The unknown-id path is naturally the cheapest one — there is no secret to verify and no key
2806    /// to verify against — so the collapse of "no such client" and "wrong credential" into one
2807    /// `invalid_client` is worth nothing unless the two take the same wall time. Every refusal
2808    /// therefore ends here, and here spends whatever a request of this shape would have spent had it
2809    /// got as far as a verification:
2810    ///
2811    /// - [`AuthorizationServer::dummy_verify`] unless a real secret verification already ran. It
2812    ///   does nothing when no secret was presented, which is what MATCHES the known-id path:
2813    ///   `verify_with` refuses a confidential registration with no secret without verifying anything
2814    ///   either.
2815    /// - [`AuthorizationServer::dummy_assertion_verify`] unless a real assertion verification
2816    ///   already ran, and only when this request COULD have reached one
2817    ///   ([`AuthorizationServer::assertion_could_be_verified`]) — a request every registration would
2818    ///   have refused before decoding a byte must not pay for a verification on either path.
2819    ///
2820    /// Both conditions are facts about `cred` and about work already done. NEITHER is a fact about
2821    /// the registration, and that is the whole property: no caller of this can make a refusal cheap
2822    /// by knowing something about the client.
2823    fn settle_credential_cost(&self, cred: &ClientCredential<'_>, paid: &CredentialCost) {
2824        if !paid.secret {
2825            self.dummy_verify(cred.client_secret);
2826        }
2827        #[cfg(feature = "client-assertion")]
2828        if !paid.assertion && Self::assertion_could_be_verified(cred) {
2829            self.dummy_assertion_verify();
2830        }
2831    }
2832
2833    /// EXAMINE the presented credential and say what it amounts to. Charge nothing, record nothing,
2834    /// emit nothing: those belong to the single exit in
2835    /// [`AuthorizationServer::authenticate_client`], which is what keeps them from being forgotten.
2836    ///
2837    /// Every branch here is the one it was before this became a function of its own — the RFC 7591
2838    /// section 3.2.1 expiry gate, the RFC 7523 assertion path, the RFC 8705 mutual-TLS path and the
2839    /// shared-secret comparison, in that order — and each answers with a value instead of a wire
2840    /// refusal. `paid` is written by the two places that perform real verification work, so the exit
2841    /// can charge the remainder.
2842    async fn classify_client_credential(
2843        &self,
2844        client_id: &ClientId,
2845        cred: &ClientCredential<'_>,
2846        paid: &mut CredentialCost,
2847    ) -> Result<ClientAuthVerdict, ErrorResponse> {
2848        let found = self
2849            .store
2850            .get_client(client_id)
2851            .await
2852            .map_err(storage_error)?;
2853        let client = match found {
2854            Some(client) => client,
2855            // THE COLLAPSE IS ALSO A TIMING PROPERTY. Answering here in the time of one store read,
2856            // while a real id additionally pays a full secret verification, enumerates the whole
2857            // registry at one request per candidate: under the shape `with_secret_verifier` exists
2858            // to serve — a `ConfidentialSecretHash` in a host scheme such as argon2id — that is
2859            // roughly two milliseconds against two hundred, and per-id throttling cannot see it
2860            // because the attacker never repeats an id. Nothing is charged HERE any more; the exit
2861            // charges it, for this refusal and for every other one, through the same `verify_with`
2862            // seam a real authentication uses. See `SecretVerifier::dummy_hash` for the half only
2863            // the host can supply.
2864            None => return Ok(ClientAuthVerdict::Refused(ClientAuthFailure::UnknownClient)),
2865        };
2866        // RFC 7591 section 3.2.1 `client_secret_expires_at`. THIS SERVER MINTS THAT VALUE AND
2867        // PUBLISHES IT TO THE REGISTRANT, and until this check existed it never looked at it again:
2868        // a secret this deployment had itself declared dead on the wire went on authenticating
2869        // forever. A rotation window a server announces and does not enforce is worse than none,
2870        // because the operator believes the old secret stopped working on the day the response
2871        // said it would.
2872        //
2873        // Checked HERE, before every credential branch below, rather than beside the secret
2874        // comparison: `client_secret_jwt` (RFC 7523) also authenticates with the shared secret, so
2875        // a check attached only to the direct comparison would let the same expired secret keep
2876        // working through the assertion path. A registration with no secret carries `None` and is
2877        // unaffected, and `private_key_jwt` and mutual TLS do not authenticate with a secret at
2878        // all, so refusing them here costs nothing they could have used.
2879        //
2880        // Section 3.2.1: `0` means the secret NEVER expires. It is not "expired at the epoch", and
2881        // reading it that way would break every registration that took the default.
2882        if let Some(registration) = &client.registration {
2883            if let Some(expires_at) = registration.client_secret_expires_at {
2884                let expired = expires_at != 0
2885                    && self
2886                        .clock
2887                        .now()
2888                        .duration_since(std::time::UNIX_EPOCH)
2889                        .map(|since| since.as_secs() >= expires_at)
2890                        // A clock before the epoch cannot say anything has expired yet, and is the
2891                        // host's problem rather than a reason to refuse a live credential.
2892                        .unwrap_or(false);
2893                if expired {
2894                    // NOTHING IS VERIFIED ON THIS PATH, and that is why it was once the fastest
2895                    // refusal in the function: an expired registration answered in the time of one
2896                    // store read while an unknown id paid a full `dummy_verify` through the host's
2897                    // scheme, so "fast" meant "this id is registered", the one fact the bare
2898                    // `invalid_client` exists to withhold. `paid` is untouched, so the exit charges
2899                    // exactly what the unknown-id refusal above charges for the same request.
2900                    return Ok(ClientAuthVerdict::Refused(ClientAuthFailure::SecretExpired));
2901                }
2902            }
2903        }
2904
2905        // RFC 7523 client authentication, when the request presented an assertion. Handled apart
2906        // from the secret comparison below because it is a different KIND of credential: there is
2907        // nothing to compare, there is a signature to verify against the REGISTRATION's key and a
2908        // `jti` to spend so the request cannot be repeated.
2909        #[cfg(feature = "client-assertion")]
2910        if cred.client_assertion.is_some() {
2911            // NOT boxed, and that is a measurement rather than a style, in both directions.
2912            //
2913            // It WAS `Box::pin(..)` through 0.9.0: `authenticate_client` inlines into all four
2914            // grant helpers and so into the token future, which `tests/allocation.rs` holds under
2915            // tokio's 2048-byte debug boxing threshold, and inlining the assertion state (a claim
2916            // set, two owned Strings, a storage future) once pushed it over. It no longer does.
2917            // Measured both ways: 1144 under `client-assertion` alone, 1256 with `rar`, 1344 with
2918            // `dpop,mtls,consent,rar,par` and with `--all-features`, IDENTICAL boxed and unboxed,
2919            // because the token future's high-water mark moved elsewhere when the endpoint was
2920            // restructured (see `token_with_context`).
2921            //
2922            // What the box was still costing is one allocation on every token request that
2923            // presents an assertion, which for a `private_key_jwt` deployment is every token
2924            // request it makes: precisely the deployments RFC 7523 exists for, and the ones FAPI
2925            // 2.0 requires it of. `client_assertion_verification_bound` in
2926            // `tests/allocation_paths.rs` pins the result.
2927            let outcome = self.authenticate_by_assertion(&client, cred, paid).await;
2928            return Ok(match outcome {
2929                Ok(()) => ClientAuthVerdict::Authenticated(client),
2930                // The reason is carried to the audit channel by the single exit. The wire answer is
2931                // one bare `invalid_client` for every one of them, which is why there is nothing
2932                // here to choose between.
2933                Err(reason) => {
2934                    ClientAuthVerdict::Refused(ClientAuthFailure::AssertionInvalid { reason })
2935                }
2936            });
2937        }
2938
2939        // RFC 8705 s2 mutual-TLS client authentication, handled apart from the secret
2940        // comparison below for the same reason the assertion above is: it is a different
2941        // KIND of credential. There is nothing to compare; there is a certificate the HOST
2942        // verified, matched against what the registration says it expects to see.
2943        //
2944        // Dispatched on the REGISTRATION, never on what the request happened to present.
2945        // That direction is load bearing in both senses: a certificate presented by a
2946        // secret-authenticating client never reaches this path (it is for section 3 binding
2947        // only), and a mutual-TLS client can never fall through to the secret comparison
2948        // below.
2949        #[cfg(feature = "mtls")]
2950        if matches!(client.auth, crate::client::ClientAuth::Mtls { .. }) {
2951            return Ok(match crate::mtls::verify_certificate(&client, cred) {
2952                Ok(()) => ClientAuthVerdict::Authenticated(client),
2953                // A thumbprint comparison is microseconds, so an mTLS registration handed a
2954                // posted `client_secret` refuses far faster than an unknown id pays
2955                // `dummy_verify`. `paid` is untouched — no secret was verified — so the exit
2956                // charges the dummy, and it does nothing at all for the `None` a real mutual-TLS
2957                // request carries, which leaves the legitimate path unchanged.
2958                Err(failure) => ClientAuthVerdict::Refused(failure),
2959            });
2960        }
2961
2962        // WHICH REGISTRATIONS ACTUALLY VERIFY A SECRET, recorded BEFORE the call so the exit knows
2963        // what it still owes. `verify_with` answers `false` in nanoseconds for `Public` and for
2964        // `ConfidentialAssertion` (see the arms in `crate::client`: there is no presented string
2965        // that could be right for either), so a request posting junk as `client_secret` separated
2966        // those registrations from an unknown id by wall time alone — and the unknown id was the
2967        // SLOW one, argon2id milliseconds against nanoseconds under a host scheme. The two kinds
2968        // that DO verify are marked paid, so they are never charged twice.
2969        paid.secret = matches!(
2970            client.auth,
2971            crate::client::ClientAuth::ConfidentialSecret { .. }
2972                | crate::client::ClientAuth::ConfidentialSecretHash { .. }
2973        );
2974        // `verify_with` rather than `verify`: a registration stored as a hash in a scheme this
2975        // crate does not implement is decided by the host's verifier (see
2976        // `crate::client::SecretVerifier`), and by nobody at all when none is installed.
2977        if !client
2978            .auth
2979            .verify_with(cred.client_secret, self.hooks.secret_verifier())
2980        {
2981            return Ok(ClientAuthVerdict::Refused(
2982                ClientAuthFailure::SecretMismatch,
2983            ));
2984        }
2985        Ok(ClientAuthVerdict::Authenticated(client))
2986    }
2987
2988    /// RFC 7523 section 3, plus the single-use claim that makes it worth anything.
2989    ///
2990    /// Returns `Ok(())` for an authenticated client. Every refusal is the SAME bare
2991    /// `invalid_client` the wrong-secret path returns, with no description: this function is only
2992    /// reached once the client id is known to exist, so a description naming which check failed
2993    /// would be the difference between "this client id is real" and "it is not", which is exactly
2994    /// the distinction `authenticate_client` collapses on purpose. The host's audit channel is
2995    /// told (`ClientAuthFailure::AssertionInvalid { reason }`); the wire is not.
2996    ///
2997    /// RETURNS THE REASON rather than a built `ErrorResponse`, which is what makes that sentence
2998    /// true. Every refusal here is byte for byte the same bare `invalid_client`, so there was
2999    /// nothing for the caller to choose between and the `ErrorResponse` was constructed here only
3000    /// to be discarded by a `map_err(|_| ..)` that also discarded the `AssertionFailure` with it.
3001    /// The caller builds the one response and emits the reason, so the audit channel gets what
3002    /// `AssertionFailure` documents itself as existing for.
3003    #[cfg(feature = "client-assertion")]
3004    async fn authenticate_by_assertion(
3005        &self,
3006        client: &Client,
3007        cred: &ClientCredential<'_>,
3008        paid: &mut CredentialCost,
3009    ) -> Result<(), crate::client_assertion::AssertionFailure> {
3010        use crate::client_assertion::AssertionFailure;
3011        // THE THREE REFUSALS THAT PRECEDE ANY DECODING ARE ONE EXPRESSION, and it is the SAME
3012        // expression `authenticate_client` uses to decide whether to charge the assertion dummy on
3013        // the unknown-id path. They were three separate `if`s here and a predicate there, kept in
3014        // step by a comment, and a comment cannot hold an invariant across two functions.
3015        //
3016        // WHAT THAT COSTS WHEN IT DRIFTS — and the history matters, because 0.9.2 changed WHERE
3017        // this is paid and an auditor should not read the danger below as a hole 0.9.1 shipped.
3018        // `authenticate_client` enters this function on `cred.client_assertion.is_some()` ALONE,
3019        // so a request carrying an assertion AND a `client_secret` arrives here and is refused by
3020        // the RFC 6749 section 2.3 check below in nanoseconds. The UNKNOWN id sending identical
3021        // bytes takes the not-found arm, which charges `dummy_verify(cred.client_secret)`
3022        // unconditionally, and with a secret present that runs the host's `SecretVerifier` scheme:
3023        // argon2id milliseconds against nanoseconds. The assertion dummy balances nothing there,
3024        // because `assertion_could_be_verified` is false when a secret is present. One request per
3025        // candidate id, never repeated, sorts registered ids from unregistered ones, which is the
3026        // enumeration the whole mechanism exists to close.
3027        //
3028        // THAT STATE EXISTED DURING 0.9.1'S AUDIT ROUNDS AND WAS NOT RELEASED IN ONE. Released
3029        // 0.9.1 closed it HERE, with a `self.dummy_verify(cred.client_secret)` immediately before
3030        // the `Malformed` return below, so the known and unknown ids paid the same call for the
3031        // same request. (0.9.0 is a different shape again: it had no dummy verification anywhere,
3032        // so there was no asymmetry of this kind to have — the whole balancing mechanism arrived
3033        // in 0.9.1.) What 0.9.2 changed is that the charge is no longer made at this site, or at
3034        // the second site further down: see below.
3035        //
3036        // The three conditions, and why each refuses before anything is read:
3037        //
3038        // - An assertion has to be present at all.
3039        // - RFC 7521 section 4.2: the type is what says which assertion format this is, and this
3040        //   server implements exactly one. An absent or unrecognised type is refused rather than
3041        //   assumed, because assuming would mean verifying a credential in a format nobody
3042        //   declared.
3043        // - RFC 6749 section 2.3: "The client MUST NOT use more than one authentication method in
3044        //   each request." A request carrying both a secret and an assertion has not said which
3045        //   credential it means, and a server that picks one behaves differently from the next
3046        //   server, which is exactly the ambiguity an intermediary would exploit.
3047        //
3048        // All three answered `AssertionFailure::Malformed` before and all three answer it now:
3049        // there is no more specific variant for "this server will not read the string it was
3050        // handed", and the wire answer was one bare `invalid_client` for every one of them anyway.
3051        // So collapsing them changes what is SPENT and nothing else, on the wire or in the audit
3052        // channel.
3053        //
3054        // NOTHING IS CHARGED HERE ANY MORE, by either half of this function: `paid` is untouched on
3055        // every refusal below that did no verification, and the single exit in
3056        // `authenticate_client` spends what such a request would have spent. That is the 0.9.2
3057        // change, and it is a change of PLACE rather than of amount: 0.9.1 made two charges from
3058        // inside this function — a `dummy_verify` at the `Malformed` return just below, and a
3059        // `dummy_assertion_verify` in the `WrongPrincipal` arm after it — and each got its own
3060        // case right while neither could see what the other, or `authenticate_client`, had already
3061        // spent. `CredentialCost` is what a single exit needs in order to know.
3062        if !Self::assertion_could_be_verified(cred) {
3063            return Err(AssertionFailure::Malformed);
3064        }
3065        let assertion = cred.client_assertion.ok_or(AssertionFailure::Malformed)?;
3066
3067        // THE REGISTRATION DECIDES, and this is where that starts. A client registered for
3068        // `client_secret_basic` cannot promote itself to `private_key_jwt` by sending an assertion,
3069        // because there is no key here that anybody vouched for on its behalf.
3070        let keys = match &client.auth {
3071            crate::client::ClientAuth::ConfidentialAssertion { keys } => keys,
3072            // The registration does not authenticate this way at all, so there is no key any
3073            // assertion could have been signed with. `WrongPrincipal` is the closest true
3074            // statement: the party this credential claims to be is not the party it names.
3075            // NOTHING WAS VERIFIED, so `paid.assertion` stays false and the exit charges
3076            // `dummy_assertion_verify` for it. A registered id that does not use assertions
3077            // answering in nanoseconds, while an unknown id sending identical bytes paid a full
3078            // verification, is the registration KIND readable off the clock — and the kind is
3079            // exactly what such a probe is after, so the known path has to pay too.
3080            _ => return Err(AssertionFailure::WrongPrincipal),
3081        };
3082
3083        // RFC 7523 section 3 (3) admits either the token endpoint URL or, by long-established
3084        // practice (OpenID Connect Core section 9), the issuer identifier.
3085        //
3086        // The verifier is resolved and PASSED ALONG rather than required here, because only one of
3087        // the two methods needs one. `private_key_jwt` is ES256 and `verify_assertion` refuses it
3088        // on a `None` (an unchecked credential has authenticated nobody). `client_secret_jwt` is
3089        // an HS256 HMAC over the registered secret and touches no curve at all, so requiring a
3090        // backend on that path refused a valid credential for a reason no RFC gives. Which one
3091        // this registration is, is `client.auth`'s to say, and `AssertionKeys` is what says it.
3092        // CHARGED AS PAID AT THE CALL, not at its result. Every outcome from here on is one the
3093        // unknown-id path prices with a single `dummy_assertion_verify`, so charging a second one on
3094        // top would make a KNOWN id the slower of the two — which is how round 8 broke round 7. The
3095        // one gap this leaves is the refusals `verify_assertion` makes before it reaches a
3096        // signature; see the FOURTH residual on `authenticate_client`, and `CredentialCost`.
3097        paid.assertion = true;
3098        let verified = verify_assertion(
3099            self.es256_verifier(),
3100            keys,
3101            assertion,
3102            client.client_id.as_str(),
3103            &[self.token_endpoint(), self.issuer_identifier()],
3104            self.clock.now(),
3105        )?;
3106
3107        // RFC 7523 section 3: the `jti` is single use within the assertion's validity. THIS is the
3108        // check that makes an observed request unrepeatable, and it is the whole difference between
3109        // an authentication mechanism and a bearer credential that happens to be signed. It is
3110        // namespaced by client id so that two clients choosing the same `jti` (a counter, a
3111        // timestamp) cannot lock each other out.
3112        let claimed = self
3113            .store
3114            .claim_replay_id(
3115                &replay_key("ca", client.client_id.as_str(), &verified.jti),
3116                verified.expires_at,
3117            )
3118            .await
3119            // FAILING CLOSED. A claim that could not be recorded is a claim that did not happen,
3120            // and treating a storage outage as "probably fine" would turn every assertion into a
3121            // replayable one for the duration of the outage.
3122            //
3123            // REPORTED AS ITS OWN REASON, and not as `Replayed`, which is what it was through
3124            // 0.9.0. The wire answer is identical either way (`invalid_client`), so this is
3125            // entirely a question about the audit channel, and there the two are opposites:
3126            // `Replayed` is documented as a captured-and-replayed assertion, which
3127            // `crate::events` calls "a different incident and a much worse one", while this is the
3128            // store being unreachable. An outage fails EVERY `private_key_jwt` client at once, so
3129            // the mislabel fired a burst of this crate's worst-incident signal at the exact moment
3130            // an operator was reading the channel to find out what had broken. The DPoP twin below
3131            // propagates the same failure with `map_err(storage_error)`.
3132            .map_err(|_| AssertionFailure::ReplayCheckUnavailable)?;
3133        if !claimed {
3134            return Err(AssertionFailure::Replayed);
3135        }
3136        Ok(())
3137    }
3138
3139    /// The token endpoint URL this server answers on, which is what RFC 7523 section 3 (3) and RFC
3140    /// 9449 section 4.3 (7) compare against.
3141    ///
3142    /// Derived the same way `AuthorizationServerMetadata::from_config` derives it, and it MUST stay
3143    /// that way: the document tells a client where to send its request and what to put in `aud` and
3144    /// `htu`, so a server whose own idea of its token endpoint differs from the one it published
3145    /// refuses every conforming client.
3146    ///
3147    /// PRECOMPUTED at construction (see [`AuthorizationServer::with_clock`]) and borrowed here.
3148    /// The value is fixed for the life of the server, and this is called once per RFC 9449 proof
3149    /// verification and once per RFC 7523 assertion verification, so a `private_key_jwt` client
3150    /// sending DPoP paid two `format!`s of a constant on every token request. The crate already
3151    /// precomputes the metadata document, the JWKS and the JOSE header for exactly this reason.
3152    #[cfg(any(feature = "client-assertion", feature = "dpop"))]
3153    fn token_endpoint(&self) -> &str {
3154        &self.token_endpoint
3155    }
3156
3157    /// RFC 9449 section 4.3, and the single-use claim on the proof's `jti`.
3158    ///
3159    /// The proof is checked BEFORE the grant is looked at, because it binds to the REQUEST rather
3160    /// than to the grant: a proof that does not verify means this request is refused whatever it
3161    /// asked for, and spending its `jti` here means a replayed proof costs the attacker a lookup
3162    /// and gains them nothing.
3163    ///
3164    /// `htm` is `POST` because RFC 6749 section 3.2 makes the token endpoint POST-only, and `htu`
3165    /// is this server's own token endpoint rather than something the host passes in. That is
3166    /// deliberate: the value a conforming client puts in `htu` is the one it read from the RFC 8414
3167    /// document, which is exactly what `token_endpoint` returns, so taking it from the host would
3168    /// add a seam whose only possible use is to get it wrong.
3169    #[cfg(feature = "dpop")]
3170    async fn verify_dpop(&self, proof: Option<&str>) -> Result<Option<Box<str>>, ErrorResponse> {
3171        let proof = match proof {
3172            Some(proof) => proof,
3173            None if self.config.require_dpop => {
3174                return Err(ErrorResponse::new(ErrorCode::InvalidDpopProof)
3175                    .with_description("this server requires a DPoP proof on every token request"))
3176            }
3177            None => return Ok(None),
3178        };
3179        // Resolved BEFORE the proof is parsed: with no backend there is nothing that could make
3180        // this proof acceptable, so an unauthenticated caller does not get to spend a base64 decode
3181        // and a JSON parse finding that out.
3182        let verifier = self.es256_verifier().ok_or_else(|| {
3183            // EMITTED, and this is the refusal it matters most to emit. `jwt` carries the verifier
3184            // seam and `jwt-p256` carries the arithmetic, so a build with `dpop` and neither an
3185            // installed verifier nor that backend refuses EVERY proof: the deployment is
3186            // misconfigured, not the client, and through 0.9.0 this was the one refusal the audit
3187            // channel never heard about at all. `UnsupportedAlgorithm` is the honest reading of
3188            // `DpopFailure`'s existing vocabulary — with no backend, ES256 is not an algorithm
3189            // this build accepts, whatever `dpop_signing_alg_values_supported` advertises.
3190            self.hooks.emit(|| Event::DpopProofRefused {
3191                failure: crate::dpop::DpopFailure::UnsupportedAlgorithm,
3192            });
3193            // BARE, like the nine section 4.3 checks below. The event above carries the reason,
3194            // which is where `dpop.rs` says the distinction belongs; the description would have
3195            // put it on the wire, and `verify_dpop` runs BEFORE any client authentication, so an
3196            // anonymous caller could read a deployment misconfiguration off a refusal.
3197            ErrorResponse::new(ErrorCode::InvalidDpopProof)
3198        })?;
3199        let verified = verify_proof(
3200            verifier,
3201            proof,
3202            "POST",
3203            self.token_endpoint(),
3204            self.clock.now(),
3205        )
3206        // THE REASON GOES TO THE AUDIT CHANNEL, and until 0.9.1 it went nowhere: this arm was
3207        // `map_err(|_| ..)` and no event was emitted at all, against `dpop.rs`'s statement that
3208        // "the distinction here is for the host's audit channel, not for the wire". A deployment
3209        // could therefore not tell a client with a skewed clock (`StaleProof`) from one whose
3210        // proofs are being captured and replayed (`Replayed`), which are a configuration problem
3211        // and an incident.
3212        .map_err(|failure| {
3213            self.hooks.emit(|| Event::DpopProofRefused { failure });
3214            ErrorResponse::new(ErrorCode::InvalidDpopProof)
3215        })?;
3216        // Namespaced by THUMBPRINT rather than by client id: a proof is bound to a key, not to a
3217        // registration (a public client's proof arrives before anything has authenticated), so the
3218        // key is the only identity available at this point that an attacker cannot choose freely.
3219        let claimed = self
3220            .store
3221            .claim_replay_id(
3222                &replay_key("dpop", &verified.jkt, &verified.jti),
3223                verified.replay_until,
3224            )
3225            .await
3226            .map_err(storage_error)?;
3227        if !claimed {
3228            // The single-use check is one of RFC 9449 section 4.3's, so its failure is reported
3229            // through the same channel as the nine `verify_proof` performs. It was the one
3230            // `DpopFailure::Replayed` nothing ever produced.
3231            self.hooks.emit(|| Event::DpopProofRefused {
3232                failure: crate::dpop::DpopFailure::Replayed,
3233            });
3234            // BARE, for the reason the no-verifier arm above gives and one more of its own. This
3235            // path is reached without authentication, and "already used" told an anonymous caller
3236            // that the proof they presented is in the replay cache, which confirms that its
3237            // signature, `htu`, `htm` and `iat` all PASSED: a captured proof could be tested for
3238            // freshness against the server that would otherwise have accepted it. The event
3239            // carries `DpopFailure::Replayed` to the host, which is the whole distinction.
3240            return Err(ErrorResponse::new(ErrorCode::InvalidDpopProof));
3241        }
3242        Ok(Some(verified.jkt.into_boxed_str()))
3243    }
3244
3245    /// The registered default, TRIMMED to what the registration also says may ever be granted.
3246    ///
3247    /// The trim is a fix for a defect the 0.9.1 rotation ceiling created rather than a
3248    /// belt-and-braces check. Nothing had ever compared `default_scopes` with `allowed_scopes`:
3249    /// both are plain public fields on [`crate::client::Client`], and a registration can reach a
3250    /// host's store without passing through [`AuthorizationServer::register_client`] at all. So a
3251    /// registration whose default exceeded its own allowance granted the default, and then
3252    /// [`AuthorizationServer::refresh_token`]'s ceiling refused the first rotation with
3253    /// `invalid_scope` and did NOT put the record back, destroying the chain permanently. That
3254    /// ceiling's premise is that "the client asked to continue a grant this server is no longer
3255    /// willing to honour", and the premise is false here: an identical fresh authorization request
3256    /// naming no scope mints the same grant again. Trimming is the reading that makes the two
3257    /// halves agree, and it is the fail-closed one: what is granted is exactly what the
3258    /// registration says may ever be granted.
3259    ///
3260    /// Written as a guarded clone so the OVERWHELMINGLY common case is byte for byte what this
3261    /// always did. THE SECOND ARM IS REACHABLE AND IS NOT DEAD CODE: `register_client` does NOT
3262    /// refuse a registration whose `default_scopes` exceed its `allowed_scopes`, deliberately, and
3263    /// says why in its own body -- narrowing `allowed_scopes` alone is exactly how an operator
3264    /// corrects an over-broad registration, and refusing that would turn a security control into
3265    /// an error message. This trim is what makes that safe, so deleting it as unreachable
3266    /// reintroduces the defect the paragraph above describes.
3267    ///
3268    /// Called from BOTH places the RFC 6749 section 3.3 default is applied, which is the other
3269    /// half of the fix: the authorization endpoint had its own copy of "absent means the registered
3270    /// default", and a rule with two implementations is a rule that drifts.
3271    fn granted_default_scope(client: &Client) -> ScopeSet {
3272        if client.default_scopes.is_subset(&client.allowed_scopes) {
3273            return client.default_scopes.clone();
3274        }
3275        ScopeSet::from_tokens(
3276            client
3277                .default_scopes
3278                .iter()
3279                .filter(|s| client.allowed_scopes.contains(s.as_str()))
3280                .map(|s| s.as_str()),
3281        )
3282        // Unreachable: every token here came out of a `ScopeSet` and so parsed once already.
3283        .unwrap_or_else(|_| ScopeSet::empty())
3284    }
3285
3286    /// Resolve the scope a request will be granted: the client default when the request names
3287    /// none, otherwise the request, which must sit inside the registration's allowed set.
3288    fn resolve_scope(
3289        client: &Client,
3290        requested: Option<&ScopeSet>,
3291    ) -> Result<ScopeSet, ErrorResponse> {
3292        match requested {
3293            // INTERSECTED, not taken whole, and that is a fix for a defect the 0.9.1 rotation
3294            // ceiling created rather than a belt-and-braces check. Nothing had ever compared
3295            // `default_scopes` with `allowed_scopes`: both are plain public fields on
3296            // `crate::client::Client`, and a registration can reach a host's store without passing
3297            // through `register_client` at all. So a registration whose default exceeded its own
3298            // allowance granted the default, and then `refresh_token`'s ceiling refused the first
3299            // rotation with `invalid_scope` and did NOT put the record back, destroying a chain
3300            // permanently. That ceiling's premise is that "the client asked to continue a grant
3301            // this server is no longer willing to honour", and that premise is false here: an
3302            // identical fresh authorization request naming no scope mints the same grant again.
3303            // Intersecting is the only reading that makes the two halves agree, and it is the
3304            // fail-closed one: what is granted is exactly what the registration says may ever be
3305            // granted. `register_client` does NOT refuse the disagreement -- see its body for why
3306            // not, and `granted_default_scope` for why this trim is therefore load bearing rather
3307            // than defensive.
3308            None => Ok(Self::granted_default_scope(client)),
3309            Some(s) if s.is_subset(&client.allowed_scopes) => Ok(s.clone()),
3310            // NB: descriptions must stay inside the RFC 6749 section 5.2 charset (no double
3311            // quote, no backslash), which scope tokens themselves already satisfy.
3312            // BORROWED, not built. This refusal is reachable by an UNAUTHENTICATED caller at the
3313            // authorization endpoint, at whatever rate they choose, and the `format!` this replaced
3314            // allocated a String and echoed the caller's own scope string into it. `tests/
3315            // allocation.rs` states the rule on `refused_token_request_allocation_bound`: a refusal
3316            // is work the attacker buys, so it does not get to buy a heap allocation. Roughly fifty
3317            // other refusal sites in this crate already hand back a `&'static str`; this one was
3318            // the exception. The developer who sent it still learns which parameter was wrong,
3319            // which is all RFC 6749 section 5.2 asks of `error_description`, and not echoing the
3320            // value back keeps it out of the host's logs as well.
3321            Some(_) => Err(ErrorResponse::new(ErrorCode::InvalidScope)
3322                .with_description("requested scope exceeds the client registration")),
3323        }
3324    }
3325
3326    /// RFC 8628 section 3.1/3.2: start a device authorization.
3327    pub async fn device_authorization(
3328        &self,
3329        client_id: &ClientId,
3330        client_secret: Option<&str>,
3331        requested_scope: Option<&ScopeSet>,
3332    ) -> Result<DeviceAuthorizationResponse, ErrorResponse> {
3333        self.device_authorization_with_credential(
3334            client_id,
3335            &ClientCredential::secret(client_secret),
3336            requested_scope,
3337        )
3338        .await
3339    }
3340
3341    /// RFC 8628 section 3.1/3.2 for a client authenticating with any credential this server
3342    /// accepts, including an RFC 7523 assertion.
3343    ///
3344    /// Added ALONGSIDE [`AuthorizationServer::device_authorization`] rather than replacing it: the
3345    /// three-argument form is what every existing host already calls and a shared secret remains
3346    /// the commonest credential. Both go through the same `authenticate_client`, so there is one
3347    /// authentication path and not two.
3348    pub async fn device_authorization_with_credential(
3349        &self,
3350        client_id: &ClientId,
3351        cred: &ClientCredential<'_>,
3352        requested_scope: Option<&ScopeSet>,
3353    ) -> Result<DeviceAuthorizationResponse, ErrorResponse> {
3354        // STAMPED BEFORE THE REGISTRATION IS READ, for the reason `client_credentials_token`
3355        // states at length: `created_at` is the instant a revocation barrier compares this grant
3356        // against at redemption, so it must predate the read the write is derived from. Taking it
3357        // after `authenticate_client` would date the grant later than a `delete_client` landing in
3358        // that window, and the comparison would then admit a token for a registration deleted
3359        // before the grant was even written.
3360        //
3361        // The device flow makes that worse than elsewhere, which is why it is worth the extra
3362        // line: `put_device_grant` consults no barrier at all, the approval that follows is a
3363        // compare-and-swap against a record that is present, and a device client is typically
3364        // PUBLIC — so a host re-provisioning the same `client_id` needs no secret for the polling
3365        // device to redeem it.
3366        let created_at = self.clock.now();
3367
3368        let client = self.authenticate_client(client_id, cred).await?;
3369        if !client.allows_grant(GrantType::DeviceCode) {
3370            return Err(ErrorResponse::new(ErrorCode::UnauthorizedClient)
3371                .with_description("client registration does not include the device_code grant"));
3372        }
3373        let scope = Self::resolve_scope(&client, requested_scope)?;
3374
3375        let now = self.clock.now();
3376        // `?` rather than a panic: this is an unauthenticated-shaped request path like any
3377        // other, and an OS that will not hand over 32 bytes is a `server_error`, not a reason to
3378        // abort the host's process. See `try_random_hex`.
3379        let device_code = try_random_hex(32).ok_or_else(randomness_error)?;
3380        let user_code = self.unique_user_code().await?;
3381        let grant = DeviceGrant {
3382            device_code: device_code.clone(),
3383            user_code: user_code.clone(),
3384            client_id: client.client_id.clone(),
3385            scope,
3386            state: DeviceGrantState::Pending,
3387            // Read at request ENTRY, above, not here. `expires_at` below stays measured from the
3388            // write, because the TTL is a promise about how long the user has to type the code.
3389            created_at,
3390            expires_at: saturating_deadline(now, self.config.device_code_ttl),
3391            interval: self.config.poll_interval,
3392            last_poll_at: None,
3393        };
3394        self.store
3395            .put_device_grant(grant)
3396            .await
3397            .map_err(storage_error)?;
3398
3399        let verification_uri_complete = self.config.include_verification_uri_complete.then(|| {
3400            // RFC 8628 s3.3.1: this is a DEEP LINK whose only job is to prefill the code, so
3401            // a `?` appended to a `verification_uri` that already carries a query does not
3402            // merely look wrong — it folds the code into the previous parameter's value and
3403            // the page prefills nothing. The same helper every authorization-response URL in
3404            // this crate uses, rather than a second answer to the same question.
3405            format!(
3406                "{}{}user_code={}",
3407                self.config.verification_uri,
3408                crate::authorization::query_separator(&self.config.verification_uri),
3409                user_code
3410            )
3411        });
3412        Ok(DeviceAuthorizationResponse {
3413            device_code,
3414            user_code,
3415            verification_uri: self.config.verification_uri.clone(),
3416            verification_uri_complete,
3417            expires_in: self.config.device_code_ttl.as_secs(),
3418            interval: self.config.poll_interval.as_secs(),
3419        })
3420    }
3421
3422    /// Draw a user code that no live grant already answers to.
3423    ///
3424    /// RFC 8628 section 6.1 sizes the user code for a human to type, which is exactly why it is
3425    /// short enough to collide: the birthday bound at the floor length is in the low hundreds of
3426    /// thousands of concurrent live grants. An accepted collision is not a cosmetic problem, it is
3427    /// two devices sharing one credential, and it corrupts the store's index for both.
3428    ///
3429    /// The draw is checked, not assumed. The check is advisory (another grant can be written
3430    /// between the lookup and the put), which is why [`Storage::put_device_grant`] is REQUIRED to
3431    /// refuse a collision outright: this loop keeps the common case cheap, the store keeps it
3432    /// correct.
3433    async fn unique_user_code(&self) -> Result<String, ErrorResponse> {
3434        // Clamped, not honoured: see `ServerConfig::user_code_length`.
3435        let len = self.config.user_code_length.max(MIN_USER_CODE_LENGTH);
3436        for _ in 0..USER_CODE_GENERATION_ATTEMPTS {
3437            // `None` is the OS refusing randomness, which is the same `server_error` the
3438            // storage failures in this loop become rather than a panic on a request path.
3439            let raw = random_user_code(len).ok_or_else(randomness_error)?;
3440            // The store indexes NORMALIZED codes, and `raw` is already the normalized form (the
3441            // alphabet is upper case and carries no hyphen), so this needs no second pass.
3442            if self
3443                .store
3444                .find_device_grant_by_user_code(&raw)
3445                .await
3446                .map_err(storage_error)?
3447                .is_none()
3448            {
3449                return Ok(display_user_code(&raw));
3450            }
3451        }
3452        Err(ErrorResponse::new(ErrorCode::ServerError)
3453            .with_description("could not allocate an unused user code"))
3454    }
3455
3456    /// Fetch a still-live pending grant by entered user code, for the verification UI actions,
3457    /// with the RFC 8628 section 5.1 throttle around it.
3458    ///
3459    /// `check` runs BEFORE the lookup, so a refused attempt learns nothing, and the outcome is
3460    /// reported back afterwards because a guessing attack is visible in FAILURES, not in traffic.
3461    /// Every rejection this can produce (unknown code, expired, already used) is counted the same
3462    /// way: an attacker enumerating codes does not care which one they get.
3463    async fn pending_grant_by_user_code(
3464        &self,
3465        entered_user_code: &str,
3466    ) -> Result<DeviceGrant, DeviceApprovalError> {
3467        let attempt = Attempt::DeviceUserCodeEntry;
3468        if self.hooks.check(attempt) == RateLimitDecision::Deny {
3469            return Err(DeviceApprovalError::RateLimited);
3470        }
3471        let outcome = self
3472            .lookup_pending_grant_by_user_code(entered_user_code)
3473            .await;
3474        self.hooks.record(
3475            attempt,
3476            if outcome.is_ok() {
3477                AttemptOutcome::Succeeded
3478            } else {
3479                AttemptOutcome::Failed
3480            },
3481        );
3482        outcome
3483    }
3484
3485    /// The lookup itself, split out so the throttle above wraps every exit from it.
3486    async fn lookup_pending_grant_by_user_code(
3487        &self,
3488        entered_user_code: &str,
3489    ) -> Result<DeviceGrant, DeviceApprovalError> {
3490        let normalized = normalize_user_code(entered_user_code);
3491        let grant = self
3492            .store
3493            .find_device_grant_by_user_code(&normalized)
3494            .await
3495            .map_err(DeviceApprovalError::Storage)?
3496            .ok_or(DeviceApprovalError::UnknownUserCode)?;
3497        if self.clock.now() >= grant.expires_at {
3498            // Expired: remove it so the user-facing answer and the poll path agree the code is
3499            // gone; the device's next poll will already find nothing (invalid_grant), which is
3500            // indistinguishable from a spent code and fine either way.
3501            let _ = self.store.take_device_grant(&grant.device_code).await;
3502            return Err(DeviceApprovalError::Expired);
3503        }
3504        if grant.state != DeviceGrantState::Pending {
3505            return Err(DeviceApprovalError::NotPending);
3506        }
3507        Ok(grant)
3508    }
3509
3510    /// The host's verification UI approves a grant for `subject` (the authenticated user).
3511    ///
3512    /// # The host MUST rate limit calls to this
3513    ///
3514    /// RFC 8628 section 5.1 is explicit that the user code's entropy is sufficient only IN
3515    /// COMBINATION WITH rate limiting: the code is short because a human types it, and an
3516    /// unthrottled verification endpoint turns "short enough to type" into "short enough to
3517    /// enumerate".
3518    ///
3519    /// This crate CAN throttle this call, and does: the first thing this method does is
3520    /// `pending_grant_by_user_code`, which asks the installed [`RateLimiter`] about an
3521    /// [`crate::events::Attempt::DeviceUserCodeEntry`] BEFORE the code is looked up, and reports
3522    /// the outcome back afterwards. [`DeviceApprovalError::RateLimited`] is what a refusal looks
3523    /// like from here. A limiter is shipped ([`crate::rate_limit::FixedWindowRateLimiter`]), and
3524    /// the crate's own `http` service installs nothing by default, so a host that installs none
3525    /// gets none. This paragraph said the opposite through 0.9.1 — "performs NO rate limiting and
3526    /// cannot" — which was a promise of absence beside code that consults the throttle on its
3527    /// first statement, and is the kind of doc that gets a host to build a second throttle or,
3528    /// worse, to conclude the risk is unavoidable.
3529    ///
3530    /// What remains the HOST's, and it is the important half: this crate has no notion of a
3531    /// caller, an IP, a session or a user, so it can only key the throttle on what it is given.
3532    /// An attacker who spreads guesses across the whole code space from many sources is visible
3533    /// to the host and not to this crate. Without a limiter installed, [`MIN_USER_CODE_LENGTH`]
3534    /// symbols is a guessing exercise, not a credential.
3535    pub async fn approve_device(
3536        &self,
3537        entered_user_code: &str,
3538        subject: impl Into<String>,
3539    ) -> Result<(), DeviceApprovalError> {
3540        let mut grant = self.pending_grant_by_user_code(entered_user_code).await?;
3541        let subject = subject.into();
3542        // Cloned ONLY when a sink is installed: `grant` is consumed by the put below, so the
3543        // event's fields have to be captured first, and an unobserved host must not pay for that.
3544        let audit = self
3545            .hooks
3546            .is_observed()
3547            .then(|| (grant.client_id.clone(), subject.clone()));
3548        grant.state = DeviceGrantState::Approved { subject };
3549        // COMPARE-AND-SWAP against `Pending`, never a blind put. The read above and this write are
3550        // separated by however long the host's store takes, and this is not the only writer: a
3551        // DENIAL from a second verification-UI action, or a poll, can land in between. A blind put
3552        // resolves that by whoever writes last, which for two decisions on one user code is
3553        // arbitrary, and the arbitrary direction that matters is an approval overwriting a refusal
3554        // the user already gave. FIRST DECISION WINS instead.
3555        //
3556        // `NotPending` rather than a new variant: it is the answer this call would have given had
3557        // it arrived a moment later and read the decided grant, so the host's verification UI needs
3558        // no new case to handle a race it could already reach by being slow.
3559        if !self
3560            .store
3561            .compare_and_swap_device_grant(&DeviceGrantState::Pending, grant)
3562            .await
3563            .map_err(DeviceApprovalError::Storage)?
3564        {
3565            return Err(DeviceApprovalError::NotPending);
3566        }
3567        // Emitted AFTER the write: an approval that failed to persist did not happen, and an audit
3568        // log that says otherwise is worse than none.
3569        if let Some((client_id, subject)) = &audit {
3570            self.hooks.emit(|| Event::DeviceGrantApproved {
3571                client_id: client_id.as_str(),
3572                subject,
3573            });
3574        }
3575        Ok(())
3576    }
3577
3578    /// The host's verification UI records the user's refusal.
3579    ///
3580    /// The same RFC 8628 section 5.1 obligation as [`AuthorizationServer::approve_device`] applies:
3581    /// this path also tells a caller whether a code exists, so the HOST must rate limit it too. An
3582    /// attacker enumerating codes does not care which of the two endpoints answers.
3583    pub async fn deny_device(&self, entered_user_code: &str) -> Result<(), DeviceApprovalError> {
3584        let mut grant = self.pending_grant_by_user_code(entered_user_code).await?;
3585        let audit = self.hooks.is_observed().then(|| grant.client_id.clone());
3586        grant.state = DeviceGrantState::Denied;
3587        // Same compare-and-swap, same reason: see `approve_device`. First decision wins in both
3588        // directions, so a refusal cannot overwrite an approval either.
3589        if !self
3590            .store
3591            .compare_and_swap_device_grant(&DeviceGrantState::Pending, grant)
3592            .await
3593            .map_err(DeviceApprovalError::Storage)?
3594        {
3595            return Err(DeviceApprovalError::NotPending);
3596        }
3597        if let Some(client_id) = &audit {
3598            self.hooks.emit(|| Event::DeviceGrantDenied {
3599                client_id: client_id.as_str(),
3600            });
3601        }
3602        Ok(())
3603    }
3604
3605    /// The token endpoint (RFC 6749 section 3.2; device grant per RFC 8628 section 3.4/3.5), for a
3606    /// request that names no RFC 8707 resource indicator.
3607    ///
3608    /// Equivalent to [`AuthorizationServer::token_with_resources`] with an empty list, which is
3609    /// what a token request carrying no `resource` parameter means: no NARROWING is asked for, so
3610    /// the issued token inherits whatever the grant already carries.
3611    ///
3612    /// # THIS FUTURE IS NOT CANCELLATION SAFE, and what a drop costs
3613    ///
3614    /// Applies equally to [`AuthorizationServer::token_with_resources`] and
3615    /// [`AuthorizationServer::token_with_context`], which are the same future.
3616    ///
3617    /// A Rust future stops at whatever `await` it is suspended in when it is dropped, and it never
3618    /// resumes. Two grants reached through here are TAKE-THEN-WRITE sequences, meaning they remove
3619    /// a single-use credential from the store and then persist what that credential became. A drop
3620    /// between the two leaves the first half done and nothing to finish it, and the crate cannot
3621    /// make a dropped future complete: there is no destructor that can run an `async` store call.
3622    /// So the CONTRACT is stated here rather than silently relied on, because until 0.9.1 a host
3623    /// had no way to learn it.
3624    ///
3625    /// Named exactly, because the cost differs:
3626    ///
3627    /// - `authorization_code`. The code is TAKEN (RFC 6749 s4.1.2's one-time use), then a CONSUMED
3628    ///   record is written, then the tokens are issued, then that record is updated with what they
3629    ///   were. A drop between the take and the consumed write is the most expensive one in the
3630    ///   crate: the code is gone, so it cannot be redeemed twice, but RFC 9700 s4.1.1 replay
3631    ///   DETECTION works by recognising a code that was already redeemed, and this leaves no record
3632    ///   to recognise. A later replay of a code that leaked into a log, a `Referer` header or
3633    ///   browser history reads as an unknown string, permanently and silently, for that grant. The
3634    ///   write order was chosen to make a store FAILURE fall this way round rather than the other,
3635    ///   and a drop lands in the same window that ordering shrank; it cannot close it.
3636    /// - `refresh_token`. The presented token is TAKEN, then a SPENT record is written, then the
3637    ///   rotated chain is issued. A drop between the take and the spent write destroys the client's
3638    ///   chain (the string it holds is gone and no replacement was persisted, so the user
3639    ///   authenticates again) and loses the RFC 9700 s4.14.2 reuse marker for it, which is the same
3640    ///   loss as above: a later presentation of that token is an unknown string rather than
3641    ///   evidence of compromise, so it revokes no family.
3642    /// - The device grant's successful poll takes its grant record before issuing, with the same
3643    ///   shape and the same cost as the code path.
3644    ///
3645    /// WHAT A HOST MUST DO. Drive this from a task the connection cannot cancel, and await THAT:
3646    /// spawn the call and await the join handle, so a disconnecting client aborts the response and
3647    /// not the work. This crate's own axum adapter does exactly that; see [`crate::http`]. A host
3648    /// that instead selects this future against a timeout, a shutdown signal, or a connection
3649    /// watcher is choosing every cost listed above, and choosing it at whatever rate its clients
3650    /// disconnect.
3651    pub fn token(
3652        &self,
3653        request: TokenRequest,
3654    ) -> impl std::future::Future<Output = Result<TokenResponse, ErrorResponse>> + '_ {
3655        self.token_with_resources(request, &[])
3656    }
3657
3658    /// The token endpoint with the RFC 8707 `resource` parameter.
3659    ///
3660    /// `resources` is the (possibly repeated) `resource` parameter from the token request, in wire
3661    /// order. It is a separate argument rather than a field on every [`TokenRequest`] variant on
3662    /// purpose: RFC 8707 section 2 defines `resource` as a parameter of the token REQUEST,
3663    /// independent of `grant_type`, so putting it on each variant would state the same thing four
3664    /// times, grow the enum every host copies around, and make every future grant type repeat it
3665    /// again.
3666    ///
3667    /// What it does depends on the grant, and section 2 is what decides:
3668    ///
3669    /// - `authorization_code` and `refresh_token` may NARROW to a subset of what the authorization
3670    ///   request obtained, and never widen it;
3671    /// - `client_credentials` has no prior authorization request, so its resources are simply
3672    ///   validated and used;
3673    /// - `urn:ietf:params:oauth:grant-type:device_code` refuses any resource with `invalid_target`.
3674    ///   The device authorization request (RFC 8628 section 3.1) does not accept `resource` in this
3675    ///   crate yet, so there is nothing granted for a poll to narrow to, and inventing an audience
3676    ///   at the token endpoint that the user never approved is exactly what the narrowing rule
3677    ///   exists to prevent.
3678    pub fn token_with_resources<'a>(
3679        &'a self,
3680        request: TokenRequest,
3681        resources: &'a [String],
3682    ) -> impl std::future::Future<Output = Result<TokenResponse, ErrorResponse>> + 'a {
3683        self.token_with_context(
3684            request,
3685            TokenRequestContext {
3686                resources,
3687                ..Default::default()
3688            },
3689        )
3690    }
3691
3692    /// The token endpoint with everything about the request that does not belong inside
3693    /// [`TokenRequest`]: the RFC 8707 resource indicators, the RFC 7523 client assertion, and the
3694    /// RFC 9449 DPoP proof.
3695    ///
3696    /// [`AuthorizationServer::token`] and [`AuthorizationServer::token_with_resources`] are this
3697    /// with an emptier context, so there is one implementation of the token endpoint and not three.
3698    /// Both of them are plain functions returning THIS future rather than `async fn`s that await
3699    /// it, and that is a measurement rather than a style: an `async fn` wrapper is a second
3700    /// generator frame holding its own copy of the 120-byte [`TokenRequest`] while the inner future
3701    /// holds another, and adding one pushed the token future over tokio's 2048-byte debug boxing
3702    /// threshold. `tests/allocation.rs` caught it.
3703    ///
3704    /// # Why THIS one is a plain function too, and not an `async fn`
3705    ///
3706    /// The same measurement, one level down, and it is the largest single saving on this path.
3707    /// An `async fn` stores its parameters TWICE: once as the coroutine's upvars, which is where
3708    /// they live before the first poll, and again as the locals they are moved into on that first
3709    /// poll. rustc does not overlay the two, so `request` (120 bytes) and `context` (104 bytes)
3710    /// were each counted twice for the whole life of the future. A plain function returning an
3711    /// `async move` block captures each ONCE, as an upvar the body reads directly.
3712    ///
3713    /// Measured on the RFC 6749 s4.1.3 arm, which is the widest: 2056 bytes as an `async fn`
3714    /// against 1824 as a block, both `--all-features`. The first of those is past tokio's
3715    /// threshold and costs a 2 KB heap allocation on every single token request.
3716    ///
3717    /// The client secret may be presented EITHER on the [`TokenRequest`] variant (where it has
3718    /// always lived) or on [`TokenRequestContext::credential`]; the context wins when both are set,
3719    /// and neither is silently dropped.
3720    // `manual_async_fn` is exactly the simplification this function must NOT take: see the
3721    // measurement in the doc comment above. An `async fn` here stores `request` and `context`
3722    // twice over and puts the token future past tokio's debug boxing threshold.
3723    #[allow(clippy::manual_async_fn)]
3724    pub fn token_with_context<'a>(
3725        &'a self,
3726        request: TokenRequest,
3727        context: TokenRequestContext<'a>,
3728    ) -> impl std::future::Future<Output = Result<TokenResponse, ErrorResponse>> + 'a {
3729        async move {
3730            let requested_resources =
3731                self.validate_resources(context.resources.iter().map(|r| r.as_str()))?;
3732            // RFC 9396 s5 and s6, parsed and type-checked ONCE here for the same reason the
3733            // resource indicators are validated once here: it is a parameter of the token
3734            // request itself, not of any one grant. The s5 type check has to run at THIS
3735            // endpoint too and not only at the authorization endpoint, because
3736            // `client_credentials` reaches issuance without ever passing the other one.
3737            #[cfg(feature = "rar")]
3738            let requested_details = match context.authorization_details {
3739                None => GrantedDetails::default(),
3740                Some(raw) => {
3741                    let parsed = crate::rar::AuthorizationDetails::parse(raw)?;
3742                    parsed.require_supported_types(
3743                        self.config.authorization_details_types_supported.as_deref(),
3744                    )?;
3745                    GrantedDetails::of(&parsed)
3746                }
3747            };
3748            // And the build that supports NO type refuses the parameter outright, which is the
3749            // same s5 rule with the type list empty. Checked before the grant is looked up, so
3750            // the client hears about the parameter it sent rather than about the code it sent:
3751            // an `invalid_grant` for a request whose real defect is `authorization_details`
3752            // sends the client's author to the wrong half of the request.
3753            #[cfg(not(feature = "rar"))]
3754            if context.authorization_details.is_some() {
3755                return Err(ErrorResponse::new(ErrorCode::InvalidAuthorizationDetails)
3756                    .with_description("this server does not support authorization_details"));
3757            }
3758            #[cfg(not(feature = "rar"))]
3759            let requested_details = GrantedDetails::default();
3760            // RFC 9449 s4.3, before anything else touches the store: see `verify_dpop`.
3761            //
3762            // NOT boxed, and that is a measurement. It was `Box::pin(..)` through 0.9.0, to keep
3763            // the proof-check state out of the token future, and by then the restructuring
3764            // recorded above (an `async move` block rather than an `async fn`, and matching
3765            // `request` by reference) had already bought back more than the box was saving.
3766            // Measured both ways on four feature sets, the future is byte for byte identical:
3767            // 1136 under `dpop` alone, 1248 with `rar`, 1280 with `mtls,consent,rar,par`, 1344
3768            // `--all-features`. So the box bought nothing and cost one 168-byte allocation on
3769            // EVERY token request under this feature, including every refusal, which is traffic an
3770            // attacker sets the rate of. `refused_token_request_allocation_bound` had been
3771            // carrying that as a named exception; it now asserts zero on every feature set.
3772            //
3773            // THIS ENDPOINT IS EXPENSIVE UNDER `dpop`, AND A HOST HAS TO SIZE ITS RATE LIMITER FOR
3774            // IT. A DPoP proof carrying a WRONG signature costs a full P-256 verification, MEASURED
3775            // at 133.18 us on the machine `benches/README.md` names, and it is indistinguishable
3776            // from a valid one until that verification finishes; a merely MALFORMED proof costs
3777            // 34 ns. So roughly 7,500 requests per second of well-formed garbage saturates a core,
3778            // and the caller need hold no credential to send them.
3779            //
3780            // The ORDERING was examined and deliberately left alone. Moving the proof check after
3781            // `authenticate_client` would put a cheap credential test first, but it cannot be done
3782            // in this function (each grant helper authenticates its own client, with its own
3783            // credential shape), and doing it inside each helper would CHANGE THE WIRE ANSWER: a
3784            // request with both a bad secret and a bad proof would answer `invalid_client` where it
3785            // now answers `invalid_dpop_proof`. That is error semantics, visible to every
3786            // conforming client, traded for a mitigation that is partial anyway, since a caller who
3787            // has any valid client credential (a public client id is one) pays nothing to get past
3788            // the reorder. RFC 9449 offers nothing cheaper to filter on either: the proof is signed
3789            // by a key the AS learns FROM the proof, so there is nothing to check before checking
3790            // the signature.
3791            //
3792            // The honest answer is therefore the rate limiter, not the ordering. See
3793            // `crate::rate_limit`.
3794            #[cfg(feature = "dpop")]
3795            let jkt = self.verify_dpop(context.dpop_proof).await?;
3796            // Matched by REFERENCE, and that is the second half of the same measurement the doc
3797            // comment above records. Moving the fields out of the enum does not free the enum's
3798            // own slot in the coroutine: `request` is an upvar, so its 120 bytes are reserved for
3799            // the whole life of the future either way, and the moved-out owned fields were then a
3800            // second 120 bytes of the same data live across the grant helper's await. Borrowing
3801            // costs the helpers nothing, because every one of them already takes `&str` /
3802            // `&ClientId` / `Option<&ScopeSet>`. Measured at 128 bytes of the all-features token
3803            // future, 1952 down to 1824.
3804            match &request {
3805                TokenRequest::AuthorizationCode {
3806                    client_id,
3807                    client_secret,
3808                    code,
3809                    redirect_uri,
3810                    code_verifier,
3811                } => {
3812                    let bound = Bound {
3813                        cred: context.credential.or_secret(client_secret.as_deref()),
3814                        #[cfg(feature = "dpop")]
3815                        jkt: jkt.as_deref(),
3816                    };
3817                    let outcome = self
3818                        .authorization_code_token(
3819                            client_id,
3820                            &bound,
3821                            code,
3822                            redirect_uri.as_deref(),
3823                            code_verifier.as_deref(),
3824                            &requested_resources,
3825                            requested_details,
3826                        )
3827                        .await;
3828                    self.emit_refusal(client_id, GrantType::AuthorizationCode, &outcome);
3829                    outcome
3830                }
3831                TokenRequest::ClientCredentials {
3832                    client_id,
3833                    client_secret,
3834                    scope,
3835                } => {
3836                    let bound = Bound {
3837                        cred: context.credential.or_secret(client_secret.as_deref()),
3838                        #[cfg(feature = "dpop")]
3839                        jkt: jkt.as_deref(),
3840                    };
3841                    let outcome = self
3842                        .client_credentials_token(
3843                            client_id,
3844                            &bound,
3845                            scope.as_ref(),
3846                            requested_resources,
3847                            requested_details,
3848                        )
3849                        .await;
3850                    self.emit_refusal(client_id, GrantType::ClientCredentials, &outcome);
3851                    outcome
3852                }
3853                TokenRequest::DeviceCode {
3854                    client_id,
3855                    client_secret,
3856                    device_code,
3857                } => {
3858                    // RFC 8707 s2: nothing was granted to narrow to, so a resource here would be an
3859                    // audience the user never approved. See `token_with_resources`.
3860                    if !requested_resources.is_empty() {
3861                        return Err(
3862                            ErrorResponse::new(ErrorCode::InvalidTarget).with_description(
3863                                "the device authorization request granted no resource to narrow to",
3864                            ),
3865                        );
3866                    }
3867                    // RFC 9396 s6, and the same argument: the device authorization request
3868                    // cannot carry authorization_details in this crate, so there is nothing
3869                    // granted for this poll to narrow to, and minting detail here would be
3870                    // authorizing something the user never saw.
3871                    #[cfg(feature = "rar")]
3872                    if !requested_details.is_empty() {
3873                        return Err(ErrorResponse::new(ErrorCode::InvalidAuthorizationDetails)
3874                            .with_description(
3875                                "the device authorization request granted no authorization_details",
3876                            ));
3877                    }
3878                    let bound = Bound {
3879                        cred: context.credential.or_secret(client_secret.as_deref()),
3880                        #[cfg(feature = "dpop")]
3881                        jkt: jkt.as_deref(),
3882                    };
3883                    let outcome = self.device_token(client_id, &bound, device_code).await;
3884                    self.emit_refusal(client_id, GrantType::DeviceCode, &outcome);
3885                    outcome
3886                }
3887                TokenRequest::RefreshToken {
3888                    client_id,
3889                    client_secret,
3890                    refresh_token,
3891                    scope,
3892                } => {
3893                    let bound = Bound {
3894                        cred: context.credential.or_secret(client_secret.as_deref()),
3895                        #[cfg(feature = "dpop")]
3896                        jkt: jkt.as_deref(),
3897                    };
3898                    let outcome = self
3899                        .refresh_token(
3900                            client_id,
3901                            &bound,
3902                            refresh_token,
3903                            scope.as_ref(),
3904                            &requested_resources,
3905                            requested_details,
3906                        )
3907                        .await;
3908                    self.emit_refusal(client_id, GrantType::RefreshToken, &outcome);
3909                    outcome
3910                }
3911            }
3912        }
3913    }
3914
3915    /// Emit [`Event::GrantRefused`] when a token-endpoint answer was an error.
3916    ///
3917    /// A non-async helper called from the arms of [`AuthorizationServer::token_with_resources`],
3918    /// where the client id is already an owned local. Deliberately NOT a wrapper around the whole
3919    /// endpoint: see the note in the 0.2.0 hook patch, and `tests/allocation.rs`.
3920    fn emit_refusal(
3921        &self,
3922        client_id: &ClientId,
3923        grant_type: GrantType,
3924        outcome: &Result<TokenResponse, ErrorResponse>,
3925    ) {
3926        if let Err(error) = outcome {
3927            self.hooks.emit(|| Event::GrantRefused {
3928                client_id: client_id.as_str(),
3929                grant_type,
3930                error: error.error,
3931            });
3932        }
3933    }
3934
3935    /// Validate an authorization request (RFC 6749 section 4.1.1) before any user interaction.
3936    ///
3937    /// The order of checks is dictated by RFC 6749 section 4.1.2.1 and is a security boundary,
3938    /// not a style choice: the client and the redirect URI are validated FIRST, because until
3939    /// they are, there is no address the server may safely send an error to. Everything checked
3940    /// afterwards is reported by redirecting to the (now validated) URI.
3941    ///
3942    /// On success the host shows its consent UI and then calls
3943    /// [`AuthorizationServer::issue_authorization_code`], or reports
3944    /// [`ValidatedAuthorizationRequest::denied`] if the user refuses.
3945    pub async fn validate_authorization_request(
3946        &self,
3947        request: &AuthorizationRequest<'_>,
3948    ) -> Result<ValidatedAuthorizationRequest, AuthorizationError> {
3949        // The POLICY gate, ahead of the validation itself: a deployment may declare that
3950        // parameters in the query are not an acceptable way to ask for authorization at all.
3951        //
3952        // RFC 9126 section 4 lets a server require PAR globally, and RFC 9101 section 10.5
3953        // requires the equivalent for signed request objects, both for the same reason: an
3954        // attacker who can rewrite the browser's URL will simply strip the protection and send a
3955        // plain RFC 6749 request unless the server refuses one. This is the only entry point that
3956        // takes query parameters, so refusing here is what makes the policy hold; `par.rs` reaches
3957        // the validation below directly, having already established that the request was pushed or
3958        // signed.
3959        #[cfg(feature = "par")]
3960        if matches!(&self.config.par, Some(par) if par.require_pushed_authorization_requests) {
3961            return Err(AuthorizationError::Direct(
3962                ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
3963                    "this server accepts authorization request data only via PAR (RFC 9126 s4)",
3964                ),
3965            ));
3966        }
3967        #[cfg(feature = "jar")]
3968        if matches!(&self.config.jar, Some(jar) if jar.require_signed_request_object) {
3969            return Err(AuthorizationError::Direct(
3970                ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
3971                    "this server requires a signed request object (RFC 9101 s10.5)",
3972                ),
3973            ));
3974        }
3975        self.validate_direct_authorization_request(request).await
3976    }
3977
3978    /// The validation itself, with no policy gate in front of it.
3979    ///
3980    /// Split out for RFC 9126 / RFC 9101: a pushed or signed request has ALREADY satisfied the
3981    /// policy the wrapper above enforces, and it arrives as parameters rather than as a query, so
3982    /// it needs this and not the wrapper. Everything else about it is unchanged, which is the
3983    /// point: the PAR endpoint validates a pushed request by calling exactly the function the
3984    /// authorization endpoint calls, so the two cannot drift.
3985    pub(crate) async fn validate_direct_authorization_request(
3986        &self,
3987        request: &AuthorizationRequest<'_>,
3988    ) -> Result<ValidatedAuthorizationRequest, AuthorizationError> {
3989        // THE THROTTLE, and this endpoint had none. RFC 9700 section 4.13 is about credential
3990        // stuffing at the token endpoint; this one takes NO CREDENTIAL, which is precisely why it
3991        // needs a bound of its own rather than sharing `Attempt::ClientAuthentication`'s. What a
3992        // deployment is bounding here is work and storage: every request costs a `get_client`, and
3993        // an approved one goes on to WRITE an authorization code record that nothing but
3994        // `Storage::sweep_expired` ever reclaims.
3995        //
3996        // Asked FIRST, before the store is touched, for the same reason `authenticate_client` asks
3997        // first: a refused attempt must cost nothing and reveal nothing.
3998        //
3999        // `temporarily_unavailable` (RFC 6749 section 4.1.2.1) rather than `invalid_request`,
4000        // because nothing about the request was wrong and a client that retries later will
4001        // succeed. It is DIRECT rather than a redirect: at this point neither the client nor the
4002        // redirect URI has been validated, so there is no address this server may safely send
4003        // anything to.
4004        let attempt = Attempt::AuthorizationRequest {
4005            client_id: request.client_id.as_deref().unwrap_or(""),
4006        };
4007        if self.hooks.check(attempt) == RateLimitDecision::Deny {
4008            return Err(AuthorizationError::Direct(
4009                ErrorResponse::new(ErrorCode::TemporarilyUnavailable)
4010                    .with_description("too many authorization requests; retry later"),
4011            ));
4012        }
4013        let outcome = self
4014            .validate_direct_authorization_request_inner(request)
4015            .await;
4016        // Reported back so a limiter can count FAILURES rather than traffic, exactly as the token
4017        // plane does. A refused authorization request is the signal worth counting here: a caller
4018        // walking client ids, or replaying a malformed request, produces nothing else.
4019        self.hooks.record(
4020            attempt,
4021            match &outcome {
4022                Ok(_) => AttemptOutcome::Succeeded,
4023                Err(_) => AttemptOutcome::Failed,
4024            },
4025        );
4026        outcome
4027    }
4028
4029    /// The validation itself, with the throttle above already satisfied.
4030    ///
4031    /// Split out so that every `?` below reports its refusal to the limiter through ONE place. The
4032    /// alternative, threading `hooks.record` through a dozen early returns, is the shape that ends
4033    /// with one path that forgot to.
4034    async fn validate_direct_authorization_request_inner(
4035        &self,
4036        request: &AuthorizationRequest<'_>,
4037    ) -> Result<ValidatedAuthorizationRequest, AuthorizationError> {
4038        // `&'static str`: every description below is a constant naming a condition, never a
4039        // value out of the request, so the refusal borrows it rather than copying it. The
4040        // authorization endpoint is unauthenticated, so its refusal rate is the attacker's to
4041        // choose.
4042        let direct = |code: ErrorCode, why: &'static str| {
4043            AuthorizationError::Direct(ErrorResponse::new(code).with_description(why))
4044        };
4045
4046        // 1. The client. An unknown client_id and a malformed one collapse into one answer: the
4047        //    user agent is untrusted here, and telling it which client ids exist helps nobody.
4048        let client_id = request
4049            .client_id
4050            .as_deref()
4051            .filter(|s| !s.is_empty())
4052            .ok_or_else(|| direct(ErrorCode::InvalidRequest, "missing client_id"))?;
4053        let client = self
4054            .store
4055            .get_client(&ClientId::new(client_id))
4056            .await
4057            .map_err(|_| direct(ErrorCode::ServerError, "storage unavailable"))?
4058            .ok_or_else(|| direct(ErrorCode::InvalidRequest, "unknown client_id"))?;
4059
4060        // 2. The redirect URI. OAuth 2.1 section 4.1.3 requires exact string comparison: no
4061        //    prefix matching, no ignoring a trailing slash, no normalising case. Every relaxation
4062        //    of this rule has a published attack behind it.
4063        //
4064        //    WHETHER IT WAS SENT is recorded as well as what it resolved to, because RFC 6749
4065        //    section 4.1.3 makes the token endpoint's copy of this parameter conditional on the
4066        //    authorization request having carried one. The resolved value below cannot answer that
4067        //    question: the section 3.1.2.3 omission path fills it in from the registration, so both
4068        //    cases arrive at the token endpoint looking identical.
4069        let redirect_uri_was_explicit = request.redirect_uri.is_some();
4070        let redirect_uri = match request.redirect_uri.as_deref() {
4071            Some(requested) => client
4072                .redirect_uris
4073                .iter()
4074                .find(|registered| registered.as_str() == requested)
4075                .cloned()
4076                .ok_or_else(|| {
4077                    direct(
4078                        ErrorCode::InvalidRequest,
4079                        "redirect_uri does not exactly match a registered URI",
4080                    )
4081                })?,
4082            // RFC 6749 section 3.1.2.3: the request may omit it only when there is exactly one
4083            // registration to mean. With several, the server would be guessing where to send a
4084            // credential, and a wrong guess is the whole attack.
4085            //
4086            // ZERO AND SEVERAL ARE ONE ANSWER, and that is the same rule step 1 above states.
4087            // Separate descriptions ("client has no registered redirect_uri" against "redirect_uri
4088            // is required when several are registered") sorted every client id into three buckets
4089            // by how many URIs it has registered, from one UNAUTHENTICATED request carrying
4090            // nothing but a `client_id`. Combined with the unknown-id refusal that is four
4091            // distinguishable answers about a registration the caller has proved no relationship
4092            // to, which defeats the collapse the comment on step 1 promises. Neither case can be
4093            // repaired by the client anyway: both are answered by SENDING a `redirect_uri`, and
4094            // the string below says so.
4095            None => match client.redirect_uris.as_slice() {
4096                [only] => only.clone(),
4097                _ => {
4098                    return Err(direct(
4099                        ErrorCode::InvalidRequest,
4100                        "redirect_uri is required for this client",
4101                    ))
4102                }
4103            },
4104        };
4105
4106        // From here the redirect URI is trusted, so errors go back to the client (section
4107        // 4.1.2.1) carrying the state that lets it correlate them.
4108        let state = request.state.as_deref().map(str::to_string);
4109        let redirect = |code: ErrorCode, why: &'static str| {
4110            AuthorizationError::Redirect(AuthorizationErrorRedirect {
4111                redirect_uri: redirect_uri.clone(),
4112                error: ErrorResponse::new(code).with_description(why),
4113                state: state.clone(),
4114                // RFC 9207 section 2: every authorization response, including this one, names the
4115                // server that produced it.
4116                iss: self.issuer_identifier().to_string(),
4117            })
4118        };
4119
4120        // 3. response_type. OAuth 2.1 removes the implicit grant, so `token` is not merely
4121        //    unsupported by this server, it is gone from the protocol.
4122        match request.response_type.as_deref() {
4123            Some("code") => {}
4124            None => return Err(redirect(ErrorCode::InvalidRequest, "missing response_type")),
4125            Some(_) => {
4126                return Err(redirect(
4127                    ErrorCode::UnsupportedResponseType,
4128                    "this server issues authorization codes only",
4129                ))
4130            }
4131        }
4132
4133        if !client.allows_grant(GrantType::AuthorizationCode) {
4134            return Err(redirect(
4135                ErrorCode::UnauthorizedClient,
4136                "client registration does not include the authorization_code grant",
4137            ));
4138        }
4139
4140        // 4. PKCE. OAuth 2.1 requires it for every authorization code request. RFC 7636 section
4141        //    4.3 defaults an absent code_challenge_method to `plain`, which this server does not
4142        //    implement and does not advertise, so an absent method is refused rather than
4143        //    silently downgraded.
4144        match request.code_challenge_method.as_deref() {
4145            Some("S256") => {}
4146            None => {
4147                return Err(redirect(
4148                    ErrorCode::InvalidRequest,
4149                    "code_challenge_method=S256 is required",
4150                ))
4151            }
4152            Some(_) => {
4153                return Err(redirect(
4154                    ErrorCode::InvalidRequest,
4155                    "only code_challenge_method=S256 is supported",
4156                ))
4157            }
4158        }
4159        let code_challenge = request.code_challenge.as_deref().unwrap_or_default();
4160        if !challenge_is_well_formed(code_challenge) {
4161            // A malformed challenge can never match any verifier, so accepting it would issue a
4162            // code that is guaranteed to fail redemption later, with a misleading error.
4163            return Err(redirect(
4164                ErrorCode::InvalidRequest,
4165                "code_challenge must be the base64url SHA-256 form of RFC 7636 section 4.2",
4166            ));
4167        }
4168
4169        // 5. Scope. RFC 6749 section 3.3: absent means the registered default, trimmed to what the
4170        // registration allows. See `granted_default_scope`, which is shared with `resolve_scope`
4171        // precisely so this endpoint and the token endpoint cannot answer differently.
4172        let scope = match request.scope.as_deref() {
4173            None => Self::granted_default_scope(&client),
4174            Some(s) => {
4175                let requested = ScopeSet::parse(s)
4176                    .map_err(|_| redirect(ErrorCode::InvalidScope, "malformed scope"))?;
4177                if !requested.is_subset(&client.allowed_scopes) {
4178                    return Err(redirect(
4179                        ErrorCode::InvalidScope,
4180                        "requested scope exceeds the client registration",
4181                    ));
4182                }
4183                requested
4184            }
4185        };
4186
4187        // 6. RFC 8707 resource indicators. Checked LAST of the redirectable checks because it is
4188        //    the newest and least load-bearing of them: a request that is also missing PKCE should
4189        //    hear about PKCE. A malformed indicator is `invalid_target` (section 2), reported to
4190        //    the client rather than to the user, since by here the redirect URI is trusted.
4191        let resource = self
4192            .validate_resources(request.resource.iter().map(|r| r.as_ref()))
4193            .map_err(|e| {
4194                AuthorizationError::Redirect(AuthorizationErrorRedirect {
4195                    redirect_uri: redirect_uri.clone(),
4196                    error: e,
4197                    state: state.clone(),
4198                    iss: self.issuer_identifier().to_string(),
4199                })
4200            })?;
4201
4202        // RFC 9396 authorization_details, checked last among the redirectable checks for
4203        // the same reason `resource` is checked late: it is the newest of them, and a
4204        // request that is also missing PKCE should hear about PKCE. Reported to the client
4205        // rather than to the user, since by here the redirect URI is trusted, and REFUSED
4206        // rather than ignored (section 5), because a client whose authorization detail was
4207        // silently dropped would obtain a token it believes says something it does not.
4208        //
4209        // THE BUILD WITHOUT `rar` REFUSES EVERY ONE OF THEM, which is the stronger case and not
4210        // an absent one: it supports no authorization detail type whatsoever, so section 5's
4211        // condition holds for any value at all and there is nothing to parse before answering.
4212        // Same posture as the RFC 9101 request object path in `crate::par`, which already said
4213        // this; this is the plain query request, and the RFC 9126 push, which reaches here too.
4214        #[cfg(not(feature = "rar"))]
4215        if request.authorization_details.is_some() {
4216            return Err(AuthorizationError::Redirect(AuthorizationErrorRedirect {
4217                redirect_uri: redirect_uri.clone(),
4218                error: ErrorResponse::new(ErrorCode::InvalidAuthorizationDetails)
4219                    .with_description("this server does not support authorization_details"),
4220                state: state.clone(),
4221                iss: self.issuer_identifier().to_string(),
4222            }));
4223        }
4224
4225        #[cfg(feature = "rar")]
4226        let details = {
4227            let to_redirect = |error: ErrorResponse| {
4228                AuthorizationError::Redirect(AuthorizationErrorRedirect {
4229                    redirect_uri: redirect_uri.clone(),
4230                    error,
4231                    state: state.clone(),
4232                    iss: self.issuer_identifier().to_string(),
4233                })
4234            };
4235            match request.authorization_details.as_deref() {
4236                None => crate::rar::AuthorizationDetails::none(),
4237                Some(raw) => {
4238                    let parsed =
4239                        crate::rar::AuthorizationDetails::parse(raw).map_err(to_redirect)?;
4240                    parsed
4241                        .require_supported_types(
4242                            self.config.authorization_details_types_supported.as_deref(),
4243                        )
4244                        .map_err(to_redirect)?;
4245                    parsed
4246                }
4247            }
4248        };
4249
4250        // RFC 9470 s4's `acr_values` and `max_age`, parsed from THIS request rather than from the
4251        // query the user agent arrived with. That distinction is the whole point: for an RFC 9126
4252        // pushed request `request` is the stored record, and for an RFC 9101 signed one it is the
4253        // verified claim set, so the two parameters now survive both (they were dropped entirely
4254        // before, which disabled step-up for every PAR and JAR deployment) and the query cannot
4255        // supply them for either (RFC 9126 s4, RFC 9101 s6.3, which says the server MUST use only
4256        // the object's parameters even when the query repeats them).
4257        //
4258        // Checked last among the redirectable checks, so a request that is ALSO missing PKCE still
4259        // hears about PKCE first; that is the order the endpoint reported before this moved here.
4260        #[cfg(feature = "consent")]
4261        let requirement = crate::consent::AuthenticationRequirement::from_request(request)
4262            .map_err(|error| {
4263                AuthorizationError::Redirect(AuthorizationErrorRedirect {
4264                    redirect_uri: redirect_uri.clone(),
4265                    error,
4266                    state: state.clone(),
4267                    iss: self.issuer_identifier().to_string(),
4268                })
4269            })?;
4270
4271        // `mut` plus a setter rather than a ninth constructor argument: see
4272        // `ValidatedAuthorizationRequest::set_authorization_details`.
4273        #[allow(unused_mut)]
4274        let mut validated = ValidatedAuthorizationRequest::new(
4275            // Cloned rather than moved: `client` is an `Arc<Client>` shared with the store since
4276            // `Storage::get_client` stopped deep-copying the registration, so the id has to be
4277            // copied out. One allocation on the AUTHORIZATION endpoint, against the eight the
4278            // shared read saved on it.
4279            client.client_id.clone(),
4280            redirect_uri,
4281            redirect_uri_was_explicit,
4282            scope,
4283            state,
4284            code_challenge.to_string(),
4285            CodeChallengeMethod::S256,
4286            self.issuer_identifier().to_string(),
4287            resource,
4288        );
4289        #[cfg(feature = "rar")]
4290        validated.set_authorization_details(details);
4291        #[cfg(feature = "consent")]
4292        validated.set_authentication_requirement(requirement);
4293        Ok(validated)
4294    }
4295
4296    /// Mint an authorization code for a request the user has approved (RFC 6749 section 4.1.2).
4297    ///
4298    /// RFC 6749 SECTION 10.12 IS THE REASON THIS TAKES A [`UserApproval`] AND NOT A SUBJECT.
4299    /// Knowing WHO the user is does not establish that they agreed to anything. An authorization
4300    /// endpoint that mints a code as soon as it can name the logged-in user issues one on any
4301    /// cross-site top-level navigation that user's browser can be made to follow, which is exactly
4302    /// the cross-site request forgery section 10.12 describes: the attacker's client, the victim's
4303    /// session, a code delivered to the attacker's registered redirect URI. Nothing in this crate
4304    /// can see a user, so nothing here can detect that; the only defence a library has is to
4305    /// require the host to SAY that a resource owner approved this request, and to be unbuildable
4306    /// without it.
4307    ///
4308    /// Taking a [`ValidatedAuthorizationRequest`] (through the approval) rather than a raw request
4309    /// is deliberate for the same reason one level down: an unvalidated request cannot reach code
4310    /// issuance, because it cannot be spelled.
4311    pub async fn issue_authorization_code(
4312        &self,
4313        approval: UserApproval<'_>,
4314    ) -> Result<AuthorizationResponse, AuthorizationError> {
4315        // RFC 9470 IS ENFORCED HERE TOO, and it was not until the 0.9.1 audit.
4316        // `validate_authorization_request` parses `acr_values` and `max_age` onto the validated
4317        // request, and this entry point threw the result away: it minted a code for a request that
4318        // demanded a step-up without evaluating the demand, and `GrantedAuthentication::default()`
4319        // then recorded no authentication, so introspection reported no `auth_time` and no `acr`
4320        // for every token the code produced. The sibling below calls that enforcement "a library
4321        // job rather than a host job on purpose: a `max_age` the host is trusted to check for
4322        // itself is a `max_age` that gets checked in whichever code path somebody remembered", and
4323        // this was the path nobody remembered. `crate::http` uses the sibling, so what shipped
4324        // unenforced was the DIRECT API, which is the path `UserApproval` documents as the one
4325        // this crate's default build invites.
4326        //
4327        // Routed through the sibling with NO report, rather than duplicating the check: this entry
4328        // point has no argument through which a host could report an authentication, so `None` is
4329        // the only honest value and `satisfied_by` answers `Ok(())` for an empty requirement. A
4330        // request carrying neither parameter is therefore completely unaffected, and one carrying
4331        // either is refused with RFC 9470 section 3 `insufficient_user_authentication` rather than
4332        // granted, which is the fail-closed direction and the same answer the sibling gives an
4333        // unreported requirement.
4334        #[cfg(feature = "consent")]
4335        {
4336            let requirement = approval.request().authentication_requirement.clone();
4337            return self
4338                .issue_authorization_code_with_authentication(approval, &requirement, None)
4339                .await;
4340        }
4341        #[cfg(not(feature = "consent"))]
4342        self.issue_authorization_code_inner(approval, GrantedAuthentication::default())
4343            .await
4344    }
4345
4346    /// Mint an authorization code for a request the user has approved, holding the request's RFC
4347    /// 9470 step-up requirement to the authentication the HOST reports it performed.
4348    ///
4349    /// This is the enforcement half of RFC 9470, and it is a library job rather than a host job on
4350    /// purpose: a `max_age` the host is trusted to check for itself is a `max_age` that gets checked
4351    /// in whichever code path somebody remembered. The host still owns the authentication itself,
4352    /// and `authentication` is its REPORT of one; this crate cannot verify that report and does not
4353    /// pretend to. See the [`crate::consent`] module docs for the whole boundary.
4354    ///
4355    /// A requirement the report does not satisfy is refused with RFC 9470 section 3's
4356    /// `insufficient_user_authentication`, delivered as a REDIRECT (RFC 6749 section 4.1.2.1):
4357    /// by this point the redirect URI has been validated, and the client is both the party that
4358    /// asked the question and the party that has to decide whether to send the user back to log in.
4359    /// Nothing is minted and no consent is touched.
4360    /// The approval means the same thing here as it does on
4361    /// [`AuthorizationServer::issue_authorization_code`], and is required for the same RFC 6749
4362    /// section 10.12 reason: a satisfied `acr_values` says the user authenticated STRONGLY, never
4363    /// that they agreed.
4364    #[cfg(feature = "consent")]
4365    pub async fn issue_authorization_code_with_authentication(
4366        &self,
4367        approval: UserApproval<'_>,
4368        requirement: &crate::consent::AuthenticationRequirement,
4369        authentication: Option<&crate::consent::Authentication>,
4370    ) -> Result<AuthorizationResponse, AuthorizationError> {
4371        if let Err(failure) = requirement.satisfied_by(authentication, self.clock.now()) {
4372            let request = approval.request();
4373            return Err(AuthorizationError::Redirect(AuthorizationErrorRedirect {
4374                redirect_uri: request.redirect_uri.clone(),
4375                error: failure.error_response(),
4376                state: request.state.clone(),
4377                iss: request.issuer.clone(),
4378            }));
4379        }
4380        self.issue_authorization_code_inner(
4381            approval,
4382            GrantedAuthentication {
4383                authentication: authentication.cloned().map(Box::new),
4384            },
4385        )
4386        .await
4387    }
4388
4389    /// The issuance itself, shared by both entry points above so that they cannot drift.
4390    async fn issue_authorization_code_inner(
4391        &self,
4392        approval: UserApproval<'_>,
4393        authentication: GrantedAuthentication,
4394    ) -> Result<AuthorizationResponse, AuthorizationError> {
4395        #[cfg(not(feature = "consent"))]
4396        let _ = authentication;
4397        // Destructured rather than borrowed through the approval: `subject` is MOVED into the
4398        // record below, which is the same one allocation the previous `impl Into<String>` argument
4399        // produced. The approval adds no allocation of its own; it is a borrow plus that String.
4400        let UserApproval {
4401            request,
4402            subject,
4403            decided_at,
4404        } = approval;
4405        // THE SECOND CHARGE, and it is a separate one on purpose. `validate_authorization_request`
4406        // is charged for a READ; this is where the authorization code record is WRITTEN, and
4407        // nothing but `Storage::sweep_expired` reclaims one. Charging the write to the validation
4408        // would let a caller who validated once go on issuing for free, which is the half that
4409        // actually grows the store.
4410        //
4411        // A REDIRECT rather than a direct refusal, unlike the validation's: by this point the
4412        // redirect URI has been validated, so RFC 6749 section 4.1.2.1 sends the error back to the
4413        // client, carrying the state that lets it correlate. `temporarily_unavailable` because
4414        // nothing about the request was wrong and a retry later will succeed. Nothing is minted
4415        // and no consent is touched, which is the same posture the step-up refusal above takes.
4416        //
4417        // NOTHING IS RECORDED on the deny, and that is the same rule every other classification
4418        // site in this crate follows (`authenticate_client`, the validation above,
4419        // `pending_grant_by_user_code`, `register_dynamic_client`). `RateLimiter::record` is
4420        // documented as reporting how an ALLOWED attempt turned out, and a denial never became an
4421        // attempt: reporting it as `Failed` would drive the failure count with the very traffic
4422        // the limiter refused, and a host alerting on failure rate — which this crate tells hosts
4423        // to do — would read a client that merely exceeded its ceiling as a caller walking the
4424        // redirect-URI space. This site reported it through the 0.9.1 audit's second charge and
4425        // was the one exception.
4426        let attempt = Attempt::AuthorizationRequest {
4427            client_id: request.client_id.as_str(),
4428        };
4429        if self.hooks.check(attempt) == RateLimitDecision::Deny {
4430            return Err(AuthorizationError::Redirect(AuthorizationErrorRedirect {
4431                redirect_uri: request.redirect_uri.clone(),
4432                error: ErrorResponse::new(ErrorCode::TemporarilyUnavailable)
4433                    .with_description("too many authorization requests; retry later"),
4434                state: request.state.clone(),
4435                iss: request.issuer.clone(),
4436            }));
4437        }
4438        let now = self.clock.now();
4439        // `?` rather than a panic, for the reason `try_random_hex` gives. The refusal is a
4440        // REDIRECT because by this point the redirect URI has been validated (RFC 6749 s4.1.2.1),
4441        // so it goes back to the client the same way the storage failure below does.
4442        let code = try_random_hex(32).ok_or_else(|| {
4443            self.hooks.record(attempt, AttemptOutcome::Failed);
4444            AuthorizationError::Redirect(AuthorizationErrorRedirect {
4445                redirect_uri: request.redirect_uri.clone(),
4446                error: ErrorResponse::new(ErrorCode::ServerError),
4447                state: request.state.clone(),
4448                iss: request.issuer.clone(),
4449            })
4450        })?;
4451        let record = AuthorizationCodeRecord {
4452            // WHEN THE USER'S DECISION HAPPENED, which is not always now. Redemption carries this
4453            // into the issued token so a revocation can tell this code from one minted after it,
4454            // and a barrier refuses a grant that PREDATES it — so dating the decision later than
4455            // it happened is what lets a code outrank a withdrawal recorded in between.
4456            //
4457            // `None` means the host prompted during this request and the two instants are the
4458            // same. A host acting on a standing approval must say so with
4459            // [`UserApproval::granted_at`]; this crate's own `http` service does, passing the
4460            // instant the authorization request was received. See that method for the ordering
4461            // this closes.
4462            issued_at: decided_at.unwrap_or(now),
4463            code: code.clone(),
4464            client_id: request.client_id.clone(),
4465            redirect_uri: request.redirect_uri.clone(),
4466            // RFC 6749 s4.1.3: carried so the token endpoint can require the parameter exactly
4467            // when the authorization request sent one. See the field.
4468            redirect_uri_was_explicit: request.redirect_uri_was_explicit,
4469            scope: request.scope.clone(),
4470            subject,
4471            code_challenge: request.code_challenge.clone(),
4472            code_challenge_method: request.code_challenge_method,
4473            // RFC 8707 s2: what the token this code redeems into may be audience-restricted to.
4474            resource: request.resource.clone(),
4475            // RFC 9396 s7: the details as granted, which is what the redeeming token request
4476            // may narrow and what the issued token will carry.
4477            #[cfg(feature = "rar")]
4478            authorization_details: request.authorization_details.clone(),
4479            expires_at: saturating_deadline(now, self.config.authorization_code_ttl),
4480            state: AuthorizationCodeState::Issued,
4481            // RFC 9470 s6.2: recorded here because the token endpoint has no user in front of it and
4482            // could not ask. See `AuthorizationCodeRecord::authentication`.
4483            #[cfg(feature = "consent")]
4484            authentication: authentication.authentication,
4485        };
4486        self.store
4487            .put_authorization_code(record)
4488            .await
4489            .map_err(|_| {
4490                self.hooks.record(attempt, AttemptOutcome::Failed);
4491                AuthorizationError::Redirect(AuthorizationErrorRedirect {
4492                    redirect_uri: request.redirect_uri.clone(),
4493                    error: ErrorResponse::new(ErrorCode::ServerError),
4494                    state: request.state.clone(),
4495                    iss: request.issuer.clone(),
4496                })
4497            })?;
4498        // The record is written, so the charge is settled as a success. Reported at all because a
4499        // limiter that counts failures needs to be told about the ones that were not.
4500        self.hooks.record(attempt, AttemptOutcome::Succeeded);
4501        Ok(AuthorizationResponse {
4502            code,
4503            state: request.state.clone(),
4504            // RFC 9207 s2. Taken from the validated request rather than re-read from config so the
4505            // success and error halves of one authorization request cannot disagree about who
4506            // answered it.
4507            iss: request.issuer.clone(),
4508        })
4509    }
4510
4511    /// RFC 6749 section 4.1.3 with the OAuth 2.1 PKCE requirement: redeem an authorization code.
4512    // Eight arguments rather than seven, for the reason `issue` carries the same allow: the
4513    // alternative is a parameter struct nobody else would ever construct, wrapping values
4514    // that are already named at this function's one call site.
4515    #[allow(clippy::too_many_arguments)]
4516    async fn authorization_code_token(
4517        &self,
4518        client_id: &ClientId,
4519        bound: &Bound<'_>,
4520        code: &str,
4521        redirect_uri: Option<&str>,
4522        code_verifier: Option<&str>,
4523        requested_resources: &[String],
4524        requested_details: GrantedDetails,
4525    ) -> Result<TokenResponse, ErrorResponse> {
4526        let client = self.authenticate_client(client_id, &bound.cred).await?;
4527        if !client.allows_grant(GrantType::AuthorizationCode) {
4528            return Err(ErrorResponse::new(ErrorCode::UnauthorizedClient));
4529        }
4530
4531        // Single use is enforced by the atomic take: concurrent redemptions of the same code
4532        // cannot both succeed, because only one of them receives the record.
4533        let record = self
4534            .store
4535            .take_authorization_code(code)
4536            .await
4537            .map_err(storage_error)?
4538            .ok_or_else(|| ErrorResponse::new(ErrorCode::InvalidGrant))?;
4539
4540        // A code belongs to the client it was issued to, and this is checked FIRST, before the
4541        // replay branch below, because that branch is DESTRUCTIVE. Ordering it the other way makes
4542        // "revoke the tokens this code minted" reachable by whoever presents the code, and a code
4543        // is a value that leaks: into logs, into `Referer` headers, into browser history. The
4544        // record goes BACK rather than being burned, for the same reason: the legitimate client
4545        // must still be able to complete its flow, and letting a third party destroy a live code
4546        // is a denial of service for free.
4547        //
4548        // Being honest about what this check is and is not. For a CONFIDENTIAL client it is an
4549        // authentication gate, because `authenticate_client` above proved the caller holds the
4550        // secret. For a PUBLIC client it is not: RFC 6749 section 4.1.2 notes that a public client
4551        // id is not a secret and anyone may claim one, so a leaked code still lets an attacker
4552        // reach this branch as the client the code WAS issued to, and still ends that client's
4553        // tokens. That residual is inherent to public clients and PKCE does not close it, since
4554        // the revocation happens before any verifier is checked. What the ordering above does buy
4555        // is that the residual stops at the client whose code actually leaked, instead of being
4556        // handed to every registered client in the deployment.
4557        if record.client_id != client.client_id {
4558            // NOT fire-and-forget. If this write fails, a LIVE code belonging to an honest client
4559            // has just been destroyed by a stranger's request, and answering `invalid_grant` would
4560            // report that as the ordinary refusal it is not: the honest client would come back a
4561            // moment later, be told `invalid_grant` as well, and nobody would ever connect the two.
4562            // `server_error` is the truthful answer and it is the only place this failure can
4563            // surface, because the party in front of us is not the one who was harmed.
4564            //
4565            // It reveals nothing: reaching this branch at all requires a real code, and the
4566            // difference between the two answers is a store failure the caller cannot provoke.
4567            self.store
4568                .put_authorization_code(record)
4569                .await
4570                .map_err(storage_error)?;
4571            return Err(ErrorResponse::new(ErrorCode::InvalidGrant));
4572        }
4573
4574        // A code presented twice is evidence it leaked, so RFC 6749 section 4.1.2 and RFC 9700
4575        // section 4.1.1 want the tokens it already minted revoked, not just the replay refused.
4576        // Refusing the replay alone would leave the attacker's stolen access token live.
4577        // `Replayed` counts too: a THIRD presentation is still a replay, and the record still
4578        // names what to revoke. Reading only `Consumed` here would make every presentation after
4579        // the second one look like an unknown code, which is the answer a typo gets.
4580        if let Some((access_token, refresh_token)) = record.state.minted() {
4581            let (access_token, refresh_token) = (
4582                access_token.map(str::to_string),
4583                refresh_token.map(str::to_string),
4584            );
4585            let (access_token, refresh_token) = (&access_token, &refresh_token);
4586            // Revoking by FAMILY rather than by the two recorded strings, so that a chain the
4587            // client has legitimately rotated since redemption dies too: the compromise is of the
4588            // grant, not of one token from it (RFC 9700 section 4.14.2).
4589            let mut revoked_family = false;
4590            // EVERY STEP BELOW CAN FAIL, AND THE WIRE CANNOT CARRY THE NEWS. The answer to a
4591            // replayed code is `invalid_grant` whatever happens here, because the party being
4592            // answered is whoever holds a leaked code and there is nothing to tell them. So the
4593            // AUDIT EVENT is the only signal a deployment gets, which makes an event that claims
4594            // containment it did not achieve strictly worse than no event: it is what an operator
4595            // reads while deciding not to investigate. This flag is what stops it lying.
4596            let mut containment_failed = false;
4597            // MOVED out of the record rather than cloned: `rec` is a value this scope already owns
4598            // and drops, so carrying its family id to the audit event below costs nothing.
4599            let mut revoked_family_id: Option<String> = None;
4600            if let Some(rt) = refresh_token {
4601                // The `Err` arm is NOT the same thing as `Ok(None)` and must not be folded into it.
4602                // `Ok(None)` means there is no chain to revoke, which is a clean outcome. `Err`
4603                // means the store could not say, so the family revocation was never even ATTEMPTED
4604                // and the attacker's chain may be live. Reading both as "no chain" is exactly the
4605                // overstated containment `containment_failed` exists to prevent.
4606                match self.store.get_refresh_token(rt).await {
4607                    Ok(Some(rec)) => {
4608                        // Cloned out of the shared snapshot `get_refresh_token` handed back. One
4609                        // allocation, on the detected-compromise path only, against the seven that
4610                        // read now costs nothing on the paths that run per request.
4611                        revoked_family_id = Some(rec.family_id.clone());
4612                        // `revoked_family` now means what its name says. It was set unconditionally
4613                        // here, with the `Result` discarded, so a store that failed at the one
4614                        // moment this server was responding to a detected compromise reported a
4615                        // clean containment: the attacker's chain still live, the audit log saying
4616                        // it was killed.
4617                        match self
4618                            .store
4619                            .revoke_token_family(&rec.family_id, self.revocation_window())
4620                            .await
4621                        {
4622                            Ok(_) => revoked_family = true,
4623                            Err(_) => containment_failed = true,
4624                        }
4625                    }
4626                    Ok(None) => {}
4627                    Err(_) => containment_failed = true,
4628                }
4629            }
4630            if !revoked_family {
4631                // No refresh chain to reach the family through (or it is already swept, or the
4632                // revocation above failed): the access token this code minted is still nameable
4633                // directly, so this is a genuine fallback and not merely a tidy-up. Dropping its
4634                // failure left the compromised access token live for its whole TTL, silently.
4635                //
4636                // `None` means the redemption that consumed this code never got as far as issuing
4637                // anything (see `AuthorizationCodeState::Consumed::access_token`), so there is
4638                // genuinely nothing to revoke and nothing failed. The replay is still real and is
4639                // still reported.
4640                if let Some(at) = access_token {
4641                    if self.store.delete_token(at).await.is_err() {
4642                        containment_failed = true;
4643                    }
4644                }
4645            }
4646            // The record goes BACK, and it goes back as `Replayed` rather than as what was read.
4647            // `src/authorization.rs` and `src/store.rs` both promise it is retained until its own
4648            // expiry, and that promise is what makes replay detection work more than once: taking
4649            // it here would make the NEXT replay read as an unknown code. Losing this write
4650            // therefore loses the EVIDENCE, not just a record, which is why it counts as a
4651            // containment failure too.
4652            //
4653            // `Replayed` is the DURABLE TRACE, and it is the half of this that a concurrent
4654            // redemption can see. Putting `Consumed` back would be byte for byte what a redemption
4655            // suspended on the host's signer wrote before it suspended, so that redemption would
4656            // wake, record what it minted, and hand out an access token and a refresh chain this
4657            // very replay was supposed to have contained. See `AuthorizationCodeState::Replayed`.
4658            let replayed = AuthorizationCodeRecord {
4659                state: AuthorizationCodeState::Replayed {
4660                    access_token: access_token.clone(),
4661                    refresh_token: refresh_token.clone(),
4662                },
4663                ..record
4664            };
4665            if self.store.put_authorization_code(replayed).await.is_err() {
4666                containment_failed = true;
4667            }
4668            // EVIDENCE OF COMPROMISE (RFC 6749 section 4.1.2, RFC 9700 section 4.1.1). The
4669            // revocation above is silent without this: a host cannot investigate a stolen code it
4670            // was never told about.
4671            self.hooks.emit(|| Event::AuthorizationCodeReplayDetected {
4672                client_id: client.client_id.as_str(),
4673                family_id: revoked_family_id.as_deref(),
4674                tokens_revoked: revoked_family,
4675                containment_failed,
4676            });
4677            return Err(ErrorResponse::new(ErrorCode::InvalidGrant));
4678        }
4679
4680        if self.clock.now() >= record.expires_at {
4681            // Expired codes are not put back: they can never become valid again.
4682            return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
4683                .with_description("authorization code expired"));
4684        }
4685
4686        // RFC 6749 section 4.1.3: the redirect URI presented here must be the one the code was
4687        // issued against, which is what stops a code obtained for one registered URI being
4688        // redeemed as if it had been issued for another.
4689        //
4690        // THE PARAMETER IS CONDITIONAL, and conditional in both directions. Section 4.1.3 makes it
4691        // REQUIRED "if the `redirect_uri` parameter was included in the authorization request", and
4692        // section 3.1.2.3 entitles a client with exactly one registered URI to omit it there. This
4693        // endpoint required it unconditionally through 0.9.1, so that ordinary and conforming
4694        // client was refused here — and refused with a message blaming a mismatch that had not
4695        // happened, which is the answer that sends its developer looking at its registration.
4696        //
4697        // The check is not weakened for the request that DID send one: `Some` still has to equal
4698        // the recorded URI, and `None` is accepted only against a record that says the
4699        // authorization request named nothing. Sending one where none was sent is still refused,
4700        // because a code minted for a registration-derived URI must not be redeemable as if the
4701        // client had chosen the address itself.
4702        let redirect_uri_matches = match redirect_uri {
4703            Some(u) => u == record.redirect_uri,
4704            None => !record.redirect_uri_was_explicit,
4705        };
4706        if !redirect_uri_matches {
4707            let _ = self.store.put_authorization_code(record).await;
4708            return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
4709                .with_description("redirect_uri does not match the authorization request"));
4710        }
4711
4712        // RFC 7636 section 4.6. A missing verifier is the exact downgrade PKCE exists to stop, so
4713        // it is a failure, never a skipped check.
4714        //
4715        // The LENGTH AND ALPHABET of the verifier are checked here too (section 4.1: 43 to 128
4716        // characters from the unreserved set), and until the 0.9.1 audit they were checked
4717        // nowhere — `pkce::verifier_is_valid` existed and had no caller outside the tests. The
4718        // asymmetry mattered: this crate pins the challenge it MINTS at 43 base64url characters
4719        // and refuses `plain`, so it validated everything the server produced and nothing the
4720        // client presented. A client deriving its challenge from a six-character verifier gets a
4721        // grant an attacker can finish, because section 7.1 puts the `code_challenge` in the
4722        // authorization request — browser history, `Referer`, proxy logs — and six characters is
4723        // not a search. Refusing costs a conforming client nothing.
4724        let verified = match (code_verifier, record.code_challenge_method) {
4725            (Some(v), CodeChallengeMethod::S256) => {
4726                crate::pkce::verifier_is_valid(v)
4727                    && crate::pkce::verify_s256(v, &record.code_challenge)
4728            }
4729            (None, _) => false,
4730        };
4731        if !verified {
4732            let _ = self.store.put_authorization_code(record).await;
4733            return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
4734                .with_description("code_verifier does not match the recorded code_challenge"));
4735        }
4736
4737        // RFC 8707 s2: the token request may narrow the audience the authorization request
4738        // obtained, never widen it. The code is put BACK on refusal, exactly as the scope and
4739        // redirect_uri mismatches above do: asking for the wrong resource is a client bug, and
4740        // burning a live code for a bug the client can fix on retry is a denial of service the
4741        // attacker gets for free.
4742        // RFC 8707 s2 and RFC 9396 s6 are the SAME rule applied to two parameters: the token
4743        // request may narrow what the authorization request obtained, and never widen it.
4744        // Both are computed as one fallible expression with ONE error path, rather than two
4745        // `match` blocks each holding an `ErrorResponse` across a `put_authorization_code`
4746        // await of its own: this function IS the token future, and `tests/allocation.rs`
4747        // holds that future under tokio's 2048-byte debug boxing threshold, past which every
4748        // single token request pays a 2 KB allocation.
4749        //
4750        // The code goes BACK on refusal, exactly as the redirect_uri and PKCE mismatches
4751        // above do: asking for the wrong thing is a client bug, and burning a live code for
4752        // a bug the client can fix on retry is a denial of service the attacker gets free.
4753        let narrowed = self
4754            .narrow_and_permit(&record.resource, requested_resources)
4755            .and_then(|r| {
4756                GrantedDetails::of_code(&record)
4757                    .narrow(&requested_details)
4758                    .map(|d| (r, d))
4759            });
4760        let (resource, details) = match narrowed {
4761            Ok(narrowed) => narrowed,
4762            Err(e) => {
4763                let _ = self.store.put_authorization_code(record).await;
4764                return Err(e);
4765            }
4766        };
4767
4768        // Retain the spent code until its own expiry, recording what it minted, so a later replay
4769        // is recognisable as a replay rather than as an unknown code.
4770        //
4771        // THE CONSUMED RECORD IS WRITTEN BEFORE ISSUANCE, and word for word the same argument as
4772        // the refresh rotation in `refresh_token` below: `take_authorization_code` above has
4773        // already removed this code, `Storage` has no transaction so the take and this write cannot
4774        // be one operation, and all that can be chosen is which way the pair fails.
4775        //
4776        // Issuing first fails OPEN: a store that dies mid-issuance leaves the code gone with no
4777        // consumed record, so a replay of a code that leaked into a log, a `Referer` header or
4778        // browser history reads as a typo, and RFC 6749 section 4.1.2 / RFC 9700 section 4.1.1
4779        // replay detection is off for that grant permanently and silently. Writing it first fails
4780        // CLOSED: the client is refused and starts a new authorization request, which it can do
4781        // without help, and the alarm stays armed.
4782        //
4783        // The price is the SECOND write below, because what the code minted is not knowable until
4784        // it has been minted. That is a much smaller loss if it fails than this one is: the alarm
4785        // is already armed by then.
4786        let subject = record.subject.clone();
4787        let scope = record.scope.clone();
4788        let authentication = GrantedAuthentication::from_code(&record);
4789        let mut consumed = AuthorizationCodeRecord {
4790            state: AuthorizationCodeState::Consumed {
4791                access_token: None,
4792                refresh_token: None,
4793            },
4794            ..record
4795        };
4796        self.store
4797            .put_authorization_code(consumed.clone())
4798            .await
4799            .map_err(storage_error)?;
4800
4801        let issued = self
4802            .issue_boxed(
4803                &client,
4804                bound,
4805                GrantType::AuthorizationCode,
4806                // The user's decision, taken from the code being redeemed. NOT `now`: a code
4807                // minted before a revocation must still be refused when it is redeemed after one.
4808                record.issued_at,
4809                Some(subject),
4810                scope,
4811                resource,
4812                details,
4813                None,
4814                true,
4815                authentication,
4816                // No actor: this grant delegates nothing (RFC 8693 s4.1).
4817                GrantedActor::default(),
4818                // No ceiling: an authorization code's redemption is the START of a grant's
4819                // lifetime, so the ordinary `access_token_ttl` is the whole rule.
4820                None,
4821            )
4822            .await?;
4823
4824        // Now the record can name what it minted, which is what lets a replay REVOKE rather than
4825        // merely be refused.
4826        //
4827        // A COMPARE-AND-SWAP against the record this function wrote itself, not a blind put, and
4828        // the expectation is exactly the `Consumed { None, None }` written before issuance. Two
4829        // things can have happened during the issuance above, which may have suspended on the
4830        // host's `Es256Signer` for a network round trip:
4831        //
4832        // - A REPLAY was detected and marked the record `Replayed`. The swap fails, and it must:
4833        //   the replay path already decided this grant is compromised and found nothing to revoke
4834        //   because nothing had been issued yet. Writing here would hand out the very tokens it
4835        //   was trying to contain.
4836        // - The code was cascaded away by `delete_client` or `revoke_consent`. Absent refuses too,
4837        //   so a withdrawn consent is not undone by a redemption that started before it.
4838        //
4839        // Either way the issuance is undone below rather than reported as a storage failure.
4840        let expected_before_issuance = AuthorizationCodeState::Consumed {
4841            access_token: None,
4842            refresh_token: None,
4843        };
4844        consumed.state = AuthorizationCodeState::Consumed {
4845            access_token: Some(issued.access_token.clone()),
4846            refresh_token: issued.refresh_token.clone(),
4847        };
4848        let recorded = self
4849            .store
4850            .compare_and_swap_authorization_code(&expected_before_issuance, consumed)
4851            .await;
4852        if !matches!(recorded, Ok(true)) {
4853            // The client is answered with an error, so it never receives the tokens that were just
4854            // minted and they become orphans: live records nobody was ever handed. Best effort
4855            // cleanup, and the `Result` is deliberately discarded rather than reported, because
4856            // there is nothing useful left to say. These are 32 bytes of OS randomness that were
4857            // never transmitted to anyone, so a failed cleanup costs storage the host's
4858            // `Storage::sweep_expired` reclaims at the token's own expiry, and nothing else. The
4859            // Both artifacts are nameable directly here, which is cheaper and more precise than
4860            // asking the store for the family they belong to.
4861            self.undo_issuance(&issued.access_token).await;
4862            if let Some(rt) = &issued.refresh_token {
4863                let _ = self.store.take_refresh_token(rt).await;
4864            }
4865            // The two outcomes are answered DIFFERENTLY, because they are different facts about
4866            // the deployment and the wire code is what a host's dashboards count.
4867            //
4868            // `Ok(false)` is not a failure of this server: the swap did exactly its job. The grant
4869            // was replayed or revoked while this redemption was in flight, so `invalid_grant` is
4870            // the true answer and a `server_error` would send an operator looking for a storage
4871            // fault that never happened.
4872            return Err(match recorded {
4873                Ok(_) => ErrorResponse::new(ErrorCode::InvalidGrant).with_description(
4874                    "this authorization code was replayed or revoked during redemption",
4875                ),
4876                Err(_) => ErrorResponse::new(ErrorCode::ServerError)
4877                    .with_description("could not record the redemption"),
4878            });
4879        }
4880
4881        Ok(issued)
4882    }
4883
4884    /// RFC 6749 section 4.4: the client acts on its own behalf, with no resource owner.
4885    async fn client_credentials_token(
4886        &self,
4887        client_id: &ClientId,
4888        bound: &Bound<'_>,
4889        requested_scope: Option<&ScopeSet>,
4890        resource: Vec<String>,
4891        details: GrantedDetails,
4892    ) -> Result<TokenResponse, ErrorResponse> {
4893        // STAMPED BEFORE THE REGISTRATION IS READ, and that ordering is the whole point.
4894        //
4895        // This grant has no resource owner, so the client's own authentication IS the decision and
4896        // this is the instant it dates from. Taking it AFTER `authenticate_client` would date the
4897        // grant later than the read it derives from, and a `delete_client` landing in between —
4898        // one `get_client` round trip, plus a secret verification, plus on the assertion path a
4899        // JWT verify and a replay claim — would then record a barrier EARLIER than the grant it is
4900        // supposed to refuse. The write would be applied and a live token minted for a
4901        // registration that no longer exists.
4902        //
4903        // That is the exact resurrection the barrier exists to stop, and it was introduced by the
4904        // 0.9.1 audit fix that made barriers compare instants at all: refusing on identity alone
4905        // had closed it for free. Found by auditing that fix rather than by any test.
4906        let grant_established_at = self.clock.now();
4907        let client = self.authenticate_client(client_id, &bound.cred).await?;
4908        // RFC 6749 section 4.4: this grant is for confidential clients. A public client has no
4909        // secret, so "the client itself" is not an identity anyone has proven.
4910        //
4911        // BARE, for the reason spelled out at the introspection twin of this check: a description
4912        // here would be the only response on the endpoint that distinguishes a registered public
4913        // client from an unknown id, and the credential path just above collapses exactly that
4914        // distinction. Both sites were changed together because the introspection comment cites
4915        // this one as "the same refusal, for the same reason", and a fix applied to one of a pair
4916        // that call each other authority is how a rule ends up living in two places with two
4917        // answers. The operator's sentence is `ClientAuthFailure::NotConfidential`.
4918        if matches!(client.auth, crate::client::ClientAuth::Public) {
4919            self.hooks.emit(|| Event::ClientAuthenticationFailed {
4920                client_id: client_id.as_str(),
4921                failure: ClientAuthFailure::NotConfidential,
4922            });
4923            return Err(ErrorResponse::new(ErrorCode::InvalidClient));
4924        }
4925        if !client.allows_grant(GrantType::ClientCredentials) {
4926            return Err(ErrorResponse::new(ErrorCode::UnauthorizedClient));
4927        }
4928        let scope = Self::resolve_scope(&client, requested_scope)?;
4929        // Section 4.4.3: a refresh token SHOULD NOT be included. The client holds its own
4930        // credentials and can mint another token whenever it likes, so a refresh token would be a
4931        // second long-lived secret bought for nothing.
4932        // RFC 8707 s2: there is no prior authorization request here, so there is nothing to
4933        // narrow AGAINST. The client authenticated as itself and is naming the resource server it
4934        // means to call, which is the whole of what the parameter says in this grant.
4935        self.issue_boxed(
4936            &client,
4937            bound,
4938            GrantType::ClientCredentials,
4939            // Read at request ENTRY, above, not here. See the comment there.
4940            grant_established_at,
4941            None,
4942            scope,
4943            resource,
4944            // RFC 9396 s6: there is no prior authorization request here, so there is nothing
4945            // to narrow AGAINST. The client authenticated as itself and is naming what it
4946            // means to do, which is the whole of what the parameter says in this grant; the
4947            // s5 type check has already run at the endpoint.
4948            details,
4949            None,
4950            false,
4951            // RFC 6749 s4.4 has no resource owner, so there is no user authentication to report.
4952            GrantedAuthentication::default(),
4953            // No actor: this grant delegates nothing (RFC 8693 s4.1).
4954            GrantedActor::default(),
4955            // No ceiling: this grant derives from the client's own live credentials and from no
4956            // earlier token, so there is nothing for its lifetime to be capped against.
4957            None,
4958        )
4959        .await
4960    }
4961
4962    /// RFC 8628 sections 3.4/3.5: one device-token poll.
4963    async fn device_token(
4964        &self,
4965        client_id: &ClientId,
4966        bound: &Bound<'_>,
4967        device_code: &str,
4968    ) -> Result<TokenResponse, ErrorResponse> {
4969        let client = self.authenticate_client(client_id, &bound.cred).await?;
4970        if !client.allows_grant(GrantType::DeviceCode) {
4971            return Err(ErrorResponse::new(ErrorCode::UnauthorizedClient));
4972        }
4973
4974        let mut grant = self
4975            .store
4976            .get_device_grant(device_code)
4977            .await
4978            .map_err(storage_error)?
4979            .ok_or_else(|| ErrorResponse::new(ErrorCode::InvalidGrant))?;
4980
4981        // A device_code was issued to exactly one client; anyone else presenting it holds a grant
4982        // that was not made to them (RFC 6749 section 5.2 `invalid_grant`). The grant is NOT
4983        // consumed: a stray or malicious cross-client poll must not break the real device.
4984        if grant.client_id != client.client_id {
4985            return Err(ErrorResponse::new(ErrorCode::InvalidGrant));
4986        }
4987
4988        let now = self.clock.now();
4989
4990        // Expiry first (RFC 8628 section 3.5 `expired_token`), and the grant is removed: the code
4991        // can never become valid again, and later polls report plain `invalid_grant`.
4992        if now >= grant.expires_at {
4993            let _ = self.store.take_device_grant(device_code).await;
4994            return Err(ErrorResponse::new(ErrorCode::ExpiredToken));
4995        }
4996
4997        // Poll pacing. Too-fast polls get `slow_down`, and the REQUIRED spacing grows by the
4998        // configured increment (the RFC directs the client to add 5 seconds; the server tracks the
4999        // same number so it can hold the client to it). The window also restarts at this poll:
5000        // hammering does not drain the wait.
5001        //
5002        // BOTH WRITES BELOW ARE COMPARE-AND-SWAPS, and this is the argued part. A poll and the
5003        // user's decision at the verification UI are two different actors on one record, and the
5004        // poll's write is derived from a read that happened one or more storage round trips ago.
5005        // Blind-putting that snapshot back reverts an approval the user really gave, or, worse, a
5006        // DENIAL: the verification UI has already told the user their refusal was recorded, the
5007        // grant is `Pending` again, and nothing anywhere reports an error.
5008        //
5009        // The trade is deliberate and it is not symmetric. A poll writes exactly two fields,
5010        // `interval` and `last_poll_at`, and losing them costs at most one extra `slow_down` on the
5011        // next poll. Losing a DECISION is losing something a human did. So a missed swap here is
5012        // NOT retried and NOT an error: the poll simply declines to write, the decision stands, and
5013        // the device reads it on its next poll a few seconds later. That is the whole design rule,
5014        // and it is why this is a compare-and-swap rather than a re-read-and-merge: re-reading
5015        // narrows the window, it does not close it, and the decision must not be losable at all.
5016        if let Some(last) = grant.last_poll_at {
5017            // `checked_add`, not `+`: `SystemTime + Duration` PANICS on overflow, and
5018            // `grant.interval` is grown by the poll rate below against a host-set increment that
5019            // nothing validates. `None` means the deadline is past any representable instant, so
5020            // the client is unconditionally too early.
5021            // `map_or(true, ..)` rather than `is_none_or`, which is stable only since 1.82 while
5022            // this crate's MSRV is 1.75. The clippy lint that prefers `is_none_or` does not know
5023            // that, so it is silenced here rather than obeyed.
5024            #[allow(clippy::unnecessary_map_or)]
5025            if last
5026                .checked_add(grant.interval)
5027                .map_or(true, |next| now < next)
5028            {
5029                let expected = grant.state.clone();
5030                // `saturating_add`: `Duration: AddAssign` panics on overflow, and this
5031                // accumulator is paced by the client's own polling against a host-set increment.
5032                // A saturated interval is a client that must wait longer than the grant lives,
5033                // which is the same refusal by a different route.
5034                grant.interval = grant
5035                    .interval
5036                    .saturating_add(self.config.slow_down_increment);
5037                grant.last_poll_at = Some(now);
5038                // A genuine storage FAILURE is still fatal, exactly as it was before: only losing
5039                // the race is tolerated. The `slow_down` answer stands either way, because the
5040                // pacing verdict was computed from a real read of a real record.
5041                self.store
5042                    .compare_and_swap_device_grant(&expected, grant)
5043                    .await
5044                    .map_err(storage_error)?;
5045                return Err(ErrorResponse::new(ErrorCode::SlowDown));
5046            }
5047        }
5048
5049        // Cloned before `grant` is moved, and no more often than the `match grant.state.clone()`
5050        // this replaced: the branch arms need the state and the swap needs it as the expectation.
5051        let state = grant.state.clone();
5052        grant.last_poll_at = Some(now);
5053
5054        match state {
5055            DeviceGrantState::Pending => {
5056                self.store
5057                    .compare_and_swap_device_grant(&DeviceGrantState::Pending, grant)
5058                    .await
5059                    .map_err(storage_error)?;
5060                Err(ErrorResponse::new(ErrorCode::AuthorizationPending))
5061            }
5062            DeviceGrantState::Denied => {
5063                // Terminal answer, delivered once; the grant is consumed with it.
5064                let _ = self.store.take_device_grant(device_code).await;
5065                Err(ErrorResponse::new(ErrorCode::AccessDenied))
5066            }
5067            DeviceGrantState::Approved { subject } => {
5068                // Single use: redemption goes through the atomic take, so a concurrent double
5069                // poll can only mint one token; the loser sees `invalid_grant`.
5070                let taken = self
5071                    .store
5072                    .take_device_grant(device_code)
5073                    .await
5074                    .map_err(storage_error)?
5075                    .ok_or_else(|| ErrorResponse::new(ErrorCode::InvalidGrant))?;
5076                // No resource: the device authorization request does not carry one (see
5077                // `token_with_resources`), and the poll above refuses any the client sends.
5078                self.issue_boxed(
5079                    &client,
5080                    bound,
5081                    GrantType::DeviceCode,
5082                    // The instant the DEVICE ASKED, which is NOT the instant the user approved.
5083                    // RFC 8628 s3.3 approval happens at the host's own verification UI and
5084                    // `DeviceGrantState::Approved` records only the subject, so the approval
5085                    // instant is never persisted and `created_at` is the closest thing that
5086                    // exists. It is EARLIER than the decision, never later, so a barrier recorded
5087                    // in between refuses this grant rather than admitting it: fail-closed, at the
5088                    // cost of a user who re-approves after withdrawing consent having to restart
5089                    // the device flow. See `IssuedToken::grant_established_at`, which states the
5090                    // window in full.
5091                    taken.created_at,
5092                    Some(subject),
5093                    taken.scope,
5094                    Vec::new(),
5095                    // No details: the device authorization request does not carry them and
5096                    // the poll above refuses any the client sends.
5097                    GrantedDetails::default(),
5098                    None,
5099                    true,
5100                    // The device grant carries no authentication report: RFC 8628 s3.3 approval
5101                    // happens at the host's own verification UI, which is where the report would
5102                    // have to be taken, and inventing one here would be this server asserting
5103                    // something it never witnessed.
5104                    GrantedAuthentication::default(),
5105                    // No actor: this grant delegates nothing (RFC 8693 s4.1).
5106                    GrantedActor::default(),
5107                    // No ceiling: RFC 8628 approval starts a grant, exactly as a code redemption
5108                    // does, so the ordinary `access_token_ttl` is the whole rule.
5109                    None,
5110                )
5111                .await
5112            }
5113        }
5114    }
5115
5116    /// RFC 6749 section 6 with OAuth 2.1 rotation: redeem a refresh token, single use.
5117    async fn refresh_token(
5118        &self,
5119        client_id: &ClientId,
5120        bound: &Bound<'_>,
5121        refresh_token: &str,
5122        requested_scope: Option<&ScopeSet>,
5123        requested_resources: &[String],
5124        requested_details: GrantedDetails,
5125    ) -> Result<TokenResponse, ErrorResponse> {
5126        let client = self.authenticate_client(client_id, &bound.cred).await?;
5127        if !client.allows_grant(GrantType::RefreshToken) {
5128            return Err(ErrorResponse::new(ErrorCode::UnauthorizedClient));
5129        }
5130
5131        // Consume first (atomic): that is what makes redemption single use under concurrency.
5132        // Judging comes after, and every judgement below either puts the record back or has a
5133        // stated reason not to.
5134        let record = self
5135            .store
5136            .take_refresh_token(refresh_token)
5137            .await
5138            .map_err(storage_error)?
5139            .ok_or_else(|| ErrorResponse::new(ErrorCode::InvalidGrant))?;
5140
5141        // Presented by a client it was not issued to. The record goes BACK: the presenter proved
5142        // only that they hold a string, and destroying a live credential on that basis locks out
5143        // the client that legitimately holds it while costing the attacker nothing. Same reasoning
5144        // as the authorization code path, which has always put the record back on a mismatch.
5145        if record.client_id != client.client_id {
5146            self.restore_refresh_token(record).await?;
5147            return Err(ErrorResponse::new(ErrorCode::InvalidGrant));
5148        }
5149
5150        // REUSE. This token was already rotated away, so two parties hold it, and the AS has just
5151        // been handed unambiguous evidence of that. OAuth 2.1 draft section 6.1 and RFC 9700
5152        // section 4.14.2: invalidate the presented token AND revoke the tokens issued for that
5153        // authorization grant. Refusing the presentation alone would be the defence inverted,
5154        // because the party who presents the superseded token is by definition the one who did NOT
5155        // redeem it first, which in a theft is the victim.
5156        //
5157        // When the revocation SUCCEEDS it removes every record carrying this id, including this
5158        // one, so there is nothing to put back. When it fails there is, and that is the whole of
5159        // the paragraph below.
5160        if record.state == RefreshTokenState::Spent {
5161            // THE STORE FAILING HERE MUST NOT PROPAGATE, and the reason is the same one the
5162            // authorization-code replay path states above: the wire cannot carry this news. The
5163            // answer to a reused refresh token is `invalid_grant` however badly the store is
5164            // behaving, so the AUDIT EVENT is the only signal a deployment gets. Returning the
5165            // storage error instead lost all three halves of the response at once — the family
5166            // was not revoked, so the thief's rotated chain stayed live; the `Spent` record had
5167            // ALREADY been removed by `take_refresh_token` above and was never put back, so RFC
5168            // 9700 section 4.14.2 reuse detection for that family was off from then on and a later
5169            // presentation of the same string read as an unknown token; and no event fired at all,
5170            // so the host's only audit channel was never told any of it.
5171            let mut containment_failed = false;
5172            // MOVED out of the record before the `Err` arm below can move the record itself back
5173            // into the store. One allocation, on the detected-compromise path only.
5174            let family_id = record.family_id.clone();
5175            let mut records_revoked = 0;
5176            match self
5177                .store
5178                .revoke_token_family(&family_id, self.revocation_window())
5179                .await
5180            {
5181                Ok(revoked) => records_revoked = revoked,
5182                Err(_) => {
5183                    containment_failed = true;
5184                    // THE ALARM STAYS ARMED. The record goes back exactly as it was read, still
5185                    // `Spent`, because it is the evidence: it is what makes the NEXT presentation
5186                    // of this string detectable as reuse rather than as an unknown token. Its own
5187                    // outcome is not inspected because `containment_failed` is already true —
5188                    // the revocation failing is the compromise this event has to report, and a
5189                    // refused or failed restore only adds to a story that is already the bad one.
5190                    let _ = self.store.put_refresh_token(record).await;
5191                }
5192            }
5193            // EVIDENCE OF COMPROMISE, and the event most likely to be asked about after the fact:
5194            // the family revocation also logs out the legitimate client. It is emitted on BOTH
5195            // outcomes. A reuse that could not be contained is the more urgent of the two, not the
5196            // one worth staying quiet about, and `containment_failed` is what stops the event
5197            // claiming a containment that did not happen.
5198            self.hooks.emit(|| Event::RefreshTokenReuseDetected {
5199                client_id: client.client_id.as_str(),
5200                family_id: &family_id,
5201                records_revoked,
5202                containment_failed,
5203            });
5204            return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
5205                .with_description("refresh token reuse detected; the grant has been revoked"));
5206        }
5207
5208        // RFC 9449 s5: a refresh chain issued to a DPoP-bound grant stays bound to the SAME key,
5209        // and a rotation has to prove possession of it. Without this the binding would be
5210        // decorative past the first access token: a stolen refresh token could simply be re-bound
5211        // to the thief's key on the next rotation, leaving the attacker holding a token they can
5212        // prove possession for while the victim's key is the one refused. The record goes BACK, as
5213        // for every other judgement here that is not evidence of compromise.
5214        #[cfg(feature = "dpop")]
5215        if record.jkt.as_deref() != bound.jkt {
5216            self.restore_refresh_token(record).await?;
5217            return Err(ErrorResponse::new(ErrorCode::InvalidDpopProof)
5218                .with_description("this refresh token is bound to a different DPoP key"));
5219        }
5220
5221        // RFC 8705 s3, and word for word the same argument as the DPoP check above: a chain
5222        // issued over a client certificate stays bound to THAT certificate, and a rotation
5223        // has to present it again. Without this the binding is decorative past the first
5224        // access token, because a stolen refresh token could be re-bound to the thief's own
5225        // certificate on the next rotation. Section 3 makes it a MUST for public clients
5226        // specifically; applying it to every bound chain costs a confidential mutual-TLS
5227        // client nothing, since it presents that certificate on every request anyway.
5228        #[cfg(feature = "mtls")]
5229        if record.x5t_s256.as_deref() != bound.cred.certificate.map(|c| c.thumbprint()) {
5230            self.restore_refresh_token(record).await?;
5231            return Err(
5232                ErrorResponse::new(ErrorCode::InvalidGrant).with_description(
5233                    "this refresh token is bound to a different client certificate",
5234                ),
5235            );
5236        }
5237
5238        if let Some(expires_at) = record.expires_at {
5239            if self.clock.now() >= expires_at {
5240                // Not put back: an expired chain can never become valid again, and keeping it
5241                // would only be storage the host has to sweep.
5242                return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
5243                    .with_description("refresh token chain expired"));
5244            }
5245        }
5246
5247        // THE REGISTRATION'S CEILING, RE-APPLIED AT EVERY ROTATION.
5248        //
5249        // `Client::allowed_scopes` is documented as "the scopes this client may ever be granted",
5250        // and until the 0.9.1 audit a refresh chain was the one place that promise was not kept:
5251        // the only ceiling here was `record.scope`, so an operator who discovered a client should
5252        // never have held `payments:write` and narrowed the registration kept minting it on every
5253        // rotation. With `refresh_token_ttl` defaulting to `None` — no expiry at all — "may ever
5254        // be granted" was, by default, "may be granted forever regardless of what the registration
5255        // now says". Only `delete_client` or a per-family revocation the operator has no list of
5256        // actually stopped it.
5257        //
5258        // A code or a device grant has the same gap for its own lifetime, but those are 60 and 600
5259        // seconds and self-closing; a chain is not, which is why the check lands here.
5260        //
5261        // Refusing rather than silently intersecting is the fail-closed direction and the honest
5262        // one: the client asked to continue a grant this server is no longer willing to honour, and
5263        // quietly handing back a narrower token would look to the client like the grant it had. The
5264        // record is NOT put back — unlike the widening attempt below, this is not a retryable client
5265        // mistake, and leaving the chain alive would mean answering `invalid_scope` forever while
5266        // the credential stays valid.
5267        if !record.scope.is_subset(&client.allowed_scopes) {
5268            return Err(
5269                ErrorResponse::new(ErrorCode::InvalidScope).with_description(
5270                    "this grant carries scopes the client's registration no longer allows",
5271                ),
5272            );
5273        }
5274
5275        // Narrowing only (RFC 6749 section 6: scope must not include any scope not originally
5276        // granted). A widening attempt is a client bug, not a compromise: put the record back so
5277        // the mistake is retryable.
5278        let scope = match requested_scope {
5279            None => record.scope.clone(),
5280            Some(s) if s.is_subset(&record.scope) => s.clone(),
5281            Some(_) => {
5282                self.restore_refresh_token(record).await?;
5283                return Err(ErrorResponse::new(ErrorCode::InvalidScope)
5284                    .with_description("refresh may narrow scope, never widen it"));
5285            }
5286        };
5287
5288        // RFC 8707 s2, the same narrowing rule as the scope rule immediately above and refused the
5289        // same way: the record goes back so a client that asked for the wrong resource can retry.
5290        // RFC 8707 s2 and RFC 9396 s6, one fallible expression and one error path, for the
5291        // reason the authorization code grant gives above. The chain carries what the
5292        // PREVIOUS leg narrowed to, so a client that narrowed once cannot climb back on the
5293        // next rotation; the record goes back either way, because a widening attempt here is
5294        // a client bug and not evidence of compromise.
5295        let narrowed = self
5296            .narrow_and_permit(&record.resource, requested_resources)
5297            .and_then(|r| {
5298                GrantedDetails::of_refresh(&record)
5299                    .narrow(&requested_details)
5300                    .map(|d| (r, d))
5301            });
5302        let (resource, details) = match narrowed {
5303            Ok(narrowed) => narrowed,
5304            Err(e) => {
5305                self.restore_refresh_token(record).await?;
5306                return Err(e);
5307            }
5308        };
5309
5310        // Retain the rotated token, marked spent, exactly as the authorization code path retains a
5311        // consumed code and for the same reason: a deleted token makes a later presentation
5312        // indistinguishable from an unknown string, and reuse detection is then impossible. A
5313        // chain with no absolute expiry gets a retention deadline here, so the record is
5314        // reclaimable by `Storage::sweep_expired` rather than immortal.
5315        //
5316        // THIS WRITE HAPPENS BEFORE ISSUANCE, AND THE ORDER IS A SECURITY PROPERTY RATHER THAN A
5317        // STYLE CHOICE. `Storage` deliberately has no transaction (see the trait's own docs: a host
5318        // may be backing this with anything from a HashMap to a sharded KV store, and requiring
5319        // cross-key atomicity would exclude most of them), so the atomic take above and this write
5320        // CANNOT be made one operation. All that can be chosen is which way the pair fails.
5321        //
5322        // Issuing first and marking spent afterwards fails OPEN. The take has already removed this
5323        // token; if anything in issuance or in this write then fails, the token is gone with NO
5324        // spent record, so a later presentation of it reads as an unknown string rather than as
5325        // reuse. RFC 9700 section 4.14.2 detection is then off for this family, permanently and
5326        // silently, at exactly the moment the deployment's storage is misbehaving, which is when a
5327        // compromise is most likely to go unnoticed. The freshly minted tokens meanwhile stay live
5328        // and orphaned, because the caller is answered with an error and never sees them.
5329        //
5330        // Marking spent first fails CLOSED, and that is the right trade. If this write fails,
5331        // nothing has been minted and the client re-authenticates. If it succeeds and issuance then
5332        // fails, the client is locked out of this chain and re-authenticates, and the alarm is
5333        // ARMED: the next presentation of this token is recognised as reuse and revokes the family.
5334        // Locking a client out is an inconvenience it can recover from without help; a
5335        // compromise-detection capability going offline is not.
5336        let chain_expires_at = record.expires_at;
5337        // Read off the record BEFORE it is moved into the spent value below. These are the same
5338        // three clones the issuance took when it ran first, so the ordering change costs nothing.
5339        let subject = record.subject.clone();
5340        let family_id = record.family_id.clone();
5341        let authentication = GrantedAuthentication::from_refresh(&record);
5342        // Read BEFORE `record` is consumed into `spent` below.
5343        let grant_established_at = record.grant_established_at;
5344        let spent = RefreshTokenRecord {
5345            state: RefreshTokenState::Spent,
5346            // `saturating_deadline` rather than `+`: `refresh_reuse_window` is a plain public
5347            // field with no validating constructor, and this branch is the DEFAULT
5348            // configuration's (`refresh_token_ttl: None` means the chain has no absolute expiry),
5349            // so every rotation in such a deployment performs this addition on a request path.
5350            expires_at: chain_expires_at.or_else(|| {
5351                Some(saturating_deadline(
5352                    self.clock.now(),
5353                    self.config.refresh_reuse_window,
5354                ))
5355            }),
5356            ..record
5357        };
5358        // NOT `restore_refresh_token`: this write is not a restoration and a refusal here is not
5359        // benign. The spent marker is what arms reuse detection for the token about to be minted,
5360        // so if a revocation has reached this family the rotation must STOP rather than continue
5361        // to issuance. Continuing would mint a chain from a grant that was revoked a moment ago,
5362        // and would do it with the alarm disarmed.
5363        if self
5364            .store
5365            .put_refresh_token(spent)
5366            .await
5367            .map_err(storage_error)?
5368            .is_refused()
5369        {
5370            return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
5371                .with_description("the grant was revoked while this token was being refreshed"));
5372        }
5373
5374        self.issue_boxed(
5375            &client,
5376            bound,
5377            GrantType::RefreshToken,
5378            // CARRIED from the chain, never restamped. Restamping here would let a chain walk
5379            // forward past the revocation that killed the decision it descends from.
5380            grant_established_at,
5381            subject,
5382            scope,
5383            resource,
5384            details,
5385            Some(RefreshChain {
5386                family_id,
5387                expires_at: chain_expires_at,
5388            }),
5389            true,
5390            authentication,
5391            // A rotation carries no actor of its own: nothing was delegated by refreshing.
5392            GrantedActor::default(),
5393            // No ceiling from the access token being replaced: the CHAIN's own absolute expiry is
5394            // what bounds a refresh grant's total lifetime, and it is applied to the rotated
5395            // refresh token above rather than to the access token here.
5396            None,
5397        )
5398        .await
5399    }
5400
5401    /// Mint and persist an access token (and, when configured, a rotated refresh token).
5402    ///
5403    /// `chain`: `None` starts a NEW family (a fresh grant); `Some(_)` continues an existing one,
5404    /// keeping both its family id and its absolute lifetime, which is what makes rotation a chain
5405    /// rather than a sequence of unrelated tokens.
5406    // Eight arguments rather than seven, because the audit event has to name the grant that
5407    // produced the token and `issue` is the only place that sees the whole issuance. Bundling them
5408    // into a struct would be churn for its own sake on a private function with four call sites.
5409    #[allow(clippy::too_many_arguments)]
5410    /// [`AuthorizationServer::issue`] behind one heap allocation.
5411    ///
5412    /// Every caller of `issue` goes through here, and the reason is a measurement rather than a
5413    /// preference. `issue` is the widest frame on the token path: it holds a whole `IssuedToken`
5414    /// and a whole `RefreshTokenRecord` across its storage awaits, and because a generator's size
5415    /// is the MAXIMUM over all of its states, that width is paid by every token request including
5416    /// the polls and refusals that never issue anything. Inlined, it puts the token future over
5417    /// tokio's 2048-byte debug boxing threshold as soon as `dpop` adds a binding to both records,
5418    /// and tokio's answer to that is to box the WHOLE token future on every single request.
5419    ///
5420    /// So: one allocation, paid only when a token is actually issued, instead of one allocation the
5421    /// size of the entire token future paid on every request that reaches this endpoint. The
5422    /// allocation gates in `tests/allocation.rs` are what settled this, and they measure both.
5423    ///
5424    /// # RE-MEASURED after 0.9.0, and KEPT
5425    ///
5426    /// The two other `Box::pin`s on this path (the RFC 9449 proof check and the RFC 7523 assertion
5427    /// check) were both removed at that point, because measuring them showed the future was byte
5428    /// for byte identical with and without them: they had stopped buying anything. This one had
5429    /// not. Inlined, the `--all-features` token future goes from 1344 bytes to 1608, leaving 440
5430    /// bytes of headroom against tokio's 2048 rather than 704.
5431    ///
5432    /// That trade is REFUSED, and the number is why. What removing it would save is one allocation
5433    /// per token ISSUED, out of the 39 a code redemption already costs. What it would spend is 264
5434    /// bytes of the margin against a threshold this crate has crossed twice already, once for 120
5435    /// bytes and once for 344; a future feature the size of `rar` would put it over, and the
5436    /// failure mode on the other side of that line is a 2 KB heap allocation on every single token
5437    /// request, refusals included. Two and a half percent off the issuance path is not worth
5438    /// spending a third of the headroom that keeps the whole endpoint off tokio's slow path.
5439    #[allow(clippy::too_many_arguments)]
5440    fn issue_boxed<'a>(
5441        &'a self,
5442        client: &'a Client,
5443        bound: &'a Bound<'_>,
5444        grant_type: GrantType,
5445        // The instant the GRANT behind this issuance was authorized: the code's mint, the device
5446        // approval, or the instant carried forward from the chain. NOT `now`. See
5447        // `crate::token::IssuedToken::grant_established_at`.
5448        grant_established_at: SystemTime,
5449        subject: Option<String>,
5450        scope: ScopeSet,
5451        resource: Vec<String>,
5452        details: GrantedDetails,
5453        chain: Option<RefreshChain>,
5454        allow_refresh: bool,
5455        authentication: GrantedAuthentication,
5456        actor: GrantedActor,
5457        lifetime_ceiling: Option<SystemTime>,
5458    ) -> std::pin::Pin<
5459        Box<dyn std::future::Future<Output = Result<TokenResponse, ErrorResponse>> + Send + 'a>,
5460    > {
5461        Box::pin(self.issue(
5462            client,
5463            bound,
5464            grant_type,
5465            grant_established_at,
5466            subject,
5467            scope,
5468            resource,
5469            details,
5470            chain,
5471            allow_refresh,
5472            authentication,
5473            actor,
5474            lifetime_ceiling,
5475        ))
5476    }
5477
5478    #[allow(clippy::too_many_arguments)]
5479    pub(crate) async fn issue(
5480        &self,
5481        client: &Client,
5482        bound: &Bound<'_>,
5483        grant_type: GrantType,
5484        // The instant the GRANT behind this issuance was authorized: the code's mint, the device
5485        // approval, or the instant carried forward from the chain. NOT `now`. See
5486        // `crate::token::IssuedToken::grant_established_at`.
5487        grant_established_at: SystemTime,
5488        subject: Option<String>,
5489        scope: ScopeSet,
5490        resource: Vec<String>,
5491        details: GrantedDetails,
5492        chain: Option<RefreshChain>,
5493        allow_refresh: bool,
5494        authentication: GrantedAuthentication,
5495        actor: GrantedActor,
5496        // An ABSOLUTE CEILING on the issued access token's expiry, or `None` for the ordinary
5497        // `access_token_ttl` from now. `Some` exists for RFC 8693 token exchange, where the issued
5498        // token derives its authority from a subject token that is itself expiring: without a
5499        // ceiling the exchanged token is an ordinary access token, therefore an acceptable SUBJECT
5500        // token, therefore re-exchangeable just before each expiry for a fresh full TTL, forever.
5501        // That is a grant renewing its own lifetime without limit, which is exactly what this
5502        // grant refuses to issue a refresh token for.
5503        lifetime_ceiling: Option<SystemTime>,
5504    ) -> Result<TokenResponse, ErrorResponse> {
5505        // `bound` carries only the RFC 9449 key binding, so with that feature off it is genuinely
5506        // unused HERE. It stays in the signature regardless, so the four call sites do not have to
5507        // differ by feature: a call site that differs by feature is a call site that gets it wrong
5508        // under the configuration nobody builds locally.
5509        #[cfg(not(feature = "dpop"))]
5510        let _ = bound;
5511        // Same for the RFC 9470 authentication report: without `consent` the wrapper is empty and
5512        // genuinely unused here, and an unused parameter is a warning rather than a signature that
5513        // differs by feature.
5514        #[cfg(not(feature = "consent"))]
5515        let _ = authentication;
5516        // And the RFC 8693 actor, which only a delegation exchange ever fills.
5517        #[cfg(not(feature = "token-exchange"))]
5518        let _ = actor;
5519        // Likewise: without `rar` the details are a zero sized value nothing reads.
5520        #[cfg(not(feature = "rar"))]
5521        let _ = details;
5522        let now = self.clock.now();
5523        // ONE expiry instant, computed ONCE and used by all three places that state it: the
5524        // persisted record, the RFC 9068 `exp` claim, and the RFC 6749 s5.1 `expires_in` member. A
5525        // token whose signed `exp` disagrees with the expiry this server enforces on introspection
5526        // is worse than either bound on its own, because the two halves of the deployment then
5527        // disagree about when the token died.
5528        //
5529        // The ceiling can only ever SHORTEN the lifetime: `min` of the ordinary deadline and
5530        // whatever the caller capped it at.
5531        let expires_at = {
5532            let ordinary = saturating_deadline(now, self.config.access_token_ttl);
5533            match lifetime_ceiling {
5534                Some(ceiling) => ordinary.min(ceiling),
5535                None => ordinary,
5536            }
5537        };
5538
5539        let issues_refresh = allow_refresh
5540            && self.config.issue_refresh_tokens
5541            && client.allows_grant(GrantType::RefreshToken);
5542
5543        // ONE DRAW FOR ALL THREE ARTIFACTS. `getrandom::fill` is a syscall whose cost is almost
5544        // entirely per CALL rather than per byte (see `hex_encode`), and an issuance needed up to
5545        // three of them: a family id, an access token, and a refresh token. MEASURED at roughly
5546        // 1 us saved per issuance, which is about half of `client_credentials_issue` and a fifth of
5547        // an authorization code redemption.
5548        //
5549        // Scoped to a block with NO await inside it, deliberately. The buffer must not survive
5550        // across a suspension point or its 80 bytes join the coroutine's state, and this future is
5551        // held under tokio's 2048-byte debug boxing threshold by `tests/allocation.rs`. The refresh
5552        // token is therefore encoded HERE, before the buffer dies, and carried as an `Option<String>`
5553        // to its use below rather than being drawn there.
5554        //
5555        // Drawing 80 bytes when only 32 will be used is free: the syscall is the cost, and the
5556        // alternative (branching on `issues_refresh` before the draw) would buy nothing and add a
5557        // path where the unused half is not overwritten.
5558        let (family_id, access_token, pending_refresh) = {
5559            let mut entropy = [0u8; 80];
5560            // `ok_or_else` rather than `expect`: this is the token endpoint, every other
5561            // fallible step of which answers `server_error`, and a panic here aborts the host
5562            // in a host built with `panic = "abort"`. See `try_random_hex`.
5563            getrandom::fill(&mut entropy).map_err(|_| randomness_error())?;
5564            // The family id is minted (or inherited) BEFORE the access token, because the access
5565            // token has to carry it: RFC 9700 section 4.14.2 revokes the tokens of the whole grant
5566            // on detected reuse, and an access token with no family is unreachable from that event.
5567            // A grant that issues no refresh chain has no family, and allocates nothing for one:
5568            // there is no chain to reuse, so there is nothing a reuse detection could revoke.
5569            let family_id = match (&chain, issues_refresh) {
5570                (Some(c), _) => Some(c.family_id.clone()),
5571                (None, true) => Some(hex_encode(&entropy[..16])),
5572                (None, false) => None,
5573            };
5574            (
5575                family_id,
5576                hex_encode(&entropy[16..48]),
5577                issues_refresh.then(|| hex_encode(&entropy[48..])),
5578            )
5579        };
5580
5581        // Cloned ONLY when a sink is installed: both values are consumed by the records written
5582        // below, and an unobserved host must not pay two clones per issued token. BOXED because
5583        // this local lives across every await in the issuance, and the token future is 1936 bytes
5584        // against tokio's 2048-byte debug boxing threshold; 8 bytes here rather than 48 is 40
5585        // bytes of headroom for every caller, bought with one small allocation paid only by a
5586        // host that asked to be told about issuance.
5587        let audit = self
5588            .hooks
5589            .is_observed()
5590            .then(|| Box::new((subject.clone(), family_id.clone())));
5591
5592        // RFC 9068, when the host configured it: the WIRE token becomes a signed JWT and the
5593        // random string above becomes its `jti`. The record below is persisted either way, keyed
5594        // by whatever the client will actually present, so RFC 7662 introspection and RFC 7009
5595        // revocation keep working and a revoked JWT is genuinely dead at this AS rather than
5596        // merely deprecated.
5597        // Bound before it is matched rather than matched directly, because the signing input is
5598        // then visibly the LAST thing computed before the suspension. MEASURED: this makes no
5599        // difference to the frame either way (1704 bytes on `--all-features` both ways); it is
5600        // written for the reader, and the measurement is recorded so nobody has to repeat it.
5601        //
5602        // WHAT IS LIVE ACROSS THE AWAIT IS THE WHOLE ISSUANCE, and an earlier version of this
5603        // comment claimed otherwise. `now`, `issues_refresh`, `family_id`, `pending_refresh`,
5604        // `audit`, `subject`, `scope`, `resource`, `details`, `authentication`, `client` and
5605        // `bound` are all built above and read below, so they are all in the frame. The SIZE
5606        // claim is unaffected and is gated by `tests/allocation.rs`; the description was simply
5607        // wrong, and it matters because that await is the host's `Es256Signer` and is unbounded.
5608        #[cfg(feature = "jwt")]
5609        let prepared = self.access_token_signing_input(
5610            client,
5611            subject.as_deref(),
5612            &scope,
5613            &resource,
5614            &details,
5615            now,
5616            expires_at,
5617            access_token,
5618            bound,
5619            &actor,
5620            &authentication,
5621        )?;
5622        #[cfg(feature = "jwt")]
5623        let access_token = match prepared {
5624            Err(opaque) => opaque,
5625            // The ONE await the RFC 9068 path adds, and the only thing live across it is this
5626            // `String` and a borrow of the configuration. A signer that cannot sign mints NOTHING:
5627            // there is no fallback to an opaque token and no unsigned token, because an access
5628            // token this server could not sign is one it must not issue.
5629            Ok((jwt, input)) => jwt.finish_signing(input).await.map_err(|e| {
5630                let _ = e;
5631                ErrorResponse::new(ErrorCode::ServerError)
5632            })?,
5633        };
5634        let refused = self
5635            .store
5636            .put_token(IssuedToken {
5637                // RFC 9449 s6: the binding is recorded on the AS side too, not only in the token,
5638                // so that RFC 7662 introspection can report it and a resource server can check it
5639                // without having to parse a token this server may have issued as opaque.
5640                #[cfg(feature = "dpop")]
5641                jkt: bound.jkt.map(Box::from),
5642                // The record was written this way before there was anybody to read it, because the
5643                // record is the half that cannot be added later; a registered resource server
5644                // reads it now (`ServerConfig::resource_servers`).
5645                //
5646                // RFC 8705 s3, and the same argument as `jkt` immediately above: an opaque
5647                // token carries its binding nowhere else, so s3.2 introspection could not
5648                // report it if it were not written down here.
5649                #[cfg(feature = "mtls")]
5650                x5t_s256: bound.cred.certificate.map(|c| Box::new(*c.thumbprint())),
5651                access_token: access_token.clone(),
5652                client_id: client.client_id.clone(),
5653                subject: subject.clone(),
5654                scope: scope.clone(),
5655                resource: resource.clone(),
5656                // RFC 9396 s7: the details as granted, assigned to this access token. This
5657                // is what introspection (s9.2) reports and what the s9.1 JWT claim carries.
5658                #[cfg(feature = "rar")]
5659                authorization_details: details.clone().into_details(),
5660                issued_at: now,
5661                // The GRANT's instant, not this issuance's: a barrier is compared against it, and
5662                // a rotation writing at `now` must not thereby outlive the revocation that killed
5663                // the decision it descends from.
5664                grant_established_at,
5665                // Computed at the top of this function, so the record, the signed `exp` and the
5666                // `expires_in` below are one instant stated three times rather than three.
5667                expires_at,
5668                family_id: family_id.clone(),
5669                // RFC 8693 s4.1: the token records who authority was delegated TO, so that RFC
5670                // 7662 introspection can report it. An opaque token carries it nowhere else.
5671                #[cfg(feature = "token-exchange")]
5672                act: actor.act.clone(),
5673                // RFC 9470 s6.2: the token reports the authentication behind it, so introspection can
5674                // answer the question the resource server's challenge asked.
5675                #[cfg(feature = "consent")]
5676                authentication: authentication.authentication.clone(),
5677            })
5678            .await
5679            .map_err(storage_error)?;
5680        // The grant was revoked while this issuance was in flight, most likely across the signing
5681        // await immediately above, which is a network round trip when the host's `Es256Signer`
5682        // fronts a KMS. Nothing was written, so there is nothing to undo; the client is told its
5683        // grant is invalid, which by now it is.
5684        if refused.is_refused() {
5685            return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
5686                .with_description("the grant was revoked while this token was being issued"));
5687        }
5688
5689        let refresh_token = if issues_refresh {
5690            let expires_at = match &chain {
5691                Some(c) => c.expires_at,
5692                None => self
5693                    .config
5694                    .refresh_token_ttl
5695                    .map(|ttl| saturating_deadline(now, ttl)),
5696            };
5697            // Drawn in the single `getrandom` call at the top of this function; `issues_refresh`
5698            // is what decided both that draw and this branch, so the value is always present.
5699            let rt = pending_refresh.expect("issues_refresh decided both");
5700            let refused = self
5701                .store
5702                .put_refresh_token(RefreshTokenRecord {
5703                    // RFC 9449 s5: the chain remembers the key it was issued to, and rotation
5704                    // checks it. See the check in `refresh_token`.
5705                    #[cfg(feature = "dpop")]
5706                    jkt: bound.jkt.map(Box::from),
5707                    // RFC 8705 s3: the chain remembers the certificate it was issued to, and
5708                    // rotation checks it. See the check in `refresh_token`.
5709                    #[cfg(feature = "mtls")]
5710                    x5t_s256: bound.cred.certificate.map(|c| Box::new(*c.thumbprint())),
5711                    refresh_token: rt.clone(),
5712                    client_id: client.client_id.clone(),
5713                    subject,
5714                    scope: scope.clone(),
5715                    // The chain remembers what it may narrow from on the next rotation.
5716                    resource,
5717                    #[cfg(feature = "rar")]
5718                    authorization_details: details.clone().into_details(),
5719                    expires_at,
5720                    // CARRIED, never restamped: see `RefreshTokenRecord::grant_established_at`.
5721                    grant_established_at,
5722                    // Present whenever a refresh token is: `issues_refresh` is what decided both.
5723                    family_id: family_id.unwrap_or_default(),
5724                    state: RefreshTokenState::Active,
5725                    // Carried, never restamped: see `RefreshTokenRecord::authentication`.
5726                    #[cfg(feature = "consent")]
5727                    authentication: authentication.authentication,
5728                })
5729                .await
5730                .map_err(storage_error)?;
5731            // Revoked BETWEEN the two writes. This is the case that needs undoing rather than
5732            // merely refusing: the access token a few lines above is already in the store, and
5733            // leaving it there would hand the caller a live credential minted from a grant that no
5734            // longer exists, which is the resurrection defect wearing a different hat.
5735            if refused.is_refused() {
5736                self.undo_issuance(&access_token).await;
5737                return Err(ErrorResponse::new(ErrorCode::InvalidGrant)
5738                    .with_description("the grant was revoked while this token was being issued"));
5739            }
5740            Some(rt)
5741        } else {
5742            None
5743        };
5744
5745        // Emitted after BOTH records are persisted, so the event describes a token that exists.
5746        if let Some(audit) = &audit {
5747            self.hooks.emit(|| Event::TokenIssued {
5748                client_id: client.client_id.as_str(),
5749                grant_type,
5750                subject: audit.0.as_deref(),
5751                scope: &scope,
5752                family_id: audit.1.as_deref(),
5753                refresh_issued: refresh_token.is_some(),
5754            });
5755        }
5756
5757        Ok(TokenResponse {
5758            access_token,
5759            // RFC 9449 s5: a token bound to a proof key is a `DPoP` token and not a `Bearer` one,
5760            // and the difference is exactly what tells the client, and any resource server reading
5761            // the response, that the token must be presented with a proof.
5762            #[cfg(feature = "dpop")]
5763            token_type: match bound.jkt {
5764                Some(_) => TokenType::Dpop,
5765                None => TokenType::Bearer,
5766            },
5767            #[cfg(not(feature = "dpop"))]
5768            token_type: TokenType::Bearer,
5769            // RFC 6749 s5.1: the lifetime IN SECONDS FROM NOW of the token just issued, derived
5770            // from the same `expires_at` the record and the signed `exp` carry. Not
5771            // `access_token_ttl`, which is only the same number when nothing capped the lifetime;
5772            // reporting the uncapped TTL for a capped token would have the client keep using a
5773            // token this server has already stopped honouring.
5774            //
5775            // `unwrap_or_default` is the fail-closed direction: a ceiling already in the past
5776            // yields zero rather than an underflow, and zero tells the client the token is spent.
5777            expires_in: expires_at.duration_since(now).unwrap_or_default().as_secs(),
5778            refresh_token,
5779            scope: (!scope.is_empty()).then(|| scope.to_string()),
5780            // RFC 9396 s7: what was GRANTED, from the same value that reaches the stored record
5781            // and the signed token, so the three cannot disagree about what this token authorizes.
5782            #[cfg(feature = "rar")]
5783            authorization_details: details.into_details(),
5784        })
5785    }
5786
5787    /// Opaque-token introspection: `Ok(Some(_))` only for a known, unexpired token.
5788    ///
5789    /// This is the host-facing form, which hands back the whole record. The RFC 7662 WIRE form is
5790    /// [`AuthorizationServer::introspection_response`], which answers the reduced, caller-scoped
5791    /// document the RFC defines.
5792    pub async fn introspect(
5793        &self,
5794        access_token: &str,
5795    ) -> Result<Option<std::sync::Arc<IssuedToken>>, StorageError> {
5796        Ok(self
5797            .store
5798            .get_token(access_token)
5799            .await?
5800            .filter(|t| self.clock.now() < t.expires_at))
5801    }
5802
5803    /// RFC 7662 token introspection, as the protected endpoint the RFC describes.
5804    ///
5805    /// The caller must authenticate (section 2.1), and a token the caller has no relationship to
5806    /// reads as inactive rather than as a description of somebody else's grant: section 2.2 says
5807    /// the response for an invalid token is simply `active: false`, and section 4 warns that this
5808    /// endpoint otherwise becomes an oracle for probing tokens a caller does not hold.
5809    ///
5810    /// # The two callers
5811    ///
5812    /// Section 1 names a protected resource as the primary consumer, and section 2.1 permits a
5813    /// client to introspect its own token. Both are served:
5814    ///
5815    /// - the token's OWN CLIENT, which sees the whole record; and
5816    /// - a RESOURCE SERVER declared in [`ServerConfig::resource_servers`], which sees a token only
5817    ///   when the token's RFC 8707 [`IssuedToken::resource`] set names one of the identifiers that
5818    ///   resource server is registered for.
5819    ///
5820    /// Everything else is `{"active": false}`, including a live token belonging to another client
5821    /// and addressed to another resource server. A deployment that registers no resource servers
5822    /// answers the token's own client and nobody else, which is what this server did through
5823    /// 0.9.1.
5824    ///
5825    /// The resource server is not a new kind of principal and gets no new credential: it registers
5826    /// as an ordinary confidential client and authenticates here exactly as any client does. See
5827    /// [`ServerConfig::resource_servers`] for why the authorization half is not optional -- and
5828    /// for what a resource server's traffic costs the client-authentication rate limit, which is
5829    /// the one thing about this endpoint that a host has to size rather than accept. A resource
5830    /// server calls it once per request at the protected resource, and the default budget was
5831    /// derived from a client's token traffic.
5832    ///
5833    /// # What a resource server is not told
5834    ///
5835    /// RFC 7662 section 5: "omitting privacy-sensitive information from an introspection response
5836    /// is the simplest way of minimizing privacy issues". The sensitive thing here is not only the
5837    /// user's identity, which the resource server needs and gets. It is the SHAPE OF THE GRANT:
5838    /// which OTHER services this user's token is good at. Two members carry that fact and both are
5839    /// narrowed to the asking resource server:
5840    ///
5841    /// - `aud`, to the RFC 8707 identifiers that resource server is registered for; and
5842    /// - `authorization_details`, to the RFC 9396 section 2.2 elements whose `locations` name one
5843    ///   of those identifiers, or that carry no `locations` at all. A kept element has its own
5844    ///   `locations` narrowed too, so an element addressed to two resource servers does not smuggle
5845    ///   the second one's URI past the filter. Section 9.2 asks for precisely this ("filtered and
5846    ///   extended for the RS making the introspection request"), and section 9.1 says the same of
5847    ///   the JWT form.
5848    ///
5849    /// An earlier 0.9.2 draft narrowed `aud` and shipped `authorization_details` whole, which meant
5850    /// the disclosure
5851    /// the first refused was re-made verbatim by the second, with the actions and privileges
5852    /// granted elsewhere attached. That is fixed rather than accepted, and the alternative
5853    /// resolution -- STOP NARROWING `aud`, and treat a registered resource server as a semi-trusted
5854    /// party that sees the grant as granted -- was rejected. A resource server is registered for
5855    /// the identifiers it answers for and nothing wider; a deployment adding a second protected
5856    /// resource would otherwise be silently telling the first one about it, and the party who pays
5857    /// for that is the user, who is not present and cannot be asked. Consistency in the other
5858    /// direction is cheaper to buy and costs somebody else.
5859    ///
5860    /// Two members are NOT narrowed, and the omission is a decision rather than an oversight:
5861    ///
5862    /// - `scope` is the whole grant's scope set. Nothing in this crate maps a scope to a resource
5863    ///   server -- there is no per-resource catalogue to filter against -- so any narrowing would
5864    ///   be a guess at which strings "belong" to the asker, and a resource server that silently
5865    ///   loses a scope refuses access the resource owner granted. A scope is also a token in the
5866    ///   deployment's own vocabulary; unlike a `locations` URI it does not NAME another service.
5867    /// - `act` (RFC 8693 section 4.1) describes who is acting in the call this resource server is
5868    ///   handling, not where else the grant reaches.
5869    ///
5870    /// The cost of the narrowing, stated plainly: a resource server given a filtered
5871    /// `authorization_details` cannot distinguish "not granted" from "not for you". That is the
5872    /// same indistinguishability the `aud` narrowing already imposes, and it is the harmless
5873    /// direction -- both readings oblige the resource server to refuse, because an element it is
5874    /// not named in is one it must not act on either way. The disclosure direction has no such
5875    /// symmetry. A caller that needs the unfiltered record is the token's OWN CLIENT, and it still
5876    /// gets it; so does a host, through [`AuthorizationServer::introspect`].
5877    pub async fn introspection_response(
5878        &self,
5879        client_id: &ClientId,
5880        client_secret: Option<&str>,
5881        token: &str,
5882    ) -> Result<IntrospectionResponse, ErrorResponse> {
5883        self.introspection_response_with_credential(
5884            client_id,
5885            &ClientCredential::secret(client_secret),
5886            token,
5887        )
5888        .await
5889    }
5890
5891    /// RFC 7662 introspection for a caller authenticating with any credential this server accepts,
5892    /// including an RFC 7523 assertion. See
5893    /// [`AuthorizationServer::device_authorization_with_credential`] on why this is an addition
5894    /// rather than a replacement.
5895    pub async fn introspection_response_with_credential(
5896        &self,
5897        client_id: &ClientId,
5898        cred: &ClientCredential<'_>,
5899        token: &str,
5900    ) -> Result<IntrospectionResponse, ErrorResponse> {
5901        let client = self.authenticate_client(client_id, cred).await?;
5902        // RFC 7662 section 2.1 requires the endpoint to be protected, and section 4 says it MUST
5903        // NOT be publicly available, because it otherwise describes any token an attacker has
5904        // merely obtained a copy of. A PUBLIC client has no secret to verify, so "authenticated as
5905        // a public client" is a sentence true of every caller on the internet: naming a client id
5906        // is not authentication, and an ownership check made against an identity anyone may claim
5907        // is not an access control. Same refusal as `client_credentials_token`, for the same
5908        // reason.
5909        //
5910        // BARE, AND THE REASON GOES TO THE AUDIT CHANNEL. It used to carry the description
5911        // "introspection requires a confidential client", which was a client-existence oracle:
5912        // an unknown client id and a confidential client with the wrong secret both leave
5913        // `authenticate_client` as a BARE `invalid_client` (see "THE ONE EXIT"), so a description
5914        // here was the one answer that meant "this id is registered, and it is public". That is
5915        // precisely the distinction the whole credential path collapses, rebuilt one endpoint
5916        // downstream of it. 0.9.2 also turns this endpoint into an advertised resource-server-
5917        // facing surface, so it is now a probe an attacker is invited to make.
5918        //
5919        // The operator still gets the sentence — `ClientAuthFailure::NotConfidential` — because
5920        // the usual cause is a misregistered resource server rather than an attack, and a bare
5921        // refusal with nothing in the log would be unactionable. The rate limiter is NOT charged
5922        // again: `authenticate_client` already recorded this attempt's outcome, and recording a
5923        // second one for a single request would make this endpoint count double.
5924        if matches!(client.auth, crate::client::ClientAuth::Public) {
5925            self.hooks.emit(|| Event::ClientAuthenticationFailed {
5926                client_id: client_id.as_str(),
5927                failure: ClientAuthFailure::NotConfidential,
5928            });
5929            return Err(ErrorResponse::new(ErrorCode::InvalidClient));
5930        }
5931        let record = self.introspect(token).await.map_err(storage_error)?;
5932        // WHOSE QUESTION IS THIS. RFC 7662 has two legitimate callers and they are answered from
5933        // the same arm but not with the same document, so the viewpoint is decided once, here,
5934        // before anything is copied out of the record.
5935        //
5936        // `None` covers unknown, expired, somebody else's, and addressed-to-some-other-resource-
5937        // server. All four are `{"active": false}` on purpose: section 2.2 gives exactly one answer
5938        // for a token the caller has not proven a relationship to, and distinguishing "no such
5939        // token" from "a live token that is not yours" would rebuild the oracle section 4 warns
5940        // about out of the error channel instead of the response body.
5941        let view = record
5942            .as_ref()
5943            .and_then(|t| self.introspection_view(&client, t));
5944        Ok(match (record, view) {
5945            (Some(t), Some(view)) => IntrospectionResponse {
5946                active: true,
5947                // RFC 7662 s2.2, THE WHOLE GRANT'S SCOPE SET, to both viewpoints, and deliberately
5948                // so: see "What a resource server is not told" on this method for why this member
5949                // is not narrowed the way `aud` and `authorization_details` are.
5950                scope: (!t.scope.is_empty()).then(|| t.scope.to_string()),
5951                client_id: Some(t.client_id.as_str().to_string()),
5952                sub: t.subject.clone(),
5953                #[cfg(feature = "dpop")]
5954                token_type: Some(match t.jkt {
5955                    Some(_) => TokenType::Dpop,
5956                    None => TokenType::Bearer,
5957                }),
5958                #[cfg(not(feature = "dpop"))]
5959                token_type: Some(TokenType::Bearer),
5960                exp: unix_seconds(t.expires_at),
5961                iat: unix_seconds(t.issued_at),
5962                iss: Some(self.issuer_identifier().to_string()),
5963                // RFC 7662 s2.2: `aud` is OPTIONAL, and this server has one to report exactly when
5964                // the grant carried RFC 8707 resource indicators. Omitted rather than empty when it
5965                // does not: see `IntrospectionResponse::aud`.
5966                //
5967                // THIS IS THE ONE MEMBER THE TWO VIEWPOINTS DO NOT SHARE. The token's own client
5968                // sees the whole set, because it asked for it and already holds it. A RESOURCE
5969                // SERVER sees only the identifiers it is itself registered for, because the rest of
5970                // the set is a list of the OTHER resource servers this user's token is good at, and
5971                // section 5's privacy considerations do not stop at the user's identity: telling
5972                // api.example that this token also works at payroll.example discloses the shape of
5973                // somebody's account to a third party that has no part in it. Narrowing here rather
5974                // than at the record keeps `aud` a true statement in both documents; it is the same
5975                // claim, answered to the extent the asker is entitled to it.
5976                aud: match &view {
5977                    IntrospectionView::OwningClient => {
5978                        (!t.resource.is_empty()).then(|| t.resource.clone())
5979                    }
5980                    IntrospectionView::ResourceServer(mine) => Some(mine.clone()),
5981                },
5982                // RFC 9470 s6.2. A resource server that sent a step-up challenge has to be able to
5983                // see whether the token it now holds satisfies it; without these two it would have
5984                // to take the client's word for that, which is the whole thing the challenge exists
5985                // to avoid.
5986                #[cfg(feature = "consent")]
5987                auth_time: t
5988                    .authentication
5989                    .as_ref()
5990                    .and_then(|a| unix_seconds(a.auth_time)),
5991                #[cfg(feature = "consent")]
5992                acr: t
5993                    .authentication
5994                    .as_ref()
5995                    .and_then(|a| a.acr.as_deref().map(str::to_string)),
5996                // RFC 9396 s9.2: the details as a top-level member of the introspection
5997                // response. For an OPAQUE token this is the ONLY way a resource server can
5998                // learn what the token actually authorizes, which is the whole point of the
5999                // parameter, and a resource server registered under
6000                // `ServerConfig::resource_servers` is now the caller that receives it.
6001                //
6002                // FILTERED FOR THE ASKER, for the reason `aud` above is. An earlier 0.9.2 draft
6003                // left it unfiltered until the audit noticed the two members carry the same fact. A
6004                // section 2.2 element has `locations`, which NAMES RESOURCE SERVERS BY URI, so
6005                // handing api.example the whole array says "this token also works at
6006                // payroll.example" in the very breath `aud` refuses to say it, and adds the
6007                // actions and privileges granted there. Section 9.2 asks for exactly this:
6008                // "filtered and extended for the RS making the introspection request".
6009                #[cfg(feature = "rar")]
6010                authorization_details: match &view {
6011                    IntrospectionView::OwningClient => t.authorization_details.clone(),
6012                    IntrospectionView::ResourceServer(mine) => {
6013                        details_for_resource_server(&t.authorization_details, mine)
6014                    }
6015                },
6016                // RFC 9449 s6.1 and RFC 8705 s3.2, in ONE RFC 7800 s3.1 object. Both mechanisms
6017                // register a member of `cnf` and a token can carry both, so this is built from
6018                // every binding the record has rather than from whichever one happens to be
6019                // checked first. Omitted entirely when there is none.
6020                //
6021                // A caller that introspects must be able to confirm the binding, or the binding
6022                // stops at this server and the caller is back to trusting a bearer string.
6023                // That caller is the token's own client or, since 0.9.2, the resource server the
6024                // token is addressed to; both need to confirm the binding for the same reason.
6025                #[cfg(any(feature = "dpop", feature = "mtls"))]
6026                cnf: {
6027                    let cnf = crate::token::Confirmation {
6028                        #[cfg(feature = "dpop")]
6029                        jkt: t.jkt.as_deref().map(str::to_string),
6030                        #[cfg(feature = "mtls")]
6031                        x5t_s256: t.x5t_s256.as_deref().copied(),
6032                    };
6033                    (!cnf.is_empty()).then_some(cnf)
6034                },
6035                // RFC 8693 s4.1, and the reason it is on the record at all: for an OPAQUE token
6036                // this is the only channel a resource server has for learning that what it holds
6037                // is a DELEGATION rather than the subject acting directly.
6038                //
6039                // Not narrowed, and unlike `scope` there is nothing here that could be: `act`
6040                // describes WHO IS ACTING in the call this resource server is being made, not
6041                // where else the grant reaches. It names no other resource server, so it does not
6042                // carry the fact `aud` withholds, and withholding it would leave the RS unable to
6043                // tell a delegated call from a direct one -- which is the one thing section 4.1
6044                // exists to tell it.
6045                #[cfg(feature = "token-exchange")]
6046                act: t.act.as_deref().cloned(),
6047            },
6048            // Unknown, expired, somebody else's, or addressed to a different resource server.
6049            // All four are one answer on purpose; see `view` above.
6050            _ => IntrospectionResponse::inactive(),
6051        })
6052    }
6053
6054    /// Which RFC 7662 viewpoint `client` holds on `token`, or `None` for the callers section 2.2
6055    /// answers `{"active": false}`.
6056    ///
6057    /// Ownership is checked FIRST and wins outright. A client that is both the token's own client
6058    /// and a registered resource server is answered as the owner, which is the wider of the two
6059    /// documents; being told about your own token is not a privilege that a second, narrower role
6060    /// should be able to take away.
6061    fn introspection_view(
6062        &self,
6063        client: &Client,
6064        token: &IssuedToken,
6065    ) -> Option<IntrospectionView> {
6066        if token.client_id == client.client_id {
6067            return Some(IntrospectionView::OwningClient);
6068        }
6069        // The resource-server channel. A registration matches only by NAMING an identifier the
6070        // token actually carries, so a token whose grant requested no resource indicator at all
6071        // (`token.resource` empty) matches nothing here and is refused to every resource server.
6072        // That is deliberate and it is the whole defence: see `ServerConfig::resource_servers`.
6073        let mine: Vec<String> = self
6074            .config
6075            .resource_servers
6076            .as_deref()
6077            .unwrap_or(&[])
6078            .iter()
6079            .filter(|rs| rs.client_id == client.client_id)
6080            .flat_map(|rs| rs.resources.iter())
6081            .filter(|id| token.resource.iter().any(|r| r == *id))
6082            .map(|id| id.to_string())
6083            .fold(Vec::new(), |mut acc, id| {
6084                // Deduped because the same identifier may legitimately appear in two registrations
6085                // for one client (see `ResourceServerRegistration`), and `aud` repeating it would
6086                // be a malformed-looking document produced by a configuration that is not wrong.
6087                if !acc.contains(&id) {
6088                    acc.push(id);
6089                }
6090                acc
6091            });
6092        (!mine.is_empty()).then_some(IntrospectionView::ResourceServer(mine))
6093    }
6094
6095    /// Record that a resource owner has consented to a client acting for them.
6096    ///
6097    /// One live consent per (client, subject) pair: an existing record is WIDENED in place, keeping
6098    /// its identifier and its original `granted_at`, so a user who approves one more scope next
6099    /// month still sees one entry rather than two and withdrawing it withdraws the whole
6100    /// relationship. See [`crate::consent::ConsentRecord::extend`].
6101    ///
6102    /// The library NEVER calls this for itself. Recording consent is a statement that a user agreed
6103    /// to something, and this crate has no way to know that: it never sees a user. The host calls
6104    /// it once its own consent step has actually been answered.
6105    ///
6106    /// # Concurrency, and what this used to concede
6107    ///
6108    /// This is a read-modify-write: it looks for an existing record, widens it, and writes it
6109    /// back. It is now a COMPARE-AND-SWAP against what it read
6110    /// ([`Storage::compare_and_swap_consent`]), retried once, and both halves of that matter.
6111    ///
6112    /// The half this doc used to argue away was two overlapping FIRST approvals each finding
6113    /// nothing and each creating a record, leaving the pair with two. The argument was that the
6114    /// records are additive so the damage is a possible re-prompt. That much was true, and it is
6115    /// no longer relevant: the create is conditional on the pair still being empty, so the loser
6116    /// sees the winner's record and widens it instead.
6117    ///
6118    /// The half this doc never considered is the one that was not benign, and it is the reason
6119    /// this changed. The second writer it reasoned about was another `record_consent`. The writer
6120    /// that actually mattered was [`AuthorizationServer::withdraw_consent`]: a widen that read a
6121    /// record, and wrote it back after the user clicked withdraw, RESURRECTED a consent the user
6122    /// had destroyed, and every later authorization request was answered from it. "Benign in the
6123    /// direction that matters" was a statement about the wrong direction. See the resurrection
6124    /// rule in the [`crate::store`] module docs.
6125    ///
6126    /// A host no longer owes this path a lock of its own.
6127    #[cfg(feature = "consent")]
6128    pub async fn record_consent(
6129        &self,
6130        client_id: &ClientId,
6131        subject: &str,
6132        scope: &ScopeSet,
6133        resource: &[String],
6134        authentication: Option<crate::consent::Authentication>,
6135    ) -> Result<crate::consent::ConsentRecord, StorageError> {
6136        // REFUSED AT CREATION, because refusing at withdrawal is refusing at the one operation
6137        // that undoes damage.
6138        //
6139        // `Storage::revoke_consent` rejects an empty `client_id` or `subject`, and it reads them
6140        // out of the STORED record rather than off its argument — so a record created with an
6141        // empty subject can never be withdrawn by any input at all. The record stands, the cascade
6142        // never runs, and every token and refresh chain beneath it stays live for its full
6143        // lifetime, while `withdraw_consent` answers an error that contradicts its own
6144        // documented contract ("withdrawing a consent that is already gone is `Ok(0)`, not an
6145        // error"). A host whose subject resolver can yield the empty string — which this crate
6146        // never checks, because a subject is the host's own vocabulary for users — reaches that
6147        // state through the ordinary API.
6148        //
6149        // A `StorageError` rather than a new error type: this is the same class of refusal
6150        // `revoke_consent` already answers with, and a consent that names nobody is not a consent.
6151        if subject.is_empty() {
6152            return Err(StorageError::new(
6153                "a consent must name a subject; an empty subject cannot be withdrawn",
6154            ));
6155        }
6156        let now = self.clock.now();
6157        // TWO attempts, and no more. One retry is what a compare-and-swap needs to absorb an
6158        // ordinary lost race (the other writer created or widened the record, and this call can
6159        // simply widen theirs instead); a loop would be a spin against a withdrawal that is going
6160        // to keep refusing, on a path a human drives by clicking. The second failure is reported
6161        // as a storage error, which is the honest answer: the consent was NOT recorded.
6162        // WHAT THE FIRST ATTEMPT SAW, and the retry may not contradict it. A call that started as
6163        // a WIDEN must not become a CREATE on its second attempt: the only way the record can have
6164        // vanished between them is that it was withdrawn, and turning that into a fresh consent
6165        // hands the user back a live grant moments after they ended it. The other direction is
6166        // fine and is the ordinary lost race: a call that started as a create and now finds a
6167        // record simply widens that record instead.
6168        //
6169        // This is NOT enforced by refusing on the barrier, deliberately. A consent barrier stands
6170        // for the longest token lifetime the server mints, and refusing every create for that long
6171        // would mean a user who withdraws an application and approves it again five minutes later
6172        // is told no, for an hour, with nothing to tell them why. The rule that is actually needed
6173        // is narrower: within ONE call, the shape may not change.
6174        let mut started_as_widen = None;
6175        for attempt in 0..2 {
6176            // CLONED out of the shared snapshot, because this is the one consent path that MUTATES
6177            // what it read (`extend` below widens the grant in place). An `Arc` cannot be widened
6178            // while the store still holds it, so the clone the read used to make happens here
6179            // instead: the cost moved, it did not go away. It is paid once per host consent
6180            // decision, against the read being free on the authorization endpoint, which runs per
6181            // request. See `remembered_consent`.
6182            let existing = self.store.find_consent(client_id, subject).await?;
6183            let expected = existing.as_deref().cloned();
6184            match started_as_widen {
6185                None => started_as_widen = Some(expected.is_some()),
6186                // Started as a widen, and the record is gone. It was withdrawn while this call was
6187                // in flight, which is the direction that is not benign.
6188                Some(true) if expected.is_none() => {
6189                    return Err(StorageError::new(
6190                        "the consent was withdrawn while it was being recorded",
6191                    ))
6192                }
6193                Some(_) => {}
6194            }
6195            let _ = attempt;
6196            let mut record = match &expected {
6197                Some(existing) => existing.clone(),
6198                None => crate::consent::ConsentRecord {
6199                    // 16 bytes of OS randomness, hex encoded, the same shape as every other opaque
6200                    // identifier this server mints. It is not a credential (see the field's own
6201                    // docs), but it names a record that can end a user's sessions, so it must not
6202                    // be something a third party can produce by guessing two strings it already
6203                    // knows.
6204                    // `?` rather than a panic, for the reason `try_random_hex` gives. This
6205                    // function answers `StorageError`, so that is the shape the refusal takes
6206                    // here; the host learns what happened from its own logs either way.
6207                    consent_id: try_random_hex(16)
6208                        .ok_or_else(|| {
6209                            StorageError::new(
6210                                "the OS would not provide randomness for a consent id",
6211                            )
6212                        })?
6213                        .into_boxed_str(),
6214                    client_id: client_id.clone(),
6215                    subject: subject.into(),
6216                    scope: ScopeSet::empty(),
6217                    resource: Vec::new(),
6218                    granted_at: now,
6219                    authentication: None,
6220                },
6221            };
6222            record.extend(scope, resource);
6223            // The LATEST authentication replaces the previous one: it is what the user just did,
6224            // and it is what an RFC 9470 `max_age` on the next request has to be measured against.
6225            // A host that reports nothing this time leaves the previous report standing rather
6226            // than erasing it, because "did not say" is not "no longer authenticated".
6227            if let Some(a) = &authentication {
6228                record.authentication = Some(Box::new(a.clone()));
6229            }
6230            // The write happens only if the pair still holds exactly what was read a moment ago:
6231            // still nothing when creating, still that record when widening. A withdrawal in
6232            // between refuses BOTH shapes, which is the point.
6233            if self
6234                .store
6235                .compare_and_swap_consent(expected.as_ref(), record.clone())
6236                .await?
6237            {
6238                return Ok(record);
6239            }
6240        }
6241        Err(StorageError::new(
6242            "consent record changed concurrently twice; not recorded",
6243        ))
6244    }
6245
6246    /// The consent this user has already given this client, if any.
6247    ///
6248    /// This ANSWERS a question; it does not make a decision, and nothing in this crate approves an
6249    /// authorization request on the strength of it. See the `http` feature's
6250    /// `ServiceBuilder::with_approval_resolver`: the library reports what it remembers
6251    /// and the host decides what that is worth, because "the user agreed to this once" and "the
6252    /// user agrees to this now" are different sentences and only the host can tell them apart.
6253    #[cfg(feature = "consent")]
6254    pub async fn remembered_consent(
6255        &self,
6256        client_id: &ClientId,
6257        subject: &str,
6258    ) -> Result<Option<std::sync::Arc<crate::consent::ConsentRecord>>, StorageError> {
6259        self.store.find_consent(client_id, subject).await
6260    }
6261
6262    /// Everything one resource owner has consented to, so a host can show a user what they have
6263    /// granted. Without this a user cannot SEE what they gave away, which is half of why this
6264    /// feature exists at all.
6265    #[cfg(feature = "consent")]
6266    pub async fn consents_for_subject(
6267        &self,
6268        subject: &str,
6269    ) -> Result<Vec<std::sync::Arc<crate::consent::ConsentRecord>>, StorageError> {
6270        self.store.consents_for_subject(subject).await
6271    }
6272
6273    /// WITHDRAW a consent, revoking everything issued under it. Returns how many records the
6274    /// cascade removed.
6275    ///
6276    /// This is the point of the whole feature. A withdrawal that left tokens alive would be worse
6277    /// than no withdrawal at all, because the user would believe they had stopped something they
6278    /// had not, so the cascade is one storage operation
6279    /// ([`crate::store::Storage::revoke_consent`]) and it reaches every family the consent ever
6280    /// produced, plus the authorization codes and approved-but-unpolled device grants that would
6281    /// otherwise mint tokens seconds later.
6282    ///
6283    /// Withdrawing a consent that is already gone is `Ok(0)`, not an error.
6284    #[cfg(feature = "consent")]
6285    pub async fn withdraw_consent(&self, consent_id: &str) -> Result<u64, StorageError> {
6286        // Read first, purely so the audit event can name the client and the user. Two round trips
6287        // on a path a person drives by hand is not a cost worth optimising away, and an event that
6288        // said only "some consent was withdrawn" is an event nobody can act on.
6289        let record = self.store.get_consent(consent_id).await?;
6290        let records_revoked = self
6291            .store
6292            .revoke_consent(consent_id, self.revocation_window())
6293            .await?;
6294        if let Some(record) = &record {
6295            self.hooks.emit(|| Event::ConsentWithdrawn {
6296                client_id: record.client_id.as_str(),
6297                subject: record.subject.as_ref(),
6298                records_revoked,
6299            });
6300        }
6301        Ok(records_revoked)
6302    }
6303
6304    /// RFC 7009 token revocation.
6305    ///
6306    /// Returns `Ok(())` when the token is gone, INCLUDING when it never existed: section 2.2
6307    /// requires a 200 for an unknown token, because distinguishing "revoked" from "never heard of
6308    /// it" would let an unauthenticated caller test whether a token string is real.
6309    ///
6310    /// `token_type_hint` (section 2.1) is an optimisation, not a constraint: the RFC requires the
6311    /// server to keep looking if the hint is wrong, so a wrong hint costs a second lookup and
6312    /// nothing else.
6313    ///
6314    /// PUBLIC CLIENTS MAY REVOKE THEIR OWN TOKENS here, presenting a `client_id` and no secret,
6315    /// which is section 2.1's own rule ("in case of a confidential client" scopes the credential
6316    /// check) and section 5's ("a valid `client_id`, in the case of a public client"). What stops
6317    /// a caller who merely knows a public client's id is the OWNERSHIP check, made against the
6318    /// stored record: another client's token is untouched, and answered `Ok(())` all the same.
6319    /// This is deliberately NOT what
6320    /// [`introspection_response`](AuthorizationServer::introspection_response) does; see the
6321    /// comment inside [`revoke_with_credential`](AuthorizationServer::revoke_with_credential) for
6322    /// why the two RFCs differ.
6323    ///
6324    /// # THIS FUTURE IS NOT CANCELLATION SAFE, and what a drop costs
6325    ///
6326    /// Applies equally to
6327    /// [`revoke_with_credential`](AuthorizationServer::revoke_with_credential), which is the same
6328    /// future. A dropped future stops at whatever `await` it was suspended in and never resumes,
6329    /// and this crate cannot make it finish: there is no destructor that can run an `async` store
6330    /// call. So the contract is stated rather than left to be discovered.
6331    ///
6332    /// Revoking a REFRESH token is a two-write sequence: the RFC 7009 s2.1 cascade over the
6333    /// grant's family, and the removal of the presented string. A drop between them leaves the
6334    /// family revoked, with a barrier recorded, and one live-LOOKING refresh string that names a
6335    /// family nothing will honour. That is fail-closed on purpose, and it is why the cascade runs
6336    /// first; the opposite order was worse than an incomplete revocation, because the client's
6337    /// RETRY found the presented string already gone and answered 200 without cascading at all,
6338    /// leaving every access token of a grant the user had logged out of live for its whole TTL.
6339    /// A retry after a drop now still reaches the cascade, and the cascade is idempotent
6340    /// ([`crate::store::Storage::revoke_token_family`]). `tests/revocation_cancellation.rs` pins
6341    /// it.
6342    ///
6343    /// WHAT IS STILL LOST to a drop, stated rather than implied: the [`crate::events::Event`] the
6344    /// completed call would have emitted, so an audit trail can miss a revocation that partly
6345    /// happened. A host that needs the sequence to complete must drive it from a task the
6346    /// connection cannot cancel, spawning the call and awaiting the join handle, which is what this
6347    /// crate's own axum adapter does; see [`crate::http`].
6348    pub async fn revoke(
6349        &self,
6350        client_id: &ClientId,
6351        client_secret: Option<&str>,
6352        token: &str,
6353        token_type_hint: Option<TokenTypeHint>,
6354    ) -> Result<(), ErrorResponse> {
6355        self.revoke_with_credential(
6356            client_id,
6357            &ClientCredential::secret(client_secret),
6358            token,
6359            token_type_hint,
6360        )
6361        .await
6362    }
6363
6364    /// RFC 7009 revocation for a caller authenticating with any credential this server accepts,
6365    /// including an RFC 7523 assertion. See
6366    /// [`AuthorizationServer::device_authorization_with_credential`] on why this is an addition
6367    /// rather than a replacement.
6368    pub async fn revoke_with_credential(
6369        &self,
6370        client_id: &ClientId,
6371        cred: &ClientCredential<'_>,
6372        token: &str,
6373        token_type_hint: Option<TokenTypeHint>,
6374    ) -> Result<(), ErrorResponse> {
6375        let client = self.authenticate_client(client_id, cred).await?;
6376        // PUBLIC CLIENTS ARE ADMITTED HERE, and deliberately, which is the opposite of
6377        // `introspection_response_with_credential` a few hundred lines above. The two arrived
6378        // together under one citation pair and only the introspection half of it held.
6379        //
6380        // RFC 7009 section 2.1 scopes credential validation in as many words: the server "first
6381        // validates the client credentials (in case of a confidential client) and then verifies
6382        // whether the token was issued to the client making the revocation request". Section 5
6383        // says the same thing from the other side, naming "a valid client_id, in the case of a
6384        // public client". So the OWNERSHIP check below, not client authentication, is what this
6385        // endpoint's access control rests on for a public client, and the checks below already
6386        // perform it: a record whose `client_id` is not this one is untouched and answered 200.
6387        //
6388        // Why this matters rather than being a conformance detail: this server issues tokens to
6389        // public clients through code+PKCE and through the device grant, so refusing them
6390        // revocation left a native or browser app with no standard way to make a logout mean
6391        // anything. The token stayed live for its whole TTL and the refresh chain outlived the
6392        // user's decision entirely.
6393        //
6394        // What an attacker gains is bounded by what they must already have: the token STRING. A
6395        // caller who holds it can already use it, so being able to destroy it is a strictly
6396        // smaller capability than the one they have; and holding somebody else's token buys
6397        // nothing here, because the comparison is against the record, not against the asserted
6398        // identity. Introspection is refused for exactly the reason this is allowed: it is a
6399        // request to DESCRIBE a token rather than to destroy one, and RFC 7662 section 4 says it
6400        // MUST NOT be publicly available.
6401
6402        let try_refresh = || async {
6403            // READ, then take. Section 2.1's ownership check is a question ABOUT someone else's
6404            // credential, so it must not be answered by removing it: a take-then-put-back is a
6405            // non-atomic read-modify-write on a live token, it opens a window in which the real
6406            // owner's concurrent refresh sees nothing, and if the restoring write fails the
6407            // victim's chain is destroyed permanently while this endpoint still answers 200.
6408            // Reading first means a non-owner's request touches nothing at all.
6409            match self.store.get_refresh_token(token).await {
6410                Ok(Some(record)) if record.client_id == client.client_id => {
6411                    // THE CASCADE RUNS FIRST, AND THE ORDER IS THE FIX. It used to take the
6412                    // presented refresh token and revoke the family afterwards, which is the
6413                    // fail-OPEN order for a two-write sequence that can stop between the writes:
6414                    // this future is dropped whenever the host's connection is cancelled, and a
6415                    // drop after the take left the presented string gone and the family whole.
6416                    // The client's retry then found nothing at `get_refresh_token`, fell to the
6417                    // `Ok(_)` arm below, and answered 200 with no cascade at all, so every access
6418                    // token of a grant the user had just logged out of stayed live for its whole
6419                    // TTL and nothing anywhere recorded it. Note that the store-ERROR path was
6420                    // already handled honestly (`cascade_failed` below, emitted either way); the
6421                    // DROP path emitted nothing, which is why it was invisible.
6422                    //
6423                    // Revoking first inverts that. A drop between the two writes leaves the family
6424                    // revoked, with a barrier recorded, and one live-looking refresh string that
6425                    // names a family nothing will honour: fail-closed, and the retry re-runs a
6426                    // cascade that is idempotent by contract (see `Storage::revoke_token_family`,
6427                    // "removing records that are already gone is success").
6428                    //
6429                    // The `family_id` comes from the record READ above rather than from a taken
6430                    // one. A refresh token string cannot change families, so the two always agreed;
6431                    // and after this call there is usually no record left to take, because
6432                    // `revoke_token_family` removes the family's refresh records as well.
6433                    //
6434                    // The in-flight-rotation case the previous ordering worried about is handled
6435                    // by the barrier, not by the ordering: a rotation that has already taken its
6436                    // record is invisible to the cascade's scan, and it is the barrier that
6437                    // REFUSES its later writes rather than letting them restore the chain the user
6438                    // just revoked. Recording that barrier sooner can only help.
6439                    //
6440                    // RFC 7009 section 2.1: "If the particular token is a refresh token and the
6441                    // authorization server supports the revocation of access tokens, then the
6442                    // authorization server SHOULD also invalidate all access tokens based on the
6443                    // same authorization grant." This server does support it, so the SHOULD
6444                    // applies, and the grant is exactly what `family_id` names (see
6445                    // `RefreshTokenRecord::family_id`): every token, access or refresh, minted from
6446                    // the same authorization. Killing only the presented string would leave the
6447                    // access token that came out of the same redemption live for its whole TTL,
6448                    // which is the opposite of what a client asking for revocation has just said.
6449                    //
6450                    // Deliberately NOT fatal on a storage failure. Section 2.2 makes the presented
6451                    // token's own revocation the answer, and the take below still runs and still
6452                    // reports its own failure; a cascade that could turn a completed revocation
6453                    // into a 503 would leave the client believing nothing was revoked when the
6454                    // token it named is already gone.
6455                    //
6456                    // NOT fatal, but no longer INVISIBLE. The event fired unconditionally, so an
6457                    // operator could not tell a complete revocation from one that killed the
6458                    // presented string and left every access token of the same grant alive. That
6459                    // is the difference between "the client's session is over" and "the client's
6460                    // session continues for up to one access token TTL", and only the host can
6461                    // decide what to do about it.
6462                    let cascade_failed = self
6463                        .store
6464                        .revoke_token_family(record.family_id.as_str(), self.revocation_window())
6465                        .await
6466                        .is_err();
6467                    // The presented string, in case the cascade did not reach it: it will already
6468                    // be gone in a store whose `revoke_token_family` removed the family's refresh
6469                    // records, and removing a record that is not there is not an error. This is
6470                    // what makes the sequence safe to stop after the cascade rather than before
6471                    // it, and what makes a retry a no-op.
6472                    self.store
6473                        .take_refresh_token(token)
6474                        .await
6475                        .map_err(storage_error)?;
6476                    self.hooks.emit(|| Event::TokenRevoked {
6477                        client_id: client.client_id.as_str(),
6478                        token_type: TokenTypeHint::RefreshToken,
6479                        cascade_failed,
6480                    });
6481                    Ok(true)
6482                }
6483                // Unknown, or somebody else's: nothing to do, and section 2.2 makes both a 200.
6484                Ok(_) => Ok(false),
6485                Err(e) => Err(storage_error(e)),
6486            }
6487        };
6488        let try_access = || async {
6489            match self.store.get_token(token).await {
6490                Ok(Some(t)) if t.client_id == client.client_id => {
6491                    self.store
6492                        .delete_token(token)
6493                        .await
6494                        .map_err(storage_error)?;
6495                    self.hooks.emit(|| Event::TokenRevoked {
6496                        client_id: client.client_id.as_str(),
6497                        token_type: TokenTypeHint::AccessToken,
6498                        // An access token names no grant to cascade to: it is one record and it is
6499                        // gone, or the `?` above already turned the failure into an error.
6500                        cascade_failed: false,
6501                    });
6502                    Ok(true)
6503                }
6504                Ok(_) => Ok(false),
6505                Err(e) => Err(storage_error(e)),
6506            }
6507        };
6508
6509        // The hint only decides which lookup happens first.
6510        match token_type_hint {
6511            Some(TokenTypeHint::AccessToken) => {
6512                if !try_access().await? {
6513                    try_refresh().await?;
6514                }
6515            }
6516            _ => {
6517                if !try_refresh().await? {
6518                    try_access().await?;
6519                }
6520            }
6521        }
6522        Ok(())
6523    }
6524}
6525
6526#[cfg(test)]
6527#[path = "tests/server.rs"]
6528mod tests;