Refresh token management with family-based reuse detection.
Refresh tokens are opaque, hashed tokens stored in the user_tokens table
with context: "api_refresh". Each token belongs to a "family" identified
by a UUID. When a refresh token is rotated, the old token is marked as
superseded and a new token is created in the same family.
Reuse Detection (Auth0 Pattern)
If a superseded token is presented for rotation, the entire token family is revoked. This detects stolen refresh tokens: if an attacker uses a stolen token after the legitimate user has already rotated it, the reuse triggers family-wide revocation, protecting both parties.
Storage
Token metadata (family_id, scopes, superseded_at) is stored as JSON in
the sent_to field of the user_tokens table.
Summary
Functions
Creates a new refresh token for the given user.
Revokes a specific refresh token by marking it as superseded.
Revokes all refresh tokens for a user.
Revokes all tokens in a family by marking them as superseded.
Rotates a refresh token: supersedes the old token and creates a new one in the same family.
Functions
Creates a new refresh token for the given user.
Returns {raw_token, token_record} where raw_token is the opaque string
to send to the client.
Options
:user_token_schema- Required. The Ecto schema module for user tokens.
@spec revoke(Sigra.Config.t(), String.t(), keyword()) :: :ok | {:error, :invalid_token}
Revokes a specific refresh token by marking it as superseded.
Options
:user_token_schema- Required. The Ecto schema module for user tokens.
@spec revoke_all_for_user(Sigra.Config.t(), term(), keyword()) :: {:ok, non_neg_integer()}
Revokes all refresh tokens for a user.
Used when a password is changed to invalidate all existing refresh tokens.
Options
:user_token_schema- Required. The Ecto schema module for user tokens.
@spec revoke_family(Sigra.Config.t(), String.t(), keyword()) :: {:ok, non_neg_integer()}
Revokes all tokens in a family by marking them as superseded.
Options
:user_token_schema- Required. The Ecto schema module for user tokens.
@spec rotate(Sigra.Config.t(), String.t(), keyword()) :: {:ok, String.t(), struct(), [String.t()]} | {:error, :invalid_token | :token_expired | :reuse_detected}
Rotates a refresh token: supersedes the old token and creates a new one in the same family.
Returns {:ok, raw_new_token, new_record, scopes} on success.
Reuse Detection
If the presented token has already been superseded (i.e., it was already
rotated), this indicates token theft. The entire family is revoked and
{:error, :reuse_detected} is returned.
Options
:user_token_schema- Required. The Ecto schema module for user tokens.