Auth Token
Data Entity
Description
A persisted JWT access or refresh token record associated with a user session. Tracks token value, expiry, issuer (email/password, BankID, Vipps), and revocation status. Used by the backend to validate mobile API requests and by the mobile client to maintain session continuity.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Unique identifier for the token record | PKrequiredunique |
user_id |
uuid |
Foreign key reference to the owning user. A user may hold multiple active tokens across devices and token types. | required |
session_id |
uuid |
Foreign key reference to the session that produced this token. Nullable because tokens may survive beyond a discrete session record or be issued prior to full session creation. | - |
token_hash |
string |
SHA-256 hash of the raw JWT value. The raw token is never persisted; only the hash is stored for validation lookups. Backend compares hash of incoming Bearer token against this field. | requiredunique |
token_type |
enum |
Distinguishes access tokens (short-lived, used in Authorization headers) from refresh tokens (long-lived, used to obtain new access tokens without re-authentication). | required |
provider |
enum |
The authentication provider that issued the original authentication event which produced this token. Determines which identity verification path was used. | required |
issued_at |
datetime |
Timestamp when the token was generated. Used for token age calculations and audit reporting. | required |
expires_at |
datetime |
Timestamp after which the token is no longer valid. Access tokens: short-lived (15–60 minutes). Refresh tokens: long-lived (30 days). Backend rejects tokens where expires_at is in the past. | required |
last_used_at |
datetime |
Timestamp of the most recent successful use of this token for an authenticated request. Updated on each valid API call. Used for idle session detection and security audit. | - |
revoked_at |
datetime |
Timestamp when the token was explicitly revoked before natural expiry. Null means the token has not been revoked. Revoked tokens remain in the table for audit trail integrity. | - |
revocation_reason |
enum |
Reason the token was revoked. Only populated when revoked_at is non-null. | - |
token_family |
uuid |
Groups a refresh token with all access tokens it has produced. Used to detect refresh token reuse attacks: if a token from a consumed family is presented, all tokens in the family are immediately revoked. | - |
device_id |
string |
Opaque device identifier provided by the mobile client. Allows per-device token management and targeted revocation (e.g., revoke all tokens for a lost device). | - |
ip_address |
string |
IP address of the client at the time the token was issued. Stored for security audit and anomaly detection. Supports both IPv4 and IPv6 notation. | - |
user_agent |
string |
HTTP User-Agent string from the client at token issuance time. Aids in identifying the device type and app version during security incident investigation. | - |
Database Indexes
idx_auth_token_hash
Columns: token_hash
idx_auth_token_user_id
Columns: user_id
idx_auth_token_user_type
Columns: user_id, token_type
idx_auth_token_session_id
Columns: session_id
idx_auth_token_expires_at
Columns: expires_at
idx_auth_token_token_family
Columns: token_family
idx_auth_token_active
Columns: user_id, revoked_at, expires_at
Validation Rules
token_hash_format
error
Validation failed
token_hash_uniqueness
error
Validation failed
expires_at_future
error
Validation failed
user_id_exists
error
Validation failed
revocation_reason_required_when_revoked
error
Validation failed
revoked_at_immutable
error
Validation failed
token_type_valid_enum
error
Validation failed
provider_valid_enum
error
Validation failed
ip_address_format
warning
Validation failed
Business Rules
token_hash_only_storage
Raw JWT values must never be persisted to the database. Only the SHA-256 hex-encoded hash of the token is stored. The backend computes the hash of each incoming Bearer token and compares it against stored hashes.
access_token_short_expiry
Access tokens must expire within 60 minutes of issuance. This limits the window of exposure if an access token is intercepted or leaked.
refresh_token_long_expiry
Refresh tokens may have an expiry of up to 30 days. They must be stored in OS-level hardware-backed secure storage on the mobile device (Keychain/Keystore) and never in plain local storage.
revoked_tokens_retained
Revoked tokens must not be deleted immediately. They are retained with revoked_at and revocation_reason populated to preserve an immutable audit trail. Cleanup jobs may archive them after 1 year.
refresh_token_rotation
Each time a refresh token is used to obtain a new access token, the old refresh token must be revoked (reason: token_rotation) and a new refresh token issued in the same token_family. This enables reuse-attack detection.
token_family_reuse_revocation
If a refresh token that has already been consumed (revoked with reason token_rotation) is presented again, all tokens sharing the same token_family must be immediately revoked as a security_event. This indicates a stolen token is being replayed.
per_device_token_limit
A user should not accumulate unbounded active tokens. On login from a device that already has an active refresh token, the previous token for that device_id is revoked before issuing a new one.
provider_consistency
The provider field must match the authentication method used in the same session. A session initiated via BankID must not have tokens with provider='email_password'. Biometric tokens always inherit the provider of the initial full-authentication session.
cascade_revoke_on_account_deactivation
When a user account is deactivated by an admin, all active (non-expired, non-revoked) tokens for that user must be revoked with reason='account_deactivated'. This prevents deactivated users from continuing to make authenticated API requests.
jwt_claims_must_include_role_and_org
Every issued JWT must embed the user's current role and organization_id as claims. This enables stateless role-based access control validation in the auth middleware without requiring a database lookup on every request.