User
Data Entity
Description
Core identity entity representing all platform users across Peer Mentor, Coordinator, Organization Admin, and Global Admin roles. Central to all operational data in the Meander platform.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key, globally unique user identifier | PKrequiredunique |
email |
string |
User's email address, used for login and notifications | requiredunique |
password_hash |
string |
Bcrypt hash of user's password. NULL if user authenticates exclusively via BankID, Vipps, or passkeys | - |
first_name |
string |
User's given name | required |
last_name |
string |
User's family name | required |
phone_number |
string |
User's phone number, used for SMS notifications and Vipps auth matching | - |
national_id |
string |
Norwegian national identity number (personnummer). Populated via Vipps/BankID authentication. Stored encrypted. Used for member system matching. | - |
status |
enum |
Account lifecycle status | required |
primary_role |
enum |
The user's primary platform role determining their access surface (mobile app vs admin portal) | required |
language_preference |
string |
Preferred UI language code (e.g. 'nb', 'nn', 'se') | - |
accessibility_preferences |
json |
User accessibility settings: font size scale, high contrast mode, reduced motion. Supports WCAG 2.2 AA personalization. | - |
avatar_url |
string |
URL to user's profile photo stored in cloud storage | - |
invited_by_user_id |
uuid |
FK to the admin or coordinator who created the invitation for this user | - |
invitation_token |
string |
One-time token sent via email during onboarding. Cleared after first login. | - |
invitation_expires_at |
datetime |
Expiry timestamp for the invitation token | - |
last_login_at |
datetime |
Timestamp of the user's most recent successful authentication | - |
last_active_at |
datetime |
Timestamp of most recent API activity, used for session monitoring and inactivity detection | - |
biometric_enabled |
boolean |
Whether the user has enabled biometric unlock (Face ID / fingerprint) on their device | required |
push_token |
string |
Device push notification token (FCM/APNs). Updated on each app launch. | - |
push_token_updated_at |
datetime |
When the push_token was last refreshed | - |
deactivated_at |
datetime |
Timestamp when the account was deactivated. NULL if active. | - |
deactivated_by_user_id |
uuid |
FK to the admin who performed the deactivation | - |
created_at |
datetime |
Record creation timestamp | required |
updated_at |
datetime |
Record last-updated timestamp | required |
Database Indexes
idx_users_email
Columns: email
idx_users_status
Columns: status
idx_users_primary_role
Columns: primary_role
idx_users_invitation_token
Columns: invitation_token
idx_users_last_login_at
Columns: last_login_at
idx_users_invited_by
Columns: invited_by_user_id
Validation Rules
email_unique_and_valid
error
Validation failed
invitation_expiry
error
Validation failed
password_strength
error
Validation failed
national_id_format
error
Validation failed
primary_role_immutable_after_invitation
error
Validation failed
status_transition_validity
error
Validation failed
phone_number_format
warning
Validation failed
Business Rules
global_admin_no_org_data_access
Global Admins have no default access to any organization's operational data (users, activities, contacts). Access requires an explicit time-bounded support grant from the org, stored in support_access_grants.
org_admin_surfaced_as_coordinator_on_mobile
Users with primary_role=org_admin are surfaced as Coordinators in the mobile app. They use the coordinator mobile experience; admin-only capabilities exist exclusively in the admin portal.
peer_mentor_mobile_only
Users with primary_role=peer_mentor or coordinator may only authenticate to the mobile app. Attempts to access the admin portal must be rejected with a redirect to the no-access screen.
no_organization_selection_at_login
Organization context is determined by the user's account (set during invitation). Users do NOT choose their organization at login — there is no organization selection screen.
deactivation_preserves_historical_data
Deactivating a user sets status=inactive and records deactivated_at/deactivated_by. Historical activities, expenses, and contacts created by the user are preserved and remain queryable by admins.
invitation_single_use
invitation_token is cleared immediately upon first successful login. Expired or already-used tokens must be rejected.
paused_peer_mentor_excluded_from_dispatch
Users with status=paused must not appear in assignment dispatch, coordinator proxy-reporting selectors, or geographic map views. Restored to active via resume flow.
vipps_national_id_sync
When a user authenticates via Vipps for the first time, the returned personnummer must be stored encrypted in national_id if not already present. This enables member system reconciliation.
support_access_time_bounded
Global Admin support access to an org's data is time-bounded via support_access_grants. Every action during a support session must be logged in the org's audit_logs.