core PK: id 12 required 2 unique

Description

Core identity record for every authenticated principal on the Meander platform. Covers all four roles — Peer Mentor, Coordinator, Org Admin, and Global Admin — across both the Mobile App and Admin Web Portal. Stores identity, credential metadata, invitation lifecycle, status, and push token. Authorization (which org, which role) is delegated to user_organization_roles. The auth module owns session and token state; this table owns the durable person record.

23
Attributes
7
Indexes
8
Validation Rules
30
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Surrogate primary key. Generated server-side (UUIDv4). Stable for the lifetime of the account, even across email changes.
PKrequiredunique
email string Primary login credential and notification address. Lower-cased and trimmed before storage. Unique across the entire platform regardless of organization.
requiredunique
email_verified boolean True once the user has clicked the verification link. Unverified accounts can receive invitations but cannot authenticate until verified.
required
password_hash string Argon2id hash of the user's password. NULL for accounts that authenticate exclusively via BankID or Vipps (Phase 2). Never returned in API responses.
-
first_name string User's given name. Displayed throughout the app and admin portal. Used in notification salutations and Bufdir report attribution.
required
last_name string User's family name. Combined with first_name for display. Used in assignment dispatch, report attribution, and audit log actor labels.
required
phone_number string Optional contact number, used for SMS notifications (Email/SMS Notifications feature) and Vipps-linked identity lookup. Stored in E.164 format.
-
status enum Lifecycle state of the account. Active: normal operation. Paused: peer mentor temporarily inactive (Pause Function); account intact, excluded from public listings and assignment matching. Deactivated: admin-initiated, cannot log in. Suspended: platform-level lock, reserved for policy violations.
required
is_global_admin boolean True for Norse Digital Products staff accounts. Global admins operate across all organizations from the Admin Web Portal and have no default access to any organization's operational data. This flag is separate from org-scoped roles stored in user_organization_roles.
required
avatar_url string URL of the user's profile photo stored in cloud object storage. NULL if no photo uploaded. Used in profile screens, coordinator overviews, and admin portal user cards.
-
preferred_language enum UI language preference. Drives app locale and notification templates. Defaults to Norwegian Bokmål.
required
push_notification_token string FCM/APNs device token for the mobile app. Updated on each app launch. NULL for admin portal-only accounts (Org Admins and Global Admins who have not installed the mobile app). One token per user — multi-device is handled by updating this field on new login.
-
biometric_enabled boolean Whether the user has enrolled biometric unlock (Face ID / fingerprint) on their primary device. The biometric credential itself is stored in the platform secure store on the device — this flag signals to the auth flow that biometric unlock is available.
required
invitation_token string One-time token included in the invitation email link. Consumed on first login. NULL after the invitation is accepted or the token expires.
-
invitation_expires_at datetime Timestamp after which the invitation_token is no longer valid. NULL for accounts that completed onboarding. Set to 7 days after invitation_sent_at.
-
invited_by_user_id uuid FK to the user record of the Org Admin who sent the invitation. Used in audit trails and invitation management screens. NULL for seed/bootstrap accounts.
-
onboarding_completed boolean True once the user has completed the first-run onboarding flow (accepted ToS, set password, verified email). Controls whether the app routes to onboarding screens on next launch.
required
support_access_until datetime Timestamp until which a Global Admin has been granted time-bounded access to this user's organization data. Set by the Org Admin via Organization Settings. NULL means no active support access grant. The auth module and role guard check this field before granting cross-org access to Global Admins.
-
last_login_at datetime Timestamp of the most recent successful authentication. Updated on every login event. Used by the Security Dashboard and session audit tooling.
-
deactivated_at datetime Timestamp when the account was deactivated. NULL for active/paused accounts. Set on admin-initiated deactivation or bulk deactivation.
-
deactivated_by_user_id uuid FK to the admin user who performed the deactivation. Part of the deactivation audit trail. NULL if account has never been deactivated.
-
created_at datetime Record creation timestamp. Set once on INSERT. Used for Bufdir reporting period attribution and admin user list sorting.
required
updated_at datetime Timestamp of the most recent modification to any field on this record. Maintained by an ON UPDATE trigger. Used by conflict resolution in the mobile sync pipeline.
required

Database Indexes

idx_users_email
btree unique

Columns: email

idx_users_status
btree

Columns: status

idx_users_is_global_admin
btree

Columns: is_global_admin

idx_users_invitation_token
btree unique

Columns: invitation_token

idx_users_created_at
btree

Columns: created_at

idx_users_last_login_at
btree

Columns: last_login_at

idx_users_full_name
btree

Columns: last_name, first_name

Validation Rules

email_format error

Validation failed

password_strength error

Validation failed

name_not_blank error

Validation failed

phone_e164_format error

Validation failed

status_transition_allowed error

Validation failed

invitation_expiry_window error

Validation failed

support_access_max_duration error

Validation failed

avatar_url_https_only error

Validation failed

Business Rules

unique_email_across_platform
on_create

Email addresses are unique across all organizations. A user invited by Org A who later joins Org B uses the same account — no duplicate records per email. Enforced at DB level (unique index) and API level before INSERT.

deactivated_user_cannot_authenticate
always

Users with status='deactivated' or status='suspended' are rejected at the auth layer before any token is issued. The auth-service checks status on every sign-in attempt, including biometric and BankID flows.

paused_mentor_excluded_from_listings
on_update

Peer mentors with status='paused' are excluded from the Geographic Map View, assignment matching candidates, and any public-facing coordinator overviews. Their account and data remain fully intact. Coordinators are notified when a mentor pauses.

global_admin_no_default_org_access
always

is_global_admin=true grants access to the Admin Web Portal globally but confers zero access to any organization's operational data (users, activities, contacts) unless support_access_until is set and in the future for that org. Role guard enforces this on every org-scoped API endpoint.

support_access_auto_expiry
always

When now() > support_access_until, Global Admin access to that organization's data is revoked immediately without any manual step. The role guard performs the check inline; no background job required. Every support session is written to the organization's audit log.

coordinator_peer_mentor_no_admin_portal_login
always

Users whose only roles (across all user_organization_roles records) are peer_mentor or coordinator are blocked from logging into the Admin Web Portal. The auth service returns a 403 with a redirect hint to the mobile app. is_global_admin=false is also required.

invitation_token_single_use_and_expiring
on_update

invitation_token is consumed (set to NULL) on first successful login. If invitation_expires_at has passed, the token is rejected and the Org Admin must re-invite. This prevents stale invite links from being used after an account is no longer expected.

certification_expiry_triggers_auto_pause
on_update

When a peer mentor's certification expires (tracked in certifications table), the expiry-check-service sets status='paused' automatically. The mentor is notified and the coordinator receives a warning. This implements the HLF requirement that expired-certificate mentors disappear from listings automatically.

bulk_deactivation_audit_required
on_update

Any bulk status change (deactivation, suspension) via bulk-user-service must record the acting admin's user ID in deactivated_by_user_id and write an audit log entry for each affected user. Individual deactivations via user-administration-service follow the same rule.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage