Skip to main content

AuthorizationCodeState

Enum AuthorizationCodeState 

Source
pub enum AuthorizationCodeState {
    Issued,
    Consumed {
        access_token: Option<String>,
        refresh_token: Option<String>,
    },
    Replayed {
        access_token: Option<String>,
        refresh_token: Option<String>,
    },
}
Expand description

What an issued authorization code became, once redeemed.

Consumed codes are RETAINED until their expiry rather than deleted, because RFC 6749 section 4.1.2 and RFC 9700 section 4.1.1 want a replayed code to revoke the tokens it already minted. Deleting the record on redemption would make a replay indistinguishable from a typo, and the stolen access token would stay live. Debug is hand-written (see below), for the same reason as crate::client::ClientAuth: the Consumed variant carries the access and refresh tokens this code minted, and those are bearer credentials that a host’s tracing::debug!(?record) must not write to a log in plaintext.

Variants§

§

Issued

Issued and not yet redeemed.

§

Consumed

Already redeemed, recording what it minted so a replay can revoke it.

Fields

§access_token: Option<String>

The access token issued, if the issuance got as far as producing one.

None means the code was marked consumed and the issuance that followed did not complete. That is deliberate and it is not a lost write: the record is written BEFORE issuance precisely so that a store failure halfway through a redemption cannot take the replay alarm offline with it (see AuthorizationServer::authorization_code_token). There is genuinely nothing to revoke in that case, because nothing was issued, and a replay of the code is still recognised as a replay.

§refresh_token: Option<String>

The refresh token issued, if any.

§

Replayed

Consumed, AND presented again afterwards. A detected replay, recorded DURABLY.

§Why this is a state and not a boolean on the side

It exists to be read by a redemption that is still running. The interleaving it closes: redeemer A takes the code, writes Consumed { access_token: None, .. } before issuing (so that a store failure cannot disarm the alarm), and then SUSPENDS on the host’s crate::jwt::Es256Signer, which is a network round trip when that signer fronts a KMS. Replayer B arrives in that window, finds Consumed { access_token: None }, and correctly concludes there is nothing to revoke, because nothing has been issued YET. B refuses the replay and puts the record back.

If what B puts back is Consumed, it is byte for byte what A wrote, so when A wakes and records what it minted, A cannot tell that anything happened. The replay was detected, the audit event fired, and A’s freshly minted access token and refresh chain are live. The alarm rang and nothing was contained.

Replayed is the trace A can see. A’s second write is a compare-and-swap against the Consumed it wrote itself (see crate::store::Storage::compare_and_swap_authorization_code), so this state makes it fail, and A undoes its own issuance.

It carries the same two fields because a THIRD presentation is still a replay and must still revoke whatever is by then known to have been minted.

Fields

§access_token: Option<String>

The access token issued, if the redemption that consumed this code got as far as producing one before the replay was detected.

§refresh_token: Option<String>

The refresh token issued, if any.

Implementations§

Source§

impl AuthorizationCodeState

Source

pub fn minted(&self) -> Option<(Option<&str>, Option<&str>)>

What this code minted, for the two states that can name it.

One accessor rather than two matches at each call site: the replay path treats Consumed and Replayed identically when deciding what to revoke, and the only difference between them is which one a concurrent redemption is allowed to overwrite.

Trait Implementations§

Source§

impl Clone for AuthorizationCodeState

Source§

fn clone(&self) -> AuthorizationCodeState

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AuthorizationCodeState

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for AuthorizationCodeState

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for AuthorizationCodeState

Source§

impl PartialEq for AuthorizationCodeState

Source§

fn eq(&self, other: &AuthorizationCodeState) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for AuthorizationCodeState

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for AuthorizationCodeState

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.