User
Data Entity
Description
A platform account representing any individual who interacts with the system across all four organizations. Users can hold multiple roles across multiple local associations simultaneously (up to five affiliations). Serves as the identity anchor for all authentication methods including BankID, Vipps, email/password, and biometric.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Globally unique identifier for the user account, used as the primary key and foreign key anchor across all related tables | PKrequiredunique |
email |
string |
User's primary email address used for login, invitation delivery, and system notifications. Must be unique across the entire platform regardless of organization. | requiredunique |
display_name |
string |
Full name of the user as displayed across the app — in contact lists, activity records, assignment dispatches, and coordinator views | required |
phone_number |
string |
Optional Norwegian mobile phone number used for Vipps authentication linkage, SMS notifications, and coordinator contact lookup. Stored in E.164 format. | - |
password_hash |
string |
bcrypt hash of the user's password for email/password authentication. Null for users who have only ever authenticated via BankID or Vipps and have never set a password. | - |
status |
enum |
Current account lifecycle status. Controls whether the user can log in and whether their data appears in coordinator and admin views. | required |
bankid_subject |
string |
The immutable subject identifier (sub claim) from the BankID OIDC provider, linking this user account to their verified Norwegian identity. One-to-one with a real person — enforced at the platform level. | unique |
vipps_subject |
string |
The immutable subject identifier (sub claim) from the Vipps OAuth provider. Used to match returning Vipps users to their existing platform account without requiring re-registration. | unique |
personnummer_encrypted |
string |
AES-256-GCM encrypted Norwegian national identity number (personnummer) captured from Vipps userinfo when the user consents during initial Vipps login. Used to backfill member records in HLF and NHF systems that lack personnummer for existing members. | - |
locale |
string |
User's preferred language locale for app UI and notifications. Defaults to Norwegian Bokmål. Supports future Sami language addition for NHF. | required |
last_login_at |
datetime |
UTC timestamp of the user's most recent successful authentication across any method (BankID, Vipps, email/password, biometric session resume). Used by the scenario-based reminder engine to detect inactivity. | - |
deactivated_at |
datetime |
UTC timestamp when the account was deactivated. Null for active accounts. Set atomically with status transition to 'deactivated'. Used for Bufdir audit trail and GDPR retention tracking. | - |
deactivated_by |
uuid |
ID of the administrator or coordinator who deactivated this account. Null for self-initiated deactivation or accounts not yet deactivated. References the users table (self-referential FK). | - |
deactivation_reason |
text |
Optional free-text reason provided by the administrator at the time of deactivation. Captured for audit compliance and to satisfy the Deactivate User Dialog confirmation flow. | - |
invited_by |
uuid |
ID of the administrator who initiated the invitation for this user account. Null for users who registered via BankID or Vipps self-service without a prior invitation. | - |
invitation_accepted_at |
datetime |
UTC timestamp when the user accepted their invitation and completed onboarding. Null for users who have not yet accepted or who registered via identity provider self-service. | - |
created_at |
datetime |
UTC timestamp of account creation. Set once on insert and never modified. | required |
updated_at |
datetime |
UTC timestamp of the most recent update to this record. Managed by a database trigger or ORM lifecycle hook. | required |
Database Indexes
idx_user_email
Columns: email
idx_user_bankid_subject
Columns: bankid_subject
idx_user_vipps_subject
Columns: vipps_subject
idx_user_status
Columns: status
idx_user_last_login_at
Columns: last_login_at
idx_user_deactivated_at
Columns: deactivated_at
idx_user_created_at
Columns: created_at
Validation Rules
email_format
error
Validation failed
email_lowercase_normalization
error
Validation failed
display_name_length
error
Validation failed
status_valid_enum
error
Validation failed
deactivated_at_consistency
error
Validation failed
phone_number_e164_format
error
Validation failed
password_hash_never_plaintext
error
Validation failed
personnummer_encrypted_format
error
Validation failed
identity_subject_immutable
error
Validation failed
locale_supported_value
warning
Validation failed
Business Rules
max_five_associations
A single user may hold membership in at most five local associations simultaneously. This is a hard constraint identified by NHF — peer mentors active in multiple local associations must not exceed this limit. Enforced at write time by the Membership Service before inserting any new user_organization_roles record.
unique_identity_claim_per_provider
Each BankID subject identifier and each Vipps subject identifier must map to exactly one platform user. A single person may not hold multiple user accounts authenticated via the same identity provider. Enforced by the Identity Verification Service on every OAuth callback resolution.
deactivation_requires_impact_check
Before deactivating a user, the system must surface a summary of their open assignments, pending reimbursements, and unsynced activities to the administrator. The Deactivate User Dialog collects this impact summary and requires explicit administrator confirmation before proceeding. This prevents accidental loss of in-flight work records.
deactivated_user_sessions_revoked
When a user account is set to 'deactivated' status, all active sessions and auth tokens for that user must be immediately invalidated. The Auth Middleware must reject subsequent requests bearing tokens issued before the deactivation timestamp.
invited_status_expiry
User accounts in 'invited' status that have not accepted their invitation within the token TTL (typically 7 days) must not be granted login access. The invitation token infrastructure enforces single-use token semantics and expiry.
personnummer_consent_required
The personnummer captured from Vipps may only be stored if the user explicitly granted consent during the Vipps login flow. The Vipps Integration Service must verify the consent scope before encrypting and persisting this field.
email_unique_across_platform
Email addresses are unique across all organizations. A user who belongs to multiple organizations (e.g., both NHF and HLF) uses a single user account with multiple role assignments. Duplicate email registration must be rejected even if the organizations differ.
role_hierarchy_enforcement_on_invite
An administrator may only invite users to roles that are at or below their own role level. A Coordinator cannot invite Organization Administrators. An Organization Administrator cannot invite Global Administrators. Enforced by the User Management Service at invite time.
last_login_updated_on_auth
Every successful authentication event — regardless of method (email/password, BankID, Vipps, biometric session resume) — must update last_login_at. This timestamp is consumed by the scenario-based reminder engine to detect 10-day inactivity and trigger follow-up alerts.
audit_all_status_changes
Every transition of the user status field (active → deactivated, invited → active, etc.) must produce an immutable audit log entry capturing the actor ID, timestamp, previous state, new state, and optional reason. This supports GDPR accountability requirements.
CRUD Operations
Storage Configuration
Entity Relationships
A user (peer mentor or coordinator acting as proxy) registers many activities
A user has one annual summary per calendar year computed by the batch aggregation job
A coordinator (user) dispatches many encrypted assignments to peer mentors
A user can have multiple JWT tokens (access and refresh) active simultaneously across devices
A user (peer mentor) can hold multiple certifications across different courses and renewal cycles
A user can enroll in multiple courses across their mentoring career
A user (peer mentor or coordinator) creates and owns group events
A user can be registered as an attendee for multiple events
A user (peer mentor or coordinator) authors many notes about contacts
A user receives many notifications over their session lifetime
A user has multiple notification setting records — one per scenario type — controlling which alerts they receive
A user who serves as a peer mentor has exactly one extended peer mentor profile
A user (peer mentor) generates referral tokens and has their conversion outcomes tracked
A user (peer mentor) submits many reimbursement claims over their active period
A coordinator (user) makes many manual approval decisions on reimbursement claims
A user has many scheduled reminders generated by the scenario evaluation engine
A user can have multiple active sessions across different devices
A user has exactly one settings record containing all their personal preferences
A user can have multiple role assignments across different organizations and associations
A user can participate in multiple mentor program workshop sessions