core PK: id 6 required 2 unique

Description

Represents an authenticated system user account with a unique email address. Each user belongs to one or more organizations and is assigned one or more roles that determine their access scope. Users may also be represented as peer mentors, coordinators, or administrators depending on their role assignments.

13
Attributes
5
Indexes
8
Validation Rules
30
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Immutable primary key generated by Supabase Auth (auth.users.id). Serves as the foreign key anchor for all user-related tables including user_roles, sessions, notifications, and peer_mentor profiles.
PKrequiredunique
email string The user's verified email address used for credential-based login and system notifications. Must be globally unique across all organizations. Supabase Auth owns this field; the application layer reads it from the session JWT claims.
requiredunique
full_name string Display name of the user, sourced from BankID or Vipps identity verification if available, otherwise provided during registration. Used in notifications, audit trails, and coordinator-facing roster views.
-
phone_number string Optional phone number for the user, may be retrieved from Vipps userinfo endpoint (phoneNumber claim). Stored in E.164 format.
-
bankid_verified boolean Flag indicating whether the user has successfully completed BankID identity verification. When true, the associated personnummer_encrypted field is expected to be populated. Enables higher-trust workflows for organizations requiring verified identity.
required
bankid_verified_at datetime UTC timestamp of the most recent successful BankID verification event. Null if bankid_verified is false. Used to enforce re-verification policies and displayed in admin audit views.
-
personnummer_encrypted string AES-256 encrypted Norwegian national identity number (personnummer / fødselsnummer) obtained from Vipps NIN claim or BankID identity assertion. Encryption key is organization-scoped and managed via Supabase Vault. Required for Vipps-linked accounts where NIN is returned. Never returned in plain text via API.
-
is_active boolean Soft-delete flag and account status indicator. When false the user cannot authenticate and all active sessions are invalidated. Set to false on account deactivation by an org_admin or global_admin. Does not delete any associated data.
required
last_login_at datetime UTC timestamp of the user's most recent successful authentication event (email/password, BankID, Vipps, or biometric session resume). Updated on every successful auth. Exposed to org_admin for inactive user reporting.
-
preferred_language string BCP-47 language tag for the user's preferred UI language. Defaults to 'nb' (Norwegian Bokmål). Used to localize push notification payloads, email notifications, and in-app content.
-
avatar_url string URL pointing to the user's profile image stored in Supabase Storage. Falls back to initials-based avatar in the UI when null. Organization-scoped storage path.
-
created_at datetime UTC timestamp when the user account was first created in Supabase Auth. Immutable after creation. Used for audit reporting and user lifecycle analytics.
required
updated_at datetime UTC timestamp of the most recent modification to any mutable field on this record. Automatically maintained by a Supabase trigger. Used for cache invalidation in the terminology sync and role resolution services.
required

Database Indexes

idx_user_email
btree unique

Columns: email

idx_user_is_active
btree

Columns: is_active

idx_user_bankid_verified
btree

Columns: bankid_verified

idx_user_last_login_at
btree

Columns: last_login_at

idx_user_created_at
btree

Columns: created_at

Validation Rules

email_format error

Validation failed

email_lowercase_normalization error

Validation failed

personnummer_format error

Validation failed

full_name_not_blank_if_provided warning

Validation failed

phone_e164_format warning

Validation failed

preferred_language_valid_bcp47 warning

Validation failed

avatar_url_valid_format error

Validation failed

id_matches_supabase_auth error

Validation failed

Business Rules

unique_email_globally
on_create

A user's email address must be unique across the entire Supabase Auth namespace. Attempting to register a second account with an existing email must be rejected at the Supabase Auth layer before reaching the application layer.

bankid_verification_immutability
on_update

Once bankid_verified is set to true and personnummer_encrypted is populated, these fields may only be updated by a global_admin or via an explicit re-verification flow. The application layer must not overwrite a verified NIN with a new value without coordinator or admin approval.

inactive_user_session_revocation
on_update

When is_active is set to false, all active Supabase sessions for the user must be revoked immediately. The auth-session-manager must call Supabase Auth admin.signOut(userId) and clear any cached tokens in secure storage.

global_admin_mobile_access_denied
always

Users whose only role assignment is global_admin must be redirected to the no-access screen after authentication. Global admins perform their work in the web-based admin portal, not the mobile app.

personnummer_encryption_required
on_create

The personnummer must never be stored in plain text. It must be encrypted with an organization-scoped key managed via Supabase Vault before being written to the personnummer_encrypted column. Decryption is only permitted in server-side contexts (Edge Functions).

single_account_per_email
on_create

A user may belong to multiple organizations but must use a single account (email address). Multi-organization membership is modeled through user_role records, not separate user accounts.

last_login_updated_on_auth
on_update

The last_login_at field must be updated to the current UTC timestamp on every successful authentication event regardless of method (email/password, BankID, Vipps, biometric resume).

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Entity Relationships

periodic_summary
incoming belongs_to

A periodic summary is computed for and owned by a specific user (peer mentor or coordinator) for the relevant period

required cascade delete
accessibility_preferences
outgoing one_to_one

Each user has exactly one accessibility preferences record controlling font scale, contrast mode, and haptic feedback settings

optional cascade delete
device_token
outgoing one_to_many

A user may have FCM push tokens registered across multiple devices simultaneously

optional cascade delete
notification
outgoing one_to_many

A user receives many notifications over their lifetime across all notification types

optional cascade delete
notification_preference
outgoing one_to_many

A user has one preference record per notification category controlling opt-in and opt-out state

optional cascade delete
peer_mentor
outgoing one_to_one

A user account that holds the peer_mentor role is linked to exactly one peer_mentor profile record per organization

optional
session
outgoing one_to_many

A user may have multiple active sessions across different devices for biometric re-authentication

optional cascade delete
user_preferences
outgoing one_to_one

Each user has exactly one preferences record storing last-used activity type, default duration, and notification settings

optional cascade delete
user_role
outgoing one_to_many

A user may hold multiple role assignments across different organizations (peer_mentor in one org, coordinator in another)

required cascade delete