Biometric Credential
Data Entity
Description
Stores device-level biometric enrollment references for users who have registered Face ID or fingerprint authentication after completing their first BankID or Vipps login. Credential material never leaves the device secure enclave; only a reference identifier is stored.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Immutable primary key, generated server-side on enrollment. | PKrequiredunique |
user_id |
uuid |
Foreign key referencing the users table. Links the credential to the owning user account. | required |
device_id |
string |
Stable platform device identifier (iOS identifierForVendor / Android ID). Used to differentiate enrollments across multiple devices owned by the same user. | required |
credential_type |
enum |
The biometric modality used for this credential enrollment. | required |
platform |
enum |
The mobile OS platform on which this credential is enrolled. | required |
credential_reference |
string |
Opaque reference token returned by the device secure enclave / Keystore after successful biometric enrollment. Never contains raw biometric data. Used by the app to retrieve the stored Supabase session token via Flutter Secure Storage. | required |
device_name |
string |
Human-readable device name (e.g., 'iPhone 15 Pro') sourced from the OS at enrollment time. Displayed to the user in credential management UIs. | - |
is_active |
boolean |
Whether this credential is currently valid and usable for authentication. Set to false on logout, password change, or explicit revocation rather than hard deleting the row. | required |
enrolled_at |
datetime |
Timestamp (UTC) when the biometric credential was first registered after a successful BankID or Vipps authentication session. | required |
last_used_at |
datetime |
Timestamp (UTC) of the most recent successful biometric authentication using this credential. Updated on each successful challenge. Null if the credential was enrolled but never yet used for login. | - |
revoked_at |
datetime |
Timestamp (UTC) when the credential was explicitly deactivated. Null for active credentials. | - |
revocation_reason |
enum |
Machine-readable reason for revocation, useful for auditing and support diagnosis. | - |
created_at |
datetime |
Row creation timestamp, set by Supabase default. | required |
updated_at |
datetime |
Row last-update timestamp, maintained by a Supabase trigger. | required |
Database Indexes
idx_biometric_credentials_user_id
Columns: user_id
idx_biometric_credentials_user_device
Columns: user_id, device_id
idx_biometric_credentials_user_active
Columns: user_id, is_active
idx_biometric_credentials_last_used_at
Columns: last_used_at
Validation Rules
device_id_non_empty
error
Validation failed
credential_reference_non_empty
error
Validation failed
enrolled_at_not_future
error
Validation failed
last_used_not_before_enrolled
error
Validation failed
valid_credential_type_enum
error
Validation failed
valid_platform_enum
error
Validation failed
user_id_exists
error
Validation failed
device_name_length
error
Validation failed
revocation_reason_required_when_inactive
error
Validation failed
Business Rules
require_prior_national_id_auth
A biometric credential may only be created after the user has completed a successful BankID or Vipps authentication in the current session. Enrollment is blocked for users who have only authenticated via email/password.
one_credential_per_device_per_user
Each (user_id, device_id) pair must be unique. If a credential already exists for this device, the existing record must be revoked and replaced rather than creating a duplicate row.
max_enrolled_devices_per_user
A single user may have at most 5 active biometric credentials across different devices. Enrolling on a sixth device requires the user to explicitly revoke an existing credential first.
no_raw_biometric_data_stored
The credential_reference field must contain only the opaque reference token from the device secure enclave / Android Keystore. Raw biometric templates, hashes, or any biometric-derived material must never be stored in this table or transmitted off-device.
revoke_on_password_change
When the user changes their password or re-authenticates via BankID/Vipps with a new session, all active biometric credentials for that user must be set is_active = false with revocation_reason = 'password_changed' or 'bankid_reauth_required'.
revoke_on_logout
On explicit logout, all active credentials for the user on the current device must be deactivated (is_active = false, revocation_reason = 'user_logout'). Full account logout revokes all device credentials.
update_last_used_on_auth
Every successful biometric authentication must update last_used_at to the current UTC timestamp on the corresponding credential row.
rls_user_scope
Supabase Row Level Security must restrict reads and writes to the row's own user_id matching auth.uid(). No user may read, modify, or enumerate another user's biometric credential records.
CRUD Operations
Storage Configuration
Entity Relationships
A user may have biometric credentials enrolled on multiple devices