core PK: id 13 required 7 unique

Description

Central identity entity representing all authenticated individuals on the Meander platform — peer mentors, coordinators, organization administrators, and global administrators. Stores personal profile data, authentication metadata, organization membership, and account lifecycle state. Every authenticated action across all three products (mobile app, admin portal, sales website) resolves back to a users record.

26
Attributes
10
Indexes
10
Validation Rules
47
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Unique identifier for the user, generated server-side on account creation
PKrequiredunique
email string User email address, used as the primary login credential for email/password authentication and for system notifications
requiredunique
password_hash string Bcrypt-hashed password for email/password authentication. Null when user authenticates exclusively via BankID/Vipps/passkey
-
first_name string User first name, displayed in profile and used in notifications and reports
required
last_name string User last name, displayed in profile and used in reports
required
phone string Phone number in E.164 format, used for SMS notifications and Vipps identity matching
-
personnummer string Norwegian national identity number (11 digits), obtained via BankID/Vipps login. Encrypted at rest. Used for identity verification and member system synchronization
unique
organization_id uuid Foreign key to organizations table. Determines tenant context. Set during onboarding by admin — users do not choose their organization at login
required
status enum Account lifecycle state. Controls login access and visibility in member lists
required
auth_provider enum Primary authentication method used to create the account
required
bankid_subject_id string BankID unique subject identifier from OAuth identity token, used for account linking on subsequent BankID logins
unique
vipps_subject_id string Vipps Login unique subject identifier from ID token, used for account linking on subsequent Vipps logins
unique
biometric_enabled boolean Whether the user has opted into biometric authentication (Face ID/fingerprint) on at least one device
required
passkey_enabled boolean Whether the user has registered at least one FIDO2 passkey credential
required
language string Preferred UI language locale code (e.g., nb-NO, se-NO for Northern Sami)
required
invitation_token string Time-limited token sent via invitation email. Null after account activation
unique
invitation_expires_at datetime Expiry timestamp for the invitation token
-
password_reset_token string Time-limited token for password reset flow. Null when not in use
unique
password_reset_expires_at datetime Expiry timestamp for the password reset token
-
roles_updated_at datetime Timestamp of the most recent role assignment change. Used by JWT validation to detect stale role claims and force token refresh
required
last_login_at datetime Timestamp of the user's most recent successful authentication
-
paused_at datetime Timestamp when a peer mentor set themselves to paused status. Null if not paused. Coordinator is notified on pause
-
deactivated_at datetime Timestamp when the account was deactivated by an admin. Null if active
-
avatar_url string URL to the user's profile photo stored in cloud storage
-
created_at datetime Timestamp of account creation (invitation sent)
required
updated_at datetime Timestamp of last profile or status update
required

Database Indexes

idx_users_email
btree unique

Columns: email

idx_users_organization_id
btree

Columns: organization_id

idx_users_organization_status
btree

Columns: organization_id, status

idx_users_personnummer
btree unique

Columns: personnummer

idx_users_bankid_subject_id
btree unique

Columns: bankid_subject_id

idx_users_vipps_subject_id
btree unique

Columns: vipps_subject_id

idx_users_invitation_token
btree unique

Columns: invitation_token

idx_users_status
btree

Columns: status

idx_users_last_login_at
btree

Columns: last_login_at

idx_users_created_at
btree

Columns: created_at

Validation Rules

email_format_and_uniqueness error

Validation failed

phone_e164_format error

Validation failed

personnummer_checksum error

Validation failed

name_fields_non_empty error

Validation failed

valid_status_transitions error

Validation failed

organization_must_exist error

Validation failed

invitation_token_expiry error

Validation failed

password_policy error

Validation failed

language_locale_valid error

Validation failed

avatar_url_format warning

Validation failed

Business Rules

organization_context_from_invitation
on_create

Users do not choose their organization at login. Organization context is set during onboarding when an admin invites the user, and cannot be changed after account creation

tenant_isolation
always

All queries for user data must be scoped by organization_id. Users in one organization cannot see or modify users in another organization. Global admins manage the system but do not have default access to org operational data

pause_does_not_deactivate
on_update

Peer mentors can pause their status (temporarily deactivate) without leaving the program. Paused users retain their account but are removed from active member lists and coordinator dashboards. Coordinator must be notified on pause

