Skip to main content

oauth_as/
token_exchange.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (C) 2026 Matthew Jackson
3
4//! RFC 8693 token exchange: `grant_type=urn:ietf:params:oauth:grant-type:token-exchange`.
5//!
6//! One client presents a token it holds and receives another one. That is the basis of nearly
7//! every service-to-service call in a system with more than one hop: a gateway that received a
8//! user's token needs a token it can send onward without handing the downstream service the
9//! original credential, and without either service having to invent its own scheme for saying who
10//! is really calling.
11//!
12//! # Delegation and impersonation, and how to tell which you got
13//!
14//! RFC 8693 section 1.1 draws the distinction and this module makes it observable rather than
15//! implicit, because it is the whole security meaning of the call:
16//!
17//! - IMPERSONATION. No `actor_token`. The issued token names the subject and nothing else, so the
18//!   downstream resource cannot tell the caller apart from the original principal "within some
19//!   defined rights context" (section 1.1). [`ExchangeSemantics::Impersonation`].
20//! - DELEGATION. An `actor_token` is presented, and it authenticates the acting party. The issued
21//!   token still names the subject, but it also carries the section 4.1 `act` claim identifying
22//!   who is acting for them, so the resource can log and authorize "A on behalf of B" rather than
23//!   "B". [`ExchangeSemantics::Delegation`], with [`ExchangedToken::act`] populated.
24//!
25//! [`ExchangedToken`] tells the host which of the two it just produced. It is not a detail to be
26//! inferred from whether a parameter was sent.
27//!
28//! # The ceiling, which is the entire risk of this grant
29//!
30//! An exchange must never produce a token that can do something the subject token could not.
31//! Widening is the only interesting attack on token exchange: a client that legitimately holds a
32//! read-only token for one resource asks for a write token for another, and if the AS obliges,
33//! every access control upstream of it has been bypassed by one HTTP request.
34//!
35//! This crate already had the right pattern in two places, and this grant reuses THE SAME CODE
36//! rather than a similar-looking copy of it (the crate-private `narrow_resources` and
37//! `validate_resources` on `AuthorizationServer`, which the authorization code and refresh paths
38//! call too; see the notes below):
39//!
40//! - SCOPE. RFC 6749 section 6 lets a refresh narrow and never widen; the same rule applies here,
41//!   against the subject token's granted scope. A widening request is `invalid_scope`.
42//! - RESOURCE / AUDIENCE. RFC 8707 section 2 lets a token request narrow the audience the grant
43//!   obtained and never widen it. The subject token's recorded `resource` list is the ceiling, and
44//!   a target outside it is `invalid_target`, which is the code RFC 8693 section 2.2.2 names for
45//!   exactly this. A subject token that named NO resource has nothing to narrow, so naming one is
46//!   widening from nothing and is refused, exactly as it is for the authorization code grant.
47//!
48//! A SECOND ceiling applies on top, which RFC 8693 does not require and this crate imposes anyway:
49//! the issued token is issued TO the exchanging client, so its scope must also sit inside that
50//! client's own registration ([`crate::client::Client::allowed_scopes`]). Without it, a client
51//! registered for `read` could hold someone else's `write` token for a minute and mint itself a
52//! `write` token of its own, which is the registration boundary defeated by a bearer string.
53//!
54//! # A SENDER-CONSTRAINED subject token is REFUSED, and this is the argued part
55//!
56//! RFC 9449 (DPoP) and RFC 8705 (mutual TLS) exist to buy ONE property: a token that leaks is worth
57//! nothing to whoever finds it, because spending it needs a private key or a client certificate
58//! that never left the legitimate client. An exchange takes a token STRING and returns a different
59//! token. If it ignores the subject token's binding, then anyone able to authenticate as any client
60//! registered for this grant, an insider, a compromised service, a leaked client secret, posts a
61//! stolen bound token and receives a plain bearer token with the same subject, scope and audience.
62//! The binding is gone and the theft is spendable. That is the exact property the deployment paid
63//! for, defeated by one request.
64//!
65//! RFC 8693 does not settle this, so the choice is stated rather than assumed. Three candidates:
66//!
67//! - DOWNGRADE SILENTLY, which is what this crate did through 0.9.0 and is the one answer that is
68//!   definitely wrong: the operator who turned DPoP on has no way to learn that a grant they also
69//!   turned on removes it again.
70//! - PROPAGATE the binding, so the issued token inherits the subject token's `cnf`. Attractive and
71//!   still wrong here: the new token is issued TO THE EXCHANGING CLIENT, which does not hold the
72//!   original client's key. The result is a token no conforming resource server would let its
73//!   holder use (RFC 9449 section 7.1 requires a proof under the bound key on every request), which
74//!   is a broken grant dressed as a secure one. Worse, it reads at a glance as though possession
75//!   was proven, when nobody proved anything: the exchanging client presented a bearer string.
76//! - REFUSE, which is what this crate does. A sender-constrained subject token is "unacceptable
77//!   based on policy" in the sense of RFC 8693 section 2.2.2, and the section names `invalid_request`
78//!   for exactly that. The refusal is loud, it happens at the AS rather than at some resource server
79//!   later, and it cannot be mistaken for success.
80//!
81//! What would make an exchange of a bound token safe is the exchanging client PROVING possession of
82//! the key the subject token is bound to, which is the RFC 9449 section 7 check a resource server
83//! performs. That is a real design and it is deliberately not faked here: [`TokenExchangeRequest`]
84//! carries no proof and no certificate, so there is nothing to check, and a seam that accepted one
85//! would also have to bind the ISSUED token to the same key, at which point the exchange is a
86//! delegation between two holders of one key rather than between two clients. If a deployment needs
87//! that, it is an additive change to this request type and to the refusal below; it is not
88//! something to approximate by waving the check through.
89//!
90//! # What this grant does NOT do, stated rather than left to be discovered
91//!
92//! - No refresh token is issued. RFC 8693 section 2.2.1 says one "will typically not be issued
93//!   when the exchange is of one temporary credential for a different temporary credential", and
94//!   issuing one would let an exchanged token outlive by rotation the grant it was derived from.
95//! - The issued token's lifetime is the LESSER of
96//!   [`crate::server::ServerConfig::access_token_ttl`] and the subject token's own remaining
97//!   lifetime. Through 0.9.0 it was the former alone, and that contradicted the bullet immediately
98//!   above: the exchanged token is an ordinary access token, so it is an acceptable subject token
99//!   in its turn, and self-exchange is permitted. A client could therefore re-exchange just before
100//!   each expiry and receive a fresh full TTL every time, renewing by exchange exactly the grant
101//!   lifetime that withholding a refresh token was meant to bound. Clamping makes time behave the
102//!   way scope, audience and RFC 9396 details already do here: an exchange may narrow, never widen.
103//! - The `act` claim reaches a resource server BY BOTH ROUTES as of 0.9.1: it is persisted on
104//!   [`crate::token::IssuedToken`] and reported by RFC 7662 introspection
105//!   ([`crate::token::IntrospectionResponse::act`]), and under the `jwt` feature it is an RFC 9068
106//!   claim in the signed access token ([`crate::jwt::AccessTokenClaims::act`]). This paragraph
107//!   described a GAP through 0.9.0 and is kept as the record of why it closed when it did.
108//!
109//!   BOTH ROUTES ARE NEEDED, which an earlier draft of this paragraph got wrong by claiming the
110//!   record alone had closed it. The two token formats reach a resource server differently: an
111//!   OPAQUE token carries nothing, so introspection is the only channel it has, while a JWT is
112//!   typically validated offline and introspected never. Persisting the claim and stopping there
113//!   would have moved the deficiency from one deployment shape to the other rather than ending it.
114//!
115//!   Two things stood in the way and both are spent. The first was allocation, on the reasoning
116//!   that [`crate::token::IssuedToken`] "is cloned on every token-plane request":
117//!   [`crate::store::Storage::get_token`] returns an `Arc<IssuedToken>` now, so the record's shape
118//!   costs a read nothing, and the field costs a deployment without this feature zero bytes and
119//!   one with it 8 bytes per token plus one allocation per DELEGATED token.
120//!
121//!   The second was the real one, the PERSISTENCE CONTRACT: `IssuedToken` is the record every
122//!   host's [`crate::store::Storage`] implementation writes and reads, so a new field is a
123//!   migration in stores this crate does not own rather than a struct edit here. That is a
124//!   coordinated change with a release behind it, and 0.9.1 is that release: it is already
125//!   breaking `Storage` for the revocation-barrier rule, so a host migrates once instead of twice.
126//!
127//!   Why it was worth doing rather than deferring again: this crate's default access token is
128//!   OPAQUE, so introspection is the ONLY channel a resource server has. A delegation it cannot
129//!   see is a delegation the resource server has to take the host's word for, which collapses RFC
130//!   8693 section 1.1 delegation back into impersonation from the one viewpoint the distinction
131//!   exists for. The claim was persisted before there was anybody to read it, because the record
132//!   is the half that cannot be added later; since 0.9.2 a resource server registered in
133//!   `ServerConfig::resource_servers` is served on the opaque route too, so that viewpoint is now
134//!   a real one rather than a promised one.
135
136use std::fmt;
137use std::str::FromStr;
138
139use serde::{Deserialize, Serialize};
140
141use crate::client::ClientId;
142use crate::error::{ErrorCode, ErrorResponse};
143use crate::events::Event;
144use crate::grant::GrantType;
145use crate::scope::ScopeSet;
146use crate::server::{AuthorizationServer, Bound, ClientCredential, Clock};
147use crate::store::{Storage, StorageError};
148
149/// The largest number of RFC 8693 section 2.1.1 `audience` values one exchange may carry.
150///
151/// Deliberately the SAME number as [`crate::server::MAX_RESOURCE_INDICATORS`], and not a second
152/// judgement: section 2.1.1 says `audience` and `resource` name the same thing in two spellings,
153/// one a logical name and one a URI, and this crate funnels both into one list against one ceiling.
154/// Two constants would be two numbers a reader has to keep in step, for a distinction the RFC
155/// itself does not draw.
156///
157/// The reason for a bound at all is the reason given on [`crate::server::MAX_RESOURCE_INDICATORS`]:
158/// the parameter is repeatable and the dedup is an O(n) scan per element, so `n` is chosen by the
159/// caller. This endpoint IS authenticated, which is why the finding is a rung lower, but a client
160/// that has merely leaked its secret should not get an amplifier along with it.
161pub const MAX_AUDIENCE_VALUES: usize = crate::server::MAX_RESOURCE_INDICATORS;
162
163/// The longest RFC 8693 section 4.1 `act` chain this server will mint: the number of ACTORS the
164/// nested claim may name, counting the current one.
165///
166/// A bound is required rather than tidy. Each delegation exchange nests the subject token's own
167/// `act` inside the new one, and the result is PERSISTED on [`crate::token::IssuedToken`] and
168/// serialized into every RFC 9068 access token and RFC 7662 introspection response the token
169/// produces. Exchanging one's own token is explicitly permitted, so without a bound an
170/// authenticated client can loop — exchange, then exchange the result — and each hop adds a link
171/// that every later read pays for, in the store and on the wire. That is a client choosing how much
172/// storage the server spends, which is the same shape as the repeatable-parameter bounds above.
173///
174/// Eight is chosen against real topologies rather than against the RFC, which sets no limit:
175/// section 1.1's delegation is a call graph, and a request that has legitimately crossed eight
176/// distinct delegating services has a shape an operator should be told about rather than one this
177/// server should quietly extend. A chain at the bound is refused with section 2.2.2's
178/// `invalid_request` rather than TRUNCATED, because truncation would silently discard exactly the
179/// audit history the nesting exists to keep, and a server that quietly forgets who acted earlier is
180/// worse than one that says it will not go further.
181pub const MAX_ACT_CHAIN_DEPTH: usize = 8;
182
183use crate::token::TokenType;
184
185pub use crate::grant::TOKEN_EXCHANGE_GRANT_URN;
186
187/// The RFC 8693 section 3 token type identifiers.
188///
189/// The URNs are the wire values, verbatim. This is a closed enum rather than a string because the
190/// `subject_token_type` is a security-relevant statement about how the presented string must be
191/// checked, and "a type identifier this server did not recognise" has to be a REFUSAL rather than
192/// something that falls through to a default (RFC 8693 section 2.2.2: a subject token that is
193/// unacceptable based on policy is an error).
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
195pub enum TokenTypeIdentifier {
196    /// `urn:ietf:params:oauth:token-type:access_token`. The only type this server accepts as a
197    /// subject or actor token, and the only type it issues.
198    #[serde(rename = "urn:ietf:params:oauth:token-type:access_token")]
199    AccessToken,
200    /// `urn:ietf:params:oauth:token-type:refresh_token`.
201    #[serde(rename = "urn:ietf:params:oauth:token-type:refresh_token")]
202    RefreshToken,
203    /// `urn:ietf:params:oauth:token-type:id_token`.
204    #[serde(rename = "urn:ietf:params:oauth:token-type:id_token")]
205    IdToken,
206    /// `urn:ietf:params:oauth:token-type:saml1`.
207    #[serde(rename = "urn:ietf:params:oauth:token-type:saml1")]
208    Saml1,
209    /// `urn:ietf:params:oauth:token-type:saml2`.
210    #[serde(rename = "urn:ietf:params:oauth:token-type:saml2")]
211    Saml2,
212    /// `urn:ietf:params:oauth:token-type:jwt`. Note that RFC 8693 section 3 defines this as "any
213    /// JWT", which is a statement about ENCODING and not about who issued it; this server does not
214    /// accept it, because accepting a JWT as a subject token means deciding whose signature to
215    /// trust, and that is a policy no library can pick on a host's behalf.
216    #[serde(rename = "urn:ietf:params:oauth:token-type:jwt")]
217    Jwt,
218}
219
220impl TokenTypeIdentifier {
221    /// Resolve a wire `*_token_type` value WITHOUT allocating, returning `None` for anything RFC
222    /// 8693 section 3 does not register.
223    ///
224    /// This is the parse the HTTP surface uses, for the reason [`crate::grant::GrantType::parse`]
225    /// exists: [`FromStr`]'s error carries the caller's value, and the router deliberately does not
226    /// echo it (RFC 6749 s5.2 restricts `error_description` to a charset an attacker-supplied URN
227    /// need not respect), so the copy was allocated and dropped unread. The refusal STRING here was
228    /// already made a `&'static str` for exactly this rule; the allocation underneath it was
229    /// missed, and it is the worse one, because the caller chooses its SIZE and this refusal
230    /// happens before the presented client credential has been checked.
231    ///
232    /// [`FromStr`] is unchanged and still carries the value, for the host-side callers that want
233    /// to report which URN they got wrong.
234    pub fn parse(s: &str) -> Option<Self> {
235        match s {
236            "urn:ietf:params:oauth:token-type:access_token" => {
237                Some(TokenTypeIdentifier::AccessToken)
238            }
239            "urn:ietf:params:oauth:token-type:refresh_token" => {
240                Some(TokenTypeIdentifier::RefreshToken)
241            }
242            "urn:ietf:params:oauth:token-type:id_token" => Some(TokenTypeIdentifier::IdToken),
243            "urn:ietf:params:oauth:token-type:saml1" => Some(TokenTypeIdentifier::Saml1),
244            "urn:ietf:params:oauth:token-type:saml2" => Some(TokenTypeIdentifier::Saml2),
245            "urn:ietf:params:oauth:token-type:jwt" => Some(TokenTypeIdentifier::Jwt),
246            _ => None,
247        }
248    }
249
250    /// The registered URN, verbatim.
251    pub fn as_str(self) -> &'static str {
252        match self {
253            TokenTypeIdentifier::AccessToken => "urn:ietf:params:oauth:token-type:access_token",
254            TokenTypeIdentifier::RefreshToken => "urn:ietf:params:oauth:token-type:refresh_token",
255            TokenTypeIdentifier::IdToken => "urn:ietf:params:oauth:token-type:id_token",
256            TokenTypeIdentifier::Saml1 => "urn:ietf:params:oauth:token-type:saml1",
257            TokenTypeIdentifier::Saml2 => "urn:ietf:params:oauth:token-type:saml2",
258            TokenTypeIdentifier::Jwt => "urn:ietf:params:oauth:token-type:jwt",
259        }
260    }
261}
262
263impl fmt::Display for TokenTypeIdentifier {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        f.write_str(self.as_str())
266    }
267}
268
269/// The rejection for a `*_token_type` value that is not one RFC 8693 section 3 registers.
270///
271/// The payload is SEALED and read through [`UnknownTokenTypeIdentifier::identifier`], matching
272/// [`crate::par::RequestObjectKeyError`], which is the crate's other one-payload rejection type.
273/// The two disagreed: this one published its `String` as a tuple field, so a host could also
274/// CONSTRUCT one and hand it to code that reasonably assumed only this crate mints them. Readable
275/// and not forgeable is the rule both follow now.
276#[derive(Debug, Clone, PartialEq, Eq)]
277pub struct UnknownTokenTypeIdentifier(String);
278
279impl UnknownTokenTypeIdentifier {
280    /// The unregistered identifier, exactly as it arrived.
281    ///
282    /// Echoing it back is safe and useful: it is a `*_token_type` URN out of the request, not a
283    /// token, and a host debugging an interoperability failure needs to see what the peer sent.
284    pub fn identifier(&self) -> &str {
285        &self.0
286    }
287}
288
289impl fmt::Display for UnknownTokenTypeIdentifier {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        write!(f, "unknown token type identifier {:?}", self.0)
292    }
293}
294
295impl std::error::Error for UnknownTokenTypeIdentifier {}
296
297impl FromStr for TokenTypeIdentifier {
298    type Err = UnknownTokenTypeIdentifier;
299
300    fn from_str(s: &str) -> Result<Self, Self::Err> {
301        TokenTypeIdentifier::parse(s).ok_or_else(|| UnknownTokenTypeIdentifier(s.to_string()))
302    }
303}
304
305/// Which of RFC 8693 section 1.1's two semantics an exchange produced.
306#[derive(Debug, Clone, Copy, PartialEq, Eq)]
307pub enum ExchangeSemantics {
308    /// No acting party was presented: the issued token is indistinguishable from one the subject
309    /// holds directly, within the rights context it covers.
310    Impersonation,
311    /// An acting party authenticated and is named in [`ExchangedToken::act`]: the issued token
312    /// says "the actor, acting for the subject".
313    Delegation,
314}
315
316/// The RFC 8693 section 4.1 `act` (actor) claim: who authority was delegated TO.
317///
318/// Section 4.1 is explicit about what a consumer may do with it: "the consumer of a token MUST
319/// only consider the token's top-level claims and the party identified as the current actor by the
320/// act claim", and a nested `act` is a PRIOR actor, kept for audit rather than for authorization.
321/// That is why [`ActClaim::act`] is boxed and optional rather than a flat list: the nesting IS the
322/// ordering, and flattening it would lose which actor is current.
323#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
324pub struct ActClaim {
325    /// The acting party's subject identifier.
326    pub sub: String,
327    /// RFC 8693 section 4.3 `client_id`, when the actor is (or authenticated as) a client. Present
328    /// here because for a machine-to-machine actor the client identifier is frequently the only
329    /// identity there is, and section 4.1's example carries it.
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub client_id: Option<String>,
332    /// A PRIOR actor in the delegation chain (section 4.1). The outermost claim is the current
333    /// actor; anything nested inside acted earlier.
334    ///
335    /// BOXED because the type is otherwise infinitely sized, and because the overwhelmingly common
336    /// chain is one link long and should not carry the storage for more.
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub act: Option<Box<ActClaim>>,
339}
340
341/// An RFC 8693 section 2.1 token exchange request, as the host parsed it off the wire.
342///
343/// Borrowed rather than owned: a token-endpoint request is parsed, judged and dropped, and the
344/// three credentials in here (`client_secret`, `subject_token`, `actor_token`) are strings a host
345/// should be copying as little as possible.
346///
347/// `Debug` is hand-written (see below) rather than derived, for the reason
348/// [`crate::server::TokenRequest`] gives: every one of those three is a bearer credential, and this
349/// is precisely the value a host is most likely to debug-print, because it is the request it just
350/// parsed.
351#[derive(Clone, PartialEq, Eq)]
352/// `#[non_exhaustive]`: the two `client-assertion` fields appear only under that feature, so a host
353/// that writes this as a literal is writing one of two different structs and does not get to choose
354/// which. [`TokenExchangeRequest::new`] already exists and already says the rest is "optional and
355/// set on the returned value"; the attribute is what makes that the only way in, rather than the
356/// documented way in that a literal quietly bypasses.
357#[non_exhaustive]
358pub struct TokenExchangeRequest<'a> {
359    /// The client performing the exchange. It is authenticated, and it is the client the issued
360    /// token belongs to.
361    pub client_id: &'a ClientId,
362    /// Its secret, for a client registered for `client_secret_basic` or `client_secret_post`.
363    /// A confidential credential of SOME kind is required: see [`TokenExchange::exchange_token`]
364    /// on why a public client may not use this grant.
365    pub client_secret: Option<&'a str>,
366    /// RFC 7521 section 4.2 `client_assertion_type`, for a client registered for
367    /// `private_key_jwt` or `client_secret_jwt`.
368    ///
369    /// This field and the one below exist because RFC 8693 section 2.1 authenticates the client
370    /// "as described in Section 2.3 of \[RFC6749\]", which is a reference to every method the server
371    /// offers and not to shared secrets alone. Carrying only `client_secret` meant a confidential
372    /// client registered for assertion authentication was answered `invalid_client` on a grant the
373    /// RFC 8414 document advertises to it: the credential it presented had nowhere to travel.
374    #[cfg(feature = "client-assertion")]
375    pub client_assertion_type: Option<&'a str>,
376    /// RFC 7523 section 2.2 `client-assertion`: the signed JWT itself.
377    #[cfg(feature = "client-assertion")]
378    pub client_assertion: Option<&'a str>,
379    /// REQUIRED (section 2.1). The token representing the party on whose behalf the request is
380    /// made.
381    pub subject_token: &'a str,
382    /// REQUIRED (section 2.1). The type of `subject_token`.
383    pub subject_token_type: TokenTypeIdentifier,
384    /// OPTIONAL (section 2.1). The token representing the ACTING party. Its presence is what makes
385    /// the exchange delegation rather than impersonation.
386    pub actor_token: Option<&'a str>,
387    /// REQUIRED when `actor_token` is present, and meaningless without it (section 2.1).
388    pub actor_token_type: Option<TokenTypeIdentifier>,
389    /// OPTIONAL (section 2.1). RFC 8707 resource indicators for the target service. May only
390    /// narrow what the subject token already carries.
391    pub resource: &'a [String],
392    /// OPTIONAL (section 2.1). The logical name of the target service.
393    ///
394    /// Section 2.1.1 relates `audience` and `resource`: both name where the client intends to use
395    /// the token, differing in whether the name is a URI. This crate has exactly ONE audience
396    /// model, the RFC 8707 resource indicator recorded on the grant, so an `audience` value is
397    /// checked against the same ceiling as a `resource` value. A logical name the subject token
398    /// does not already carry can therefore never be granted, and answers `invalid_target`.
399    pub audience: &'a [String],
400    /// OPTIONAL (section 2.1). Narrows the issued token's scope; never widens it.
401    pub scope: Option<&'a ScopeSet>,
402    /// OPTIONAL (section 2.1). Absent means the server's own choice, which for this server is
403    /// always an access token.
404    pub requested_token_type: Option<TokenTypeIdentifier>,
405}
406
407impl<'a> TokenExchangeRequest<'a> {
408    /// The minimum request RFC 8693 section 2.1 admits: a client, the subject token it is
409    /// exchanging, and WHAT THE CALLER SAYS THAT TOKEN IS. Everything else is optional and set on
410    /// the returned value.
411    ///
412    /// `subject_token_type` is an argument rather than a default because it is the parameter the
413    /// exchange refuses on (see [`TokenExchange::exchange_token`]): a caller that presents a refresh
414    /// token and labels it an access token is asking this server to check the string a different
415    /// way than it is going to, and refusing the mismatch is what stops the type parameter being
416    /// decorative. Section 2.1 makes it REQUIRED. Defaulting it here meant a host that assembled
417    /// the request in code, and forgot to copy the form field across, silently converted that
418    /// refusal into a pass, because the value the constructor invented is precisely the one value
419    /// that passes.
420    pub fn new(
421        client_id: &'a ClientId,
422        subject_token: &'a str,
423        subject_token_type: TokenTypeIdentifier,
424    ) -> Self {
425        TokenExchangeRequest {
426            client_id,
427            client_secret: None,
428            #[cfg(feature = "client-assertion")]
429            client_assertion_type: None,
430            #[cfg(feature = "client-assertion")]
431            client_assertion: None,
432            subject_token,
433            subject_token_type,
434            actor_token: None,
435            actor_token_type: None,
436            resource: &[],
437            audience: &[],
438            scope: None,
439            requested_token_type: None,
440        }
441    }
442}
443
444/// Hand-written so no credential reaches a debug format, while everything that identifies WHICH
445/// exchange this is stays visible. The Some/None distinction is kept for the redacted fields
446/// because it is not a credential: it is the difference between an impersonation request and a
447/// delegation request, which is the first thing anyone debugging this grant needs to see.
448impl fmt::Debug for TokenExchangeRequest<'_> {
449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        fn redact_opt<T>(value: &Option<T>) -> Option<&'static str> {
451            value.as_ref().map(|_| "[redacted]")
452        }
453        let mut out = f.debug_struct("TokenExchangeRequest");
454        out.field("client_id", &self.client_id)
455            .field("client_secret", &redact_opt(&self.client_secret));
456        // The assertion is a bearer credential for as long as it is unexpired (RFC 7523 s3), so it
457        // is redacted exactly as the secret is. The TYPE is printed, because it is not a
458        // credential (it is a fixed URN) and a mistyped one is the commonest way this
459        // authentication method fails.
460        #[cfg(feature = "client-assertion")]
461        out.field("client_assertion_type", &self.client_assertion_type)
462            .field("client_assertion", &redact_opt(&self.client_assertion));
463        out.field("subject_token", &"[redacted]")
464            .field("subject_token_type", &self.subject_token_type)
465            .field("actor_token", &redact_opt(&self.actor_token))
466            .field("actor_token_type", &self.actor_token_type)
467            .field("resource", &self.resource)
468            .field("audience", &self.audience)
469            .field("scope", &self.scope)
470            .field("requested_token_type", &self.requested_token_type)
471            .finish()
472    }
473}
474
475/// The RFC 8693 section 2.2.1 successful response.
476///
477/// Deliberately NOT [`crate::token::TokenResponse`]: section 2.2.1 adds `issued_token_type` as
478/// REQUIRED, and the meaning of `access_token` widens to "the security token issued", which need
479/// not be an OAuth access token at all. Reusing the RFC 6749 section 5.1 type would have made
480/// `issued_token_type` optional in the type system, and a REQUIRED member that the type lets you
481/// forget is a member that eventually goes missing.
482///
483/// `Debug` is hand-written so the issued token does not print, matching every other credential
484/// carrying type in this crate.
485#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
486pub struct TokenExchangeResponse {
487    /// REQUIRED (section 2.2.1). The security token issued.
488    pub access_token: String,
489    /// REQUIRED (section 2.2.1). An identifier for the representation of the issued token. Always
490    /// `urn:ietf:params:oauth:token-type:access_token` from this server.
491    pub issued_token_type: TokenTypeIdentifier,
492    /// REQUIRED (section 2.2.1), and REQUIRED even though the issued token "need not be an OAuth
493    /// access token": it states the method of using it. Always `Bearer` here (RFC 6750).
494    pub token_type: TokenType,
495    /// RECOMMENDED (section 2.2.1). This server always includes it.
496    #[serde(skip_serializing_if = "Option::is_none")]
497    pub expires_in: Option<u64>,
498    /// OPTIONAL if identical to the requested scope, REQUIRED otherwise (section 2.2.1). This
499    /// server always includes it when non-empty, which satisfies the stronger of the two: an
500    /// exchange narrows, so the issued scope frequently differs from the request.
501    #[serde(skip_serializing_if = "Option::is_none")]
502    pub scope: Option<String>,
503    /// OPTIONAL (section 2.2.1), and never issued by this server. See the module docs.
504    #[serde(skip_serializing_if = "Option::is_none")]
505    pub refresh_token: Option<String>,
506}
507
508impl fmt::Debug for TokenExchangeResponse {
509    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510        f.debug_struct("TokenExchangeResponse")
511            .field("access_token", &"[redacted]")
512            .field("issued_token_type", &self.issued_token_type)
513            .field("token_type", &self.token_type)
514            .field("expires_in", &self.expires_in)
515            .field("scope", &self.scope)
516            .field(
517                "refresh_token",
518                &self.refresh_token.as_ref().map(|_| "[redacted]"),
519            )
520            .finish()
521    }
522}
523
524/// What an exchange produced: the wire response, plus the two things a host needs that are NOT
525/// wire response members.
526///
527/// `semantics` and `act` are separated from [`TokenExchangeResponse`] rather than living on it
528/// with `#[serde(skip)]`, because RFC 8693 section 2.2.1 defines the response members exhaustively
529/// and `act` is a TOKEN CLAIM (section 4.1), not a response parameter. A type that serialized to
530/// something the RFC does not define would be wrong in the one place this crate cannot afford to
531/// be approximate.
532#[derive(Debug, Clone, PartialEq, Eq)]
533pub struct ExchangedToken {
534    /// The RFC 8693 section 2.2.1 body, ready to serialize.
535    pub response: TokenExchangeResponse,
536    /// Whether this exchange was delegation or impersonation (section 1.1).
537    pub semantics: ExchangeSemantics,
538    /// The section 4.1 `act` claim, present exactly when `semantics` is
539    /// [`ExchangeSemantics::Delegation`]. See the module docs for why this crate hands it back
540    /// rather than putting it in its own opaque token.
541    pub act: Option<ActClaim>,
542}
543
544/// RFC 8693 token exchange, as an extension trait on [`AuthorizationServer`].
545///
546/// A trait rather than an inherent method because the grant is behind an off-by-default cargo
547/// feature, and a feature should add a name a reader can find rather than silently change the
548/// shape of a type they already know.
549pub trait TokenExchange {
550    /// Exchange `subject_token` for a new token, per RFC 8693 section 2.
551    ///
552    /// The caller MUST be a confidential client. RFC 8693 gives the AS the decision (section 2.1
553    /// leaves client authentication to the deployment), and this server decides the same way it
554    /// decides for client credentials, introspection and revocation: a public client identifier is
555    /// a string anyone may claim, so "authenticated as a public client" is a sentence true of
556    /// every caller on the internet. A grant whose entire job is to convert one principal's
557    /// authority into another's cannot rest on that.
558    ///
559    /// The client's registration must also include
560    /// [`crate::grant::GrantType::TokenExchange`], or the answer is `unauthorized_client`. Who may
561    /// exchange is a deployment decision, and this crate records deployment decisions about grants
562    /// in exactly one place.
563    fn exchange_token(
564        &self,
565        request: &TokenExchangeRequest<'_>,
566    ) -> impl std::future::Future<Output = Result<ExchangedToken, ErrorResponse>> + Send;
567}
568
569impl<S: Storage, C: Clock> TokenExchange for AuthorizationServer<S, C> {
570    async fn exchange_token(
571        &self,
572        request: &TokenExchangeRequest<'_>,
573    ) -> Result<ExchangedToken, ErrorResponse> {
574        let outcome = exchange(self, request).await;
575        // The same audit answer every other grant gets from
576        // `AuthorizationServer::token_with_resources`: a host that installed a sink hears about
577        // refusals, and a refused exchange is a more interesting line than most, because the thing
578        // being refused is usually an attempt to widen.
579        if let Err(error) = &outcome {
580            self.hooks().emit(|| Event::GrantRefused {
581                client_id: request.client_id.as_str(),
582                grant_type: GrantType::TokenExchange,
583                error: error.error,
584            });
585        }
586        outcome
587    }
588}
589
590/// The host sees the real error through its own `Storage` impl; the wire gets the opaque code.
591/// Same shape as `server.rs`'s own helper, which is private to that module.
592fn storage_error(e: StorageError) -> ErrorResponse {
593    let _ = e;
594    ErrorResponse::new(ErrorCode::ServerError)
595}
596
597/// The refusal for a sender-constrained subject token. One function for both mechanisms, so DPoP
598/// and mutual TLS cannot drift into two different answers for one rule.
599///
600/// Feature gated because both of its callers are, and a function with no caller is a warning the
601/// gate treats as an error.
602#[cfg(any(feature = "dpop", feature = "mtls"))]
603fn sender_constrained_refusal(mechanism: &str) -> ErrorResponse {
604    ErrorResponse::new(ErrorCode::InvalidRequest).with_description(format!(
605        "subject_token is sender constrained by {mechanism} and cannot be exchanged for a token \
606         that is not, because the issued token would belong to a client that cannot prove \
607         possession of the binding key"
608    ))
609}
610
611/// How many actors an RFC 8693 section 4.1 `act` claim names, counting the outermost (current) one.
612///
613/// ITERATIVE rather than recursive, and that is the point of writing it out. The claim is a linked
614/// list this server READS BACK OUT OF ITS OWN STORE, and a store is something a host operates: a
615/// record deeper than the stack could take would be a crash on the token endpoint rather than a
616/// refusal, which is the failure mode `MAX_ACT_CHAIN_DEPTH` exists to prevent and not one to
617/// reintroduce in the counting. It also stops early, because the only question the caller asks is
618/// whether the bound has been reached.
619fn act_chain_depth(act: &ActClaim) -> usize {
620    let mut depth = 1;
621    let mut current = &act.act;
622    while let Some(next) = current {
623        depth += 1;
624        if depth >= MAX_ACT_CHAIN_DEPTH {
625            return depth;
626        }
627        current = &next.act;
628    }
629    depth
630}
631
632/// The exchange itself. Split out of the trait method so the audit emission above wraps every exit
633/// from it, exactly as `AuthorizationServer::emit_refusal` wraps the other four grants.
634async fn exchange<S: Storage, C: Clock>(
635    server: &AuthorizationServer<S, C>,
636    request: &TokenExchangeRequest<'_>,
637) -> Result<ExchangedToken, ErrorResponse> {
638    // 1. What is being asked for (section 2.1 `requested_token_type`). Checked FIRST and before
639    //    any credential is looked at, because it costs nothing and because a client asking for a
640    //    SAML assertion from a server that issues opaque bearer tokens has made a mistake that no
641    //    amount of correct authentication will fix.
642    //
643    //    Section 2.1 leaves the type to the server when the parameter is absent, and this server
644    //    issues exactly one kind of token, so absent and `access_token` are the same request.
645    let issued_token_type = match request.requested_token_type {
646        None | Some(TokenTypeIdentifier::AccessToken) => TokenTypeIdentifier::AccessToken,
647        Some(_) => {
648            return Err(
649                ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
650                    "this server issues only urn:ietf:params:oauth:token-type:access_token",
651                ),
652            )
653        }
654    };
655
656    // 2. The client. See `TokenExchange::exchange_token` for why confidential only.
657    // RFC 8693 s2.1 authenticates the client the same way every other grant does, so it goes
658    // through the same value, and it carries EVERY credential shape the request could have
659    // presented rather than the shared secret alone. Section 2.1 points at RFC 6749 s2.3, which is
660    // a reference to whatever methods the server offers; `Bound::secret` discarded an RFC 7523
661    // assertion, so a confidential client registered for `private_key_jwt` or `client_secret_jwt`
662    // was answered `invalid_client` on a grant this server's RFC 8414 document advertises to it.
663    //
664    // No RFC 9449 binding, and that is now ENFORCED rather than assumed: this surface is not
665    // handed a proof, so a token issued here cannot be bound, and the HTTP router refuses a
666    // token-exchange request that presented one rather than quietly issuing an unbound token. See
667    // this module's "A SENDER-CONSTRAINED subject token is REFUSED" section, which makes the same
668    // argument about the subject token and would contradict itself if the ISSUED token were
669    // silently downgraded.
670    let bound = Bound {
671        cred: ClientCredential {
672            client_secret: request.client_secret,
673            #[cfg(feature = "client-assertion")]
674            client_assertion_type: request.client_assertion_type,
675            #[cfg(feature = "client-assertion")]
676            client_assertion: request.client_assertion,
677            // RFC 8705 is deliberately NOT threaded through here. A certificate authenticates AND
678            // binds (section 3), and this surface has nowhere to record the binding, so accepting
679            // one would issue an unbound token to a client that proved possession of a key: the
680            // silent downgrade this module refuses everywhere else. An mTLS-only client is
681            // refused `invalid_client` here, which is loud and is the honest answer until the
682            // exchange can carry a binding.
683            #[cfg(feature = "mtls")]
684            certificate: None,
685        },
686        #[cfg(feature = "dpop")]
687        jkt: None,
688    };
689    let client = server
690        .authenticate_client(request.client_id, &bound.cred)
691        .await?;
692    // BARE, and the reason goes to the audit channel. The third site of one shape: see the
693    // introspection and client-credentials twins in `crate::server`. A description here was
694    // reachable by a caller presenting NO credential at all (a public registration authenticates
695    // trivially), so it was the one answer on this endpoint meaning "this client id is registered,
696    // and it is public", while `authenticate_client` returns a bare `invalid_client` for both an
697    // unknown id and a wrong secret. The `unauthorized_client` below is NOT the same exposure: it
698    // is only reachable by a caller that has already proved a confidential credential.
699    if !client.auth.is_confidential() {
700        server.hooks().emit(|| Event::ClientAuthenticationFailed {
701            client_id: request.client_id.as_str(),
702            failure: crate::events::ClientAuthFailure::NotConfidential,
703        });
704        return Err(ErrorResponse::new(ErrorCode::InvalidClient));
705    }
706    if !client.allows_grant(GrantType::TokenExchange) {
707        return Err(ErrorResponse::new(ErrorCode::UnauthorizedClient)
708            .with_description("client registration does not include the token-exchange grant"));
709    }
710
711    // 3. The subject token (section 2.1). Only this server's own access tokens are accepted, and
712    //    the type identifier has to SAY so: a caller that presents a refresh token and labels it
713    //    an access token, or presents an access token and labels it a JWT, is asking this server
714    //    to check the string a different way than it is going to. Refusing on the mismatch rather
715    //    than on the lookup is what stops the type parameter becoming decorative.
716    if request.subject_token_type != TokenTypeIdentifier::AccessToken {
717        return Err(
718            ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
719                "subject_token_type must be urn:ietf:params:oauth:token-type:access_token",
720            ),
721        );
722    }
723    let subject = server
724        .introspect(request.subject_token)
725        .await
726        .map_err(storage_error)?
727        // Unknown, expired and revoked are one answer: the caller holds a string this server will
728        // not exchange, and which of the three it is describes a token they may not hold.
729        .ok_or_else(|| {
730            ErrorResponse::new(ErrorCode::InvalidRequest)
731                .with_description("subject_token is not a live access token")
732        })?;
733
734    // 3b. The subject token's SENDER CONSTRAINING (RFC 9449 section 6, RFC 8705 section 3). A bound
735    //     token may not be exchanged, because the token this server would issue is a BEARER token
736    //     for a different client, and issuing it converts a token that survives theft into one that
737    //     does not. The exchanging client proved possession of its own secret and of nothing else:
738    //     it presented the subject token as a string, which is precisely what a thief also has. See
739    //     the module docs for why this is a refusal rather than a propagated `cnf`.
740    //
741    //     RFC 8693 section 2.2.2 gives `invalid_request` for a subject token that is unacceptable
742    //     based on policy, which is what this is. The description names the mechanism because a
743    //     legitimate client hitting this needs to know WHY, and it is not a secret: the client just
744    //     presented the token that carries the binding.
745    //
746    //     GUARDED by `ServerConfig::allow_sender_constrained_exchange`, which is `false` by
747    //     default. The opt-in exists only because 0.9.0 and earlier performed this downgrade
748    //     SILENTLY, so a deployment already built on it needs a migration window; a host that sets
749    //     it has decided its delegation topology is trusted enough to hold the binding for it. What
750    //     it gives up is stated on the field, and it is the whole of what DPoP or mutual TLS was
751    //     bought for.
752    #[cfg(feature = "dpop")]
753    if subject.jkt.is_some() && !server.config().allow_sender_constrained_exchange {
754        return Err(sender_constrained_refusal("DPoP (RFC 9449)"));
755    }
756    #[cfg(feature = "mtls")]
757    if subject.x5t_s256.is_some() && !server.config().allow_sender_constrained_exchange {
758        return Err(sender_constrained_refusal("mutual TLS (RFC 8705)"));
759    }
760
761    // 4. The actor token (section 2.1), which is what makes this delegation rather than
762    //    impersonation (section 1.1).
763    let (semantics, act) = match (request.actor_token, request.actor_token_type) {
764        (None, None) => (ExchangeSemantics::Impersonation, None),
765        // Section 2.1 makes actor_token_type REQUIRED when actor_token is present. The reverse is
766        // not a defined request at all, and reading it as impersonation would silently discard a
767        // parameter the client thought it was sending.
768        (None, Some(_)) => {
769            return Err(ErrorResponse::new(ErrorCode::InvalidRequest)
770                .with_description("actor_token_type is meaningless without actor_token"))
771        }
772        (Some(_), None) => {
773            return Err(ErrorResponse::new(ErrorCode::InvalidRequest)
774                .with_description("actor_token_type is required when actor_token is present"))
775        }
776        (Some(actor_token), Some(actor_token_type)) => {
777            if actor_token_type != TokenTypeIdentifier::AccessToken {
778                return Err(
779                    ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
780                        "actor_token_type must be urn:ietf:params:oauth:token-type:access_token",
781                    ),
782                );
783            }
784            // ONE refusal for every way an actor token can be unusable, built once so the two
785            // sites below cannot drift apart. See the comment on the ownership check for why the
786            // mismatch is not allowed its own wording.
787            let unusable = || {
788                ErrorResponse::new(ErrorCode::InvalidRequest)
789                    .with_description("actor_token is not a live access token")
790            };
791            let actor = server
792                .introspect(actor_token)
793                .await
794                .map_err(storage_error)?
795                .ok_or_else(unusable)?;
796            // The acting party must be the party that just authenticated. Section 4.1 has the
797            // `act` claim "identify the acting party to whom authority has been delegated", and a
798            // server that will write any name there on production of a bearer string is not
799            // identifying anybody: it is transcribing. The client proved possession of its secret
800            // one step ago; the holder of a third party's access token proved possession of a
801            // string that leaks.
802            //
803            // THE REFUSAL IS THE SAME STRING AS THE ONE ABOVE, and that is the point rather than
804            // an economy. A description naming the ownership mismatch answers a question the
805            // caller was not entitled to ask: it says the string they presented IS a live access
806            // token, and that it belongs to somebody else. Any client holding this grant could
807            // then test arbitrary strings and learn which ones are live tokens of other clients,
808            // which is exactly the oracle `introspection_response` refuses to be for the same
809            // three cases ("unknown, expired, or somebody else's"), and which the subject token
810            // seventy lines above already collapses. A legitimate delegating client presents its
811            // OWN token here, so it never reads either string.
812            if actor.client_id != client.client_id {
813                return Err(unusable());
814            }
815            // THE PRIOR CHAIN, and how much of it this server will carry. Counted BEFORE anything
816            // is built, so a refusal costs one walk of a bounded list and no allocation.
817            if let Some(prior) = &subject.act {
818                if act_chain_depth(prior) >= MAX_ACT_CHAIN_DEPTH {
819                    return Err(
820                        ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
821                            "the subject token's act chain is already at this server's maximum \
822                         delegation depth",
823                        ),
824                    );
825                }
826            }
827            let act = ActClaim {
828                // RFC 9068 section 2.2's answer for a grant with no resource owner is the client
829                // identifier, and the same answer is right here: an actor token minted by the
830                // client credentials grant names no user, and the acting party is then the client
831                // itself rather than nobody.
832                sub: actor
833                    .subject
834                    .clone()
835                    .unwrap_or_else(|| actor.client_id.as_str().to_string()),
836                client_id: Some(actor.client_id.as_str().to_string()),
837                // Section 4.1's nesting expresses a chain of PRIOR actors, and this is where it
838                // comes from: whatever the SUBJECT token already recorded, moved one level in, so
839                // the new actor is the outermost and current one.
840                //
841                // This was `None` with a comment saying "this server does not yet carry `act`
842                // inside its own tokens", which 0.9.1 made false in the same file: the claim is
843                // persisted on `IssuedToken` and reported by introspection. Truncating on the
844                // strength of that stale comment turned A -> B -> C into `act={sub:C}`, where
845                // section 4.1 defines `{sub:C, act:{sub:B}}`, so a resource server auditing the
846                // chain was told the delegation started at B. Depth is bounded by
847                // `MAX_ACT_CHAIN_DEPTH`, checked above.
848                act: subject.act.clone(),
849            };
850            (ExchangeSemantics::Delegation, Some(act))
851        }
852    };
853
854    // 5. SCOPE, first ceiling. RFC 6749 section 6's narrowing rule, applied to the subject token's
855    //    granted scope. This is the attack: the whole value of an exchange to an attacker is
856    //    getting out more than went in.
857    let scope = match request.scope {
858        None => subject.scope.clone(),
859        Some(s) if s.is_subset(&subject.scope) => s.clone(),
860        Some(_) => {
861            return Err(
862                ErrorResponse::new(ErrorCode::InvalidScope).with_description(
863                    "token exchange may narrow the subject token scope, never widen it",
864                ),
865            )
866        }
867    };
868    // SCOPE, second ceiling. The issued token belongs to the EXCHANGING client, so it cannot carry
869    // scope that client's own registration never permitted. See the module docs: without this, a
870    // read-only client that momentarily holds someone else's write token can mint itself one.
871    if !scope.is_subset(&client.allowed_scopes) {
872        return Err(ErrorResponse::new(ErrorCode::InvalidScope)
873            .with_description("scope exceeds the exchanging client registration"));
874    }
875    // RFC 9396 DETAILS, and the reason this refusal sits with the scope ceilings rather than near
876    // the propagation site: it is the SAME ceiling, and its absence was the whole defect.
877    //
878    // Scope gets two ceilings above. `authorization_details` got none: they were copied onto the
879    // issued token unchanged, justified by "it is exactly what the token the client just presented
880    // already carried". That is precisely the argument the second ceiling four lines up REJECTS for
881    // scope, and for the same reason, which is that the issued token belongs to the EXCHANGING
882    // client and not to the one the subject token was minted for.
883    //
884    // The asymmetry ran the wrong way. A RAR element is strictly more specific than the scope that
885    // accompanies it (RFC 9396 exists because a scope token cannot say "transfer 50 euros to IBAN
886    // X"), so the crate applied its weaker rule to its more dangerous grant. Concretely: a
887    // downstream service registered for `read`, holding a payments client's token because
888    // forwarding the caller's token is what this grant is FOR, could ask for `read`, satisfy both
889    // scope ceilings, and receive a token issued to ITSELF carrying the payment authorization, with
890    // a fresh lifetime that outlives the token it came from.
891    //
892    // There is nothing to narrow AGAINST: `Client` has `allowed_scopes` and no equivalent for
893    // detail types, so the per-client ceiling that would make this safe does not exist yet and
894    // adding it breaks a type hosts construct. So this refuses, and a host that has reasoned about
895    // its delegation topology can say so with `allow_authorization_details_exchange`.
896    //
897    // RFC 8693 section 2.2.2 names `invalid_request` for a subject token unacceptable on policy,
898    // which is what this is; `invalid_target` is for a requested RESOURCE the AS will not issue
899    // for, and the client requested no target here. The description names the mechanism because a
900    // legitimate client hitting this needs to know why, and it reveals nothing: the client is
901    // holding the token whose details these are.
902    //
903    // SCOPED TO A CROSS-CLIENT EXCHANGE, and that scoping is the argument rather than a
904    // convenience. Everything above turns on the issued token belonging to a DIFFERENT principal
905    // than the subject token. When the exchanging client IS the client the subject token was issued
906    // to, no boundary is crossed: it already holds those details, on a token it can already spend,
907    // and refusing would block a client from exchanging its own token for a narrower one, which is
908    // an ordinary and safe use of this grant. A blanket refusal here would have been over-broad,
909    // and the test that caught it was right to.
910    #[cfg(feature = "rar")]
911    if !subject.authorization_details.is_empty()
912        && subject.client_id != client.client_id
913        && !server.config().allow_authorization_details_exchange
914    {
915        return Err(
916            ErrorResponse::new(ErrorCode::InvalidRequest).with_description(
917                "subject_token carries authorization_details and cannot be exchanged for a token \
918             issued to a different client, because this server has no per-client registration of \
919             the detail types that client may hold",
920            ),
921        );
922    }
923
924    // 6. RESOURCE and AUDIENCE, the audience ceiling (RFC 8707 section 2, RFC 8693 section 2.1.1).
925    //    `validate_resources` and `narrow_resources` are the SAME functions the authorization code
926    //    and refresh grants use. That is deliberate and is the point: a second implementation of
927    //    "may narrow, never widen" is a second thing to get wrong, and this one would be the one
928    //    nobody reviews.
929    let mut targets = server.validate_resources(request.resource.iter().map(|r| r.as_str()))?;
930    // Section 2.1.1: `audience` names the same thing as `resource` in a form that need not be a
931    // URI. It is NOT run through `validate_resources`, which requires an absolute URI, because
932    // that would report a well-formed logical name as malformed; it goes to the same ceiling
933    // instead, where a name the subject token does not carry is `invalid_target` rather than
934    // `invalid_request`. A deployment whose audiences are not resource indicators therefore finds
935    // that no `audience` value is grantable here, which is the truth rather than a silent success.
936    // CAPPED, for the reason `validate_resources` is capped and with the same number: `audience`
937    // is repeatable, it is deduplicated against `targets` with an O(n) scan per element, and
938    // nothing bounded either side. Checked on the INPUT length rather than on `targets`, because a
939    // caller sending one value ten thousand times still pays a full scan per copy even though the
940    // result stays small.
941    if request.audience.len() > MAX_AUDIENCE_VALUES {
942        return Err(ErrorResponse::new(ErrorCode::InvalidTarget)
943            .with_description("too many audience values (RFC 8693 s2.1.1)"));
944    }
945    for audience in request.audience {
946        // SKIPPING THE SYNTAX CHECK IS NOT SKIPPING THE ALLOWLIST, and through 0.9.1 it was both.
947        // `ServerConfig::allowed_resources` is this server's RFC 8707 section 2 statement of what
948        // it is unwilling to issue for — the way an operator decommissions a resource server — and
949        // the `audience` spelling walked straight past it. An operator who removed R from that list
950        // went on handing out signed tokens whose `aud` names R, to any client holding a live token
951        // whose grant had recorded R, because `narrow_resources` below only asks whether the
952        // SUBJECT TOKEN carries the value and never whether the server still stands behind it.
953        server.target_is_permitted(audience)?;
954        if !targets.iter().any(|t| t == audience) {
955            targets.push(audience.clone());
956        }
957    }
958    // `narrow_and_permit`, not `narrow_resources`: the allowlist has to apply to what is ISSUED and
959    // not only to what was NAMED. The loop above covers an `audience` the request spelled out, but
960    // a request naming NEITHER `resource` nor `audience` inherits the subject token's whole
961    // recorded list untouched, and that list can name a resource server this deployment has since
962    // decommissioned. See `AuthorizationServer::narrow_and_permit`.
963    let resource = server.narrow_and_permit(&subject.resource, &targets)?;
964
965    // 7. Issue. Through the SAME `issue` every other grant goes through, so the exchanged token is
966    //    persisted, introspectable, revocable and (with the `jwt` feature) signed exactly like any
967    //    other token this server mints.
968    //
969    //    `None` chain and `false` for refresh: RFC 8693 section 2.2.1 on why no refresh token, and
970    //    a token with no chain has no family, which is right here because there is nothing to
971    //    rotate and so nothing a reuse detection could revoke.
972    let issued = server
973        .issue(
974            &client,
975            &bound,
976            GrantType::TokenExchange,
977            // INHERITED from the subject token, not restamped. The exchanged token derives its
978            // authority from that grant, so a revocation reaching the grant must reach everything
979            // exchanged out of it; stamping `now` here would let an exchange launder a token past
980            // the revocation of the decision it descends from.
981            //
982            // THE INSTANT IS INHERITED AND THE IDENTITY IS NOT, and that is a gap rather than a
983            // decision. A `RevocationBarrier` for a withdrawn consent, and the cascade underneath
984            // it, both ask for the (client_id, subject) pair of the grant being ended; the token
985            // issued below carries the subject and the instant but belongs to the EXCHANGING
986            // client, so a cross-client exchange lands outside the reach of the withdrawal of the
987            // consent it descends from. `Storage::revoke_consent` states the same thing from the
988            // other side, with what it costs and what closing it needs. The lifetime ceiling at the
989            // end of this call is what bounds it: the descendant cannot outlive the token it was
990            // exchanged out of, so the overrun is one access token lifetime and not a fresh grant.
991            subject.grant_established_at,
992            subject.subject.clone(),
993            scope,
994            resource,
995            // RFC 9396: the exchanged token inherits the subject token's authorization details
996            // unchanged, and reaching this line at all means the ceiling above let it. Either the
997            // subject token carried no details, or the host set
998            // `allow_authorization_details_exchange` and accepted what that gives up.
999            //
1000            // The justification that used to sit here said propagation "is never a widening,
1001            // because it is exactly what the token the client just presented already carried". That
1002            // was false, and it was false in the same way for scope, where this file rejects the
1003            // identical argument two ceilings earlier: the issued token belongs to the EXCHANGING
1004            // client, so what the SUBJECT token was allowed to carry is not the question.
1005            crate::server::GrantedDetails::of_token(&subject),
1006            None,
1007            false,
1008            // RFC 8693 s1: a client is presenting a token, not a user presenting themselves.
1009            // Nobody authenticated during this request, so there is nothing to report, and
1010            // carrying the subject token's report forward would let an exchange launder a stale
1011            // authentication into a token that looks freshly stepped up.
1012            crate::server::GrantedAuthentication::default(),
1013            // THE DELEGATION, recorded on the token itself. `act` is `Some` only for the
1014            // delegation branch above; an impersonation exchange names no actor by definition.
1015            // This is what lets RFC 7662 introspection answer "A acting for B" rather than "B",
1016            // which for an OPAQUE token is the only channel that could.
1017            crate::server::GrantedActor {
1018                act: act.clone().map(Box::new),
1019            },
1020            // THE LIFETIME CEILING, and the reason it has to exist at all. The token issued here
1021            // is an ordinary access token, so it is itself an acceptable SUBJECT token, and
1022            // self-exchange is explicitly permitted a few ceilings above. Without a ceiling a
1023            // client could re-exchange just before each expiry and receive a fresh full
1024            // `access_token_ttl` every time, indefinitely, which renews the grant's lifetime
1025            // without limit. That is precisely what the "no refresh token is issued" rule in this
1026            // module's docs exists to prevent, defeated by the issued token's own type.
1027            //
1028            // `min(now + access_token_ttl, subject.expires_at)` is applied inside `issue`, so the
1029            // stored expiry, the RFC 9068 `exp` claim and the `expires_in` below all state the one
1030            // capped instant. An exchanged token can therefore be narrower in time as it is in
1031            // scope, details and audience, and never wider.
1032            Some(subject.expires_at),
1033        )
1034        .await?;
1035
1036    Ok(ExchangedToken {
1037        response: TokenExchangeResponse {
1038            access_token: issued.access_token,
1039            issued_token_type,
1040            token_type: issued.token_type,
1041            expires_in: Some(issued.expires_in),
1042            scope: issued.scope,
1043            // `issue` was told not to, and this asserts it rather than assuming it: section 2.2.1
1044            // makes the member optional, so a stray refresh token would serialize silently.
1045            refresh_token: None,
1046        },
1047        semantics,
1048        act,
1049    })
1050}
1051
1052#[cfg(test)]
1053#[path = "tests/token_exchange.rs"]
1054mod tests;