core PK: id 9 required 3 unique

Description

Rotating, single-use credentials that allow clients to obtain new short-lived access tokens without re-authenticating. Belongs to a parent Session and participates in a token family for reuse-attack detection. Issued by the Authentication Module; consumed by the API HTTP client on 401 responses.

15
Attributes
6
Indexes
7
Validation Rules
9
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Surrogate primary key. Never exposed to clients — the opaque token string is the external identifier.
PKrequiredunique
session_id uuid Foreign key to the parent sessions row. Revoking the session cascades to all its refresh tokens.
required
user_id uuid Denormalized reference to the owning user. Allows efficient per-user token enumeration and bulk revocation without joining through sessions.
required
token_hash string SHA-256 hash of the opaque token string issued to the client. The plaintext token is never stored. Compared on every refresh request.
requiredunique
token_family uuid Groups all tokens in the same rotation chain. If a previously-used token from a family is presented (reuse attack), the entire family is revoked immediately.
required
replaced_by_id uuid Points to the successor refresh token created when this token was consumed during rotation. Null while active or if invalidated without rotation.
unique
client_type enum The client surface that holds this token. Drives storage expectations: mobile uses the platform secure store; web_admin uses HTTP-only cookies.
required
issued_at datetime UTC timestamp when this token was created. Used for age checks and audit trails.
required
expires_at datetime UTC timestamp after which the token is unconditionally rejected, regardless of revocation status. Typically 30 days after issuance for mobile, 24 hours for web_admin.
required
used_at datetime UTC timestamp when this token was consumed in a successful refresh exchange. Null while unused. Presentation of a used token triggers reuse-attack handling.
-
revoked_at datetime UTC timestamp when this token was explicitly invalidated. Null while valid. Set on sign-out, session revocation, admin action, or reuse detection.
-
revocation_reason enum Machine-readable cause of revocation. Used for audit log enrichment and security dashboard metrics.
-
ip_address string IPv4 or IPv6 address of the client at token issuance. Stored for anomaly detection (IP change between issuance and use). Never used for enforcement.
-
user_agent string Device or browser User-Agent string at issuance. Displayed on the active sessions admin page to help users recognize their own devices.
-
created_at datetime Database row creation timestamp. Equals issued_at for tokens created during sign-in; may differ by milliseconds for rotation-issued tokens.
required

Database Indexes

idx_refresh_tokens_token_hash
btree unique

Columns: token_hash

idx_refresh_tokens_session_id
btree

Columns: session_id

idx_refresh_tokens_user_id
btree

Columns: user_id

idx_refresh_tokens_token_family
btree

Columns: token_family

idx_refresh_tokens_expires_at
btree

Columns: expires_at

idx_refresh_tokens_user_active
btree

Columns: user_id, revoked_at, expires_at

Validation Rules

token_hash_format error

Validation failed

expires_at_after_issued_at error

Validation failed

revocation_fields_consistency error

Validation failed

used_at_before_expires_at error

Validation failed

replaced_by_id_immutability error

Validation failed

session_id_foreign_key error

Validation failed

ip_address_format warning

Validation failed

Business Rules

single_use_enforcement
on_update

Each refresh token may be used exactly once. On a valid refresh request the current token is marked used_at = now(), a new token is issued in the same family, and replaced_by_id is set to the new token's id. A second presentation of the same token after used_at is set triggers the reuse_attack rule below.

Enforced by: Auth Service
reuse_attack_family_revocation
on_update

If a token whose used_at is already set is presented again, the entire token_family is revoked immediately (all rows in the family receive revoked_at = now() and revocation_reason = reuse_attack). The associated session is also terminated. An audit event is emitted.

expiry_enforcement
always

Tokens where expires_at < now() are rejected unconditionally at the API layer, regardless of revocation state. The client must re-authenticate through the full sign-in flow.

session_cascade_revocation
on_delete

When a session is revoked (via sign-out, admin action, or forced expiry), all refresh tokens with that session_id are revoked in the same transaction with revocation_reason = session_cascade.

rotation_chain_integrity
on_create

replaced_by_id must point to a token in the same token_family. Cross-family linking is rejected. Ensures the rotation chain is auditable end-to-end.

Enforced by: Auth Service
client_type_storage_contract
on_create

Tokens with client_type = mobile are issued to be stored in the Flutter platform secure store (via secure-token-store). Tokens with client_type = web_admin are delivered only via Set-Cookie with HttpOnly + Secure + SameSite=Strict. The API never returns the raw token in a JSON response body for web_admin.

expiry_window_by_client_type
on_create

Mobile tokens expire 30 days after issuance; web_admin tokens expire 24 hours after issuance. These windows are enforced at creation time by auth-service.

Enforced by: Auth Service

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
delete_after_30days