Skip to main content

Module refresh

Module refresh 

Source
Expand description

Refresh-token rotation with replay detection.

Sessions are minted by a TokenMinter; refresh tokens are the stateful half — short-lived access tokens are exchanged through a long-lived refresh token that rotates on every use. Each rotation:

  1. consumes the presented token,
  2. issues a fresh successor sharing the same chain_id,
  3. links the successor back to its parent.

If a consumed token is presented again, that’s a replay — the entire chain is revoked. The pattern (audited in Rauthy src/data/src/entity/sessions.rs) keeps a stolen refresh token from being usable indefinitely: the moment either the legitimate client or the attacker rotates it, the next rotation by the other party trips the replay path and severs both.

let rotator = RefreshRotator::new(store, /* ttl_seconds */ 60 * 60 * 24 * 30);
let root = rotator.mint_root(UserId::new("u1"), DeviceId::new("d1"), 1_700_000_000).await?;
let next = rotator.rotate(root.token.as_str(), 1_700_000_001).await?;
assert_eq!(next.record.chain_id, root.record.chain_id); // successor shares the chain

Structs§

ChainId
Identifier shared by every token in a single rotation chain.
RefreshRotator
Mint and rotate refresh tokens against a RefreshStore.
RefreshToken
Opaque refresh-token secret. 32 random bytes, base64url-encoded for transport. This is the client-facing wire value; only its [hash_token] digest is persisted in RefreshTokenRecord::token.
Rotated
Result of RefreshRotator::mint_root / RefreshRotator::rotate.

Constants§

CHAIN_ID_BYTES
Length of the chain identifier in bytes (128 bits — uniqueness only, not a secret).
REFRESH_TOKEN_BYTES
Length of the opaque refresh secret in bytes (256 bits of CSPRNG).