invitation_flow_required
on_create

New users are created exclusively via admin invitation (status=invited). Self-registration is not supported. Users must complete the invitation flow (accept token, set password or link BankID/Vipps) before status transitions to active

roles_updated_at_invalidates_jwt
on_update

When a user's role assignments change, roles_updated_at is bumped. JWT validation middleware compares this timestamp against the token's issued-at claim to force token refresh, ensuring stale role claims are never honoured

deactivated_users_cannot_login
on_update

Users with status=deactivated are blocked from all authentication flows. Active sessions and refresh tokens are revoked on deactivation

single_organization_membership
on_create

Each user belongs to exactly one organization. Multi-organization access is handled via the organization hierarchy (parent-child), not via multiple user records

personnummer_encrypted_at_rest
always

The personnummer field contains sensitive PII (Norwegian national ID) and must be encrypted at the database column level. Only authentication and identity verification services may decrypt it

account_linking_prevents_duplicates
on_update

When a user authenticates via BankID or Vipps and the returned personnummer or subject ID matches an existing user, the identity is linked to the existing account rather than creating a duplicate

bulk_operations_require_audit
on_update

All bulk user operations (deactivation, role reassignment, invitation resend) must create individual audit log entries per affected user within the same transaction

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage

Components Managing This Entity

service Auth API Client ["mobile"] service Auth API Client ["mobile"] service BankID OAuth Service ["mobile","backend"] service BankID OAuth Service ["mobile","backend"] service Vipps OAuth Service ["mobile"] service Vipps OAuth Service ["backend"] data Permission Store ["mobile"] data Permission Store ["mobile","backend"] service Role Guard Service ["mobile"] service Role Guard Service ["mobile","backend"] data User Profile Store ["mobile"] data User Profile Store ["mobile"] service Profile Service ["mobile"] service Profile Service ["mobile"] ui Profile Screen ["mobile"] ui Profile Screen ["mobile"] infrastructure Auth API Client ["mobile"] service Passkey Auth Service ["mobile"] service Passkey Auth Service ["mobile","backend","shared"] service Proxy Registration Service ["mobile"] service Proxy Registration Service ["mobile","backend"] service Bulk Processing Service ["mobile"] service Bulk Processing Service ["backend"] service Contact Service ["mobile"] service Contact Service ["mobile"] service Stats Aggregation Service ["backend"] service Stats Aggregation Service ["backend","mobile"] service Team Stats Service ["mobile","backend"] service Team Stats Service ["backend","mobile"] service Bufdir Report Service ["backend"] service Bufdir Report Service ["backend","mobile"] service Notification Rule Engine ["backend"] service Notification Rule Engine ["backend"] service Referral Tracking Service ["backend"] service Referral Tracking Service ["backend"] service Dashboard Service ["mobile"] service Dashboard Service ["mobile"] service Settings Service ["mobile"] service Settings Service ["mobile"] service KPI Aggregation Service ["backend"] service KPI Aggregation Service ["backend"] service Activity Feed Service ["backend"] service Activity Feed Service ["backend"] data User Repository ["backend"] data User Repository ["backend"] service User Service ["backend"] service User Service ["backend"] ui User Detail Page ["frontend"] ui User Detail Page ["frontend"] ui User List Page ["frontend"] ui User List Page ["frontend"] service Role Assignment Service ["backend"] service Role Assignment Service ["backend"] service Bulk User Processing Service ["backend"] service Bulk User Processing Service ["backend"] service Activity Approval Service ["backend"] service Activity Approval Service ["backend"] service Audit Log Service ["backend"] service Audit Log Service ["backend"] service Expense Approval Service ["backend"] service Expense Approval Service ["backend"] service Report Generation Service ["backend"] service Report Generation Service ["backend"] service Custom Report Service ["backend"] service Custom Report Service ["backend"] service Hierarchy Scope Resolver ["backend"] service Association Service ["backend"] service Association Service ["backend"] service Security Monitoring Service ["backend"] service Security Monitoring Service ["backend"] service Audit Log Service ["backend"] service Audit Log Service ["backend"] service Session Service ["backend"] service Session Service ["backend"] service Booking Service ["backend"] service Booking Service ["backend"]