#[non_exhaustive]pub struct AuthorizationCodeRecord {Show 14 fields
pub code: String,
pub client_id: ClientId,
pub redirect_uri: String,
pub redirect_uri_was_explicit: bool,
pub scope: ScopeSet,
pub subject: String,
pub code_challenge: String,
pub code_challenge_method: CodeChallengeMethod,
pub resource: Vec<String>,
pub authorization_details: AuthorizationDetails,
pub issued_at: SystemTime,
pub expires_at: SystemTime,
pub state: AuthorizationCodeState,
pub authentication: Option<Box<Authentication>>,
}Expand description
A persisted authorization code (RFC 6749 section 4.1.2).
Debug is hand-written (see below): code is itself a bearer credential (RFC 6749 section
4.1.2 treats a leaked code as equivalent to a leaked token for as long as it is live, which is
why replay revokes what it minted, see AuthorizationCodeState’s doc comment), so it must
not appear in a debug format either.
#[non_exhaustive]: rar and consent each add a field. Like the two token records this is a
value a crate::store::Storage implementor is HANDED and gives back, through the derived
serde impls, which are generated in this crate and keep working from outside it; nothing in
oauth-as-postgres names a field of it. AuthorizationCodeRecord::new is for anyone building
one directly.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.code: StringThe code string (the storage key).
client_id: ClientIdThe client the code was issued to; presentation by any other client is invalid_grant.
redirect_uri: StringThe redirect URI the authorization request used; a token request that presents one must present this one.
redirect_uri_was_explicit: boolWhether the authorization request NAMED that URI, rather than omitting it and being filled in from the client’s single registration (RFC 6749 section 3.1.2.3).
RFC 6749 section 4.1.3 makes the token endpoint’s redirect_uri parameter REQUIRED “if the
redirect_uri parameter was included in the authorization request”, and conditional means
conditional in both directions: through 0.9.1 the token endpoint required it always, so a
client entitled by section 3.1.2.3 to omit it at the authorization endpoint — the ordinary
shape for a client with exactly one registered URI — was refused at the token endpoint, and
refused with a message blaming a mismatch that had not happened. redirect_uri above cannot
stand in for this, because it is filled in either way.
#[serde(default)] with a true default, so a record persisted by 0.9.0 still
deserializes. TRUE is the fail-closed direction: it keeps the check that release performed
(the parameter is required) for records minted before this field existed, rather than
silently waiving section 4.1.3’s requirement for every grant that survived the upgrade.
scope: ScopeSetThe scope the user approved.
subject: StringThe authenticated resource owner.
code_challenge: StringThe recorded PKCE challenge (RFC 7636 section 4.4).
code_challenge_method: CodeChallengeMethodThe recorded PKCE method.
resource: Vec<String>The RFC 8707 resource indicators the authorization request named.
Recorded on the code because the token request that redeems it MAY narrow this set but MUST NOT widen it (RFC 8707 section 2), and “what was granted” is not knowable at the token endpoint any other way. Empty means the client asked for no audience restriction.
rar only.The RFC 9396 authorization details the user approved.
Recorded on the code for exactly the reason resource above is: section 6 lets the
token request that redeems it NARROW this set and never widen it, and “what was
granted” is not knowable at the token endpoint any other way. Empty means the client
asked for no rich authorization detail.
#[serde(default)], which crate::token::IssuedToken::authorization_details states in
full: a code written by a build without rar carries no such key, this is not an Option
so serde supplies no default of its own, and without one every code in flight becomes
unreadable the moment anything in the host’s dependency graph turns the feature on.
issued_at: SystemTimeThe instant this code was MINTED, which is the instant the user’s authorization decision
was made. Carried into crate::token::IssuedToken::grant_established_at on redemption so
that a revocation can tell a code that predates it from one minted afterwards.
expires_at cannot stand in for this: a code minted a minute before a withdrawal expires
minutes AFTER it, so comparing the deadline would let exactly the in-flight redemption a
barrier exists to refuse through.
#[serde(default)], and the default is the epoch, which is the FAIL-CLOSED direction.
This field is new in 0.9.1, so a code a 0.9.0 node wrote — or is still writing, during a
rolling upgrade — carries no such key, and without a default the read fails outright and
every code that release minted becomes unredeemable the moment this one starts. With it,
the record deserializes and dates from before every barrier that could ever be recorded, so
a standing revocation REFUSES it rather than admitting it. A far-future default would
deserialize just as happily and ADMIT every code 0.9.0 wrote, which is exactly the
resurrection this field exists to close, reintroduced through the upgrade path. The
There is deliberately NO backfill migration: a backfill cannot reach a 0.9.0 node still
writing field-less payloads during a rolling upgrade, which is the window that matters, so
the serde default covers strictly more than one would.
expires_at: SystemTimeExpiry instant; the code is dead at and after this instant.
state: AuthorizationCodeStateWhether the code has been redeemed, and what it produced.
authentication: Option<Box<Authentication>>consent only.What the host reported about the resource owner’s authentication when this code was
approved (the consent-0.8.0 slice; see crate::consent::Authentication).
Recorded on the CODE because that is the only path by which the authentication the user
actually performed can reach the token the code mints: the token endpoint has no user in
front of it and cannot ask. Without it, RFC 9470 section 6’s auth_time and acr could
only ever be guessed at.
Implementations§
Source§impl AuthorizationCodeRecord
impl AuthorizationCodeRecord
Sourcepub fn new(
code: impl Into<String>,
client_id: ClientId,
redirect_uri: impl Into<String>,
scope: ScopeSet,
subject: impl Into<String>,
code_challenge: impl Into<String>,
expires_at: SystemTime,
) -> Self
pub fn new( code: impl Into<String>, client_id: ClientId, redirect_uri: impl Into<String>, scope: ScopeSet, subject: impl Into<String>, code_challenge: impl Into<String>, expires_at: SystemTime, ) -> Self
A freshly minted, unredeemed code: state is AuthorizationCodeState::Issued, because
the Consumed form records what a redemption produced and there is nothing to record until
one happens.
Every argument is a value the record is worthless without, and each is one the RFC names as
the thing a later token request is checked against: the redirect_uri it must present again
(RFC 6749 section 4.1.3), the code_challenge it must produce a verifier for (RFC 7636
section 4.6), the subject and scope it is redeeming on behalf of, and the instant after
which none of that is true any more. The method is S256 and not an argument, because
CodeChallengeMethod has one variant and it is one for a reason.
Trait Implementations§
Source§impl Clone for AuthorizationCodeRecord
impl Clone for AuthorizationCodeRecord
Source§fn clone(&self) -> AuthorizationCodeRecord
fn clone(&self) -> AuthorizationCodeRecord
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for AuthorizationCodeRecord
Hand-written so the one-time code never prints (RFC 6749 section 4.1.2 makes it a credential
in its own right). EVERY other field prints, on the rule
crate::token::IssuedToken’s Debug states in full. issued_at in particular: it is what
a crate::store::RevocationBarrier is compared against on redemption, its fail-closed default
is the epoch, and without it printing an operator cannot tell a code refused by a standing
barrier from one refused for any other reason.
impl Debug for AuthorizationCodeRecord
Hand-written so the one-time code never prints (RFC 6749 section 4.1.2 makes it a credential
in its own right). EVERY other field prints, on the rule
crate::token::IssuedToken’s Debug states in full. issued_at in particular: it is what
a crate::store::RevocationBarrier is compared against on redemption, its fail-closed default
is the epoch, and without it printing an operator cannot tell a code refused by a standing
barrier from one refused for any other reason.