User
Data Entity
Description
Core identity record for all platform users across Meander Mobile App and Admin Web Portal. Represents Peer Mentors, Coordinators, Organization Admins, and Global Admins. Organization context is set during onboarding — users never choose their organization at login.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key, globally unique user identifier | PKrequiredunique |
email |
string |
Email address used for login and notifications. Normalized to lowercase. | requiredunique |
password_hash |
string |
Argon2id hash of the user's password. Null for users who authenticate exclusively via BankID, Vipps, or Passkeys. | - |
full_name |
string |
User's full display name as entered during onboarding or set by admin. | required |
phone_number |
string |
Mobile phone number in E.164 format. Used for SMS notifications and as secondary contact. | - |
national_id |
string |
Norwegian national identity number (personnummer). Populated via BankID or Vipps login. Encrypted at rest. Used for Bufdir reporting and member system reconciliation. | - |
role |
enum |
Primary platform role. Determines which product surfaces the user may access. | required |
status |
enum |
Account lifecycle state. Inactive users cannot authenticate. | required |
avatar_url |
string |
URL to profile photo stored in cloud storage. Optional. | - |
preferred_language |
string |
BCP-47 language tag for UI localization. Defaults to 'nb' (Norwegian Bokmål). | - |
accessibility_preferences |
json |
User-level accessibility overrides: font_scale, high_contrast, reduce_motion. Applied on top of system settings. | - |
bankid_subject |
string |
BankID subject identifier returned after successful BankID authentication. Enables account linking without storing the national ID in plaintext. | unique |
vipps_subject |
string |
Vipps subject identifier returned after successful Vipps login. | unique |
invited_by_user_id |
uuid |
FK to the admin user who sent the invitation. Null for seed/system-created accounts. | - |
invitation_token_hash |
string |
SHA-256 hash of the one-time invitation token. Cleared after first login. | - |
invitation_expires_at |
datetime |
Expiry timestamp for the pending invitation link. Null once accepted. | - |
email_verified_at |
datetime |
Timestamp when the user confirmed their email address. Null until verified. | - |
last_login_at |
datetime |
Timestamp of most recent successful authentication. Updated on every login. | - |
last_login_method |
enum |
Authentication method used in the most recent login. | - |
paused_at |
datetime |
Timestamp when the user's status was set to paused. Null if not currently paused. | - |
pause_reason |
enum |
Reason for the paused status. | - |
is_test_user |
boolean |
Marks accounts belonging to the Norse Test Organization. Test user data is excluded from Bufdir reporting and external exports. | required |
created_at |
datetime |
Record creation timestamp. Set once, never updated. | required |
updated_at |
datetime |
Timestamp of last record modification. Managed by application layer. | required |
deleted_at |
datetime |
Soft-delete timestamp. Non-null means the account is deactivated. GDPR erasure sets all PII fields to null and leaves only this tombstone. | - |
Database Indexes
idx_users_email
Columns: email
idx_users_bankid_subject
Columns: bankid_subject
idx_users_vipps_subject
Columns: vipps_subject
idx_users_role_status
Columns: role, status
idx_users_status
Columns: status
idx_users_deleted_at
Columns: deleted_at
idx_users_invited_by
Columns: invited_by_user_id
Validation Rules
email_unique_across_platform
error
Validation failed
password_complexity
error
Validation failed
phone_e164_format
error
Validation failed
invitation_not_expired
error
Validation failed
role_transitions_valid
error
Validation failed
status_transitions_valid
error
Validation failed
national_id_format_norwegian
error
Validation failed
full_name_not_blank
error
Validation failed
Business Rules
org_context_set_by_admin
A user's organization context is determined during invitation by an Org Admin. Users never select their organization at login time.
global_admin_no_org_data_access
Global Admins have no default access to any organization's operational data. Tenant separation is strict. Access requires an explicit time-bounded support_access_grant approved by the Org Admin.
coordinator_and_peer_mentor_mobile_only
Peer Mentors and Coordinators are managed inside the admin portal but may never log in to it. Only org_admin and global_admin roles may authenticate to the Admin Web Portal.
org_admin_surfaced_as_coordinator_on_mobile
When an org_admin authenticates on the Mobile App, they receive the Coordinator mobile experience without a separate UI path.
pause_requires_coordinator_notification
When a Peer Mentor's status transitions to paused, the associated Coordinator must be notified via push notification.
certification_expiry_auto_pause
When a Peer Mentor's certification expires, their status is automatically set to paused and they are removed from public-facing coordinator listings.
test_user_excluded_from_bufdir
Users with is_test_user=true must be excluded from all Bufdir report snapshots and external exports.
soft_delete_only
User records are never hard-deleted. Deactivation sets deleted_at. GDPR erasure nullifies all PII fields but retains the row for audit trail integrity.
invitation_single_use
The invitation token is one-time-use. It must be cleared (set to null) immediately after the user completes first login and sets a password.
vipps_national_id_backfill
When a user authenticates via Vipps for the first time, the returned personnummer is stored (encrypted) in national_id if not already set, enabling member system reconciliation.