# Entity: Session user Authenticated user context for B2B portal — demo mock now, JWT/session later. **Workspace:** `/Users/kirukib/Desktop/Yaltopia Project/Gishen-B2B` ## Fields | Field | Type | Required | Notes | |-------|------|----------|-------| | `id` | `string` | ✓ | `usr_*` or demo `user_*` | | `email` | `string` | ✓ | | | `full_name` | `string` | ✓ | | | `phone` | `Phone` | | | | `org_id` | `string` | ✓ | Active tenant | | `member_id` | `string` | | Link to Member row if enrolled | | `customer_id` | `string` | | Shared Gishen customer (Ecom/Mob) | | `roles` | `PortalRole[]` | ✓ | One or more | | `active_role` | `PortalRole` | ✓ | Role used for nav + withhold (must ∈ `roles`) | | `locale` | `Locale` | ✓ | `en` \| `am` | | `avatar_url` | `string` | | Portrait URL (demo: deterministic DiceBear) | | `org_status` | `OrgStatus` | ✓ | Denormalized for gating | | `permissions` | `string[]` | | Computed from `active_role` / roles | | `demo_profile` | `DemoProfile` | | Mock only | ### PortalRole ``` SUPER_USER | HR_ADMIN | FINANCE | MEMBER ``` ### DemoProfile (mock `/login` only) | Field | Type | |-------|------| | `label` | `string` | | `description` | `string` | Pre-seeded demo personas: `user_super`, `user_hr`, `user_fin`, `user_mem` against a shared mock org. Listed via `GET /v1/auth/demo-profiles`. ## Computed permissions | Permission | Roles | |------------|-------| | `org:read` | SUPER_USER, HR_ADMIN, FINANCE | | `org:write_hr` | SUPER_USER, HR_ADMIN | | `members:write` | SUPER_USER, HR_ADMIN | | `packages:write` | SUPER_USER, HR_ADMIN | | `migration:write` | SUPER_USER, HR_ADMIN | | `finance:read` | SUPER_USER, HR_ADMIN, FINANCE | | `finance:approve` | SUPER_USER, FINANCE | | `clinical:read` | SUPER_USER, MEMBER (own) | | `prescriptions:write` | MEMBER (own), SUPER_USER | | `id_card:print` | SUPER_USER, HR_ADMIN, MEMBER (own) | ## Locale preference `locale` is persisted on the user record and returned in session. Updated via `PATCH /v1/auth/me/locale` or `PATCH /v1/me/profile`. Synced to client cookie/localStorage for next-intl. ## Active role Multi-role users (demo Super User) can set `active_role` on `/profile`. Portal nav and clinical withhold follow `active_role` for the session. ## Clinical withhold Session drives RBAC. If effective role is HR_ADMIN or FINANCE and **not** SUPER_USER, all prescription and clinical serializers apply withhold filters automatically. ## Sample session (mock demo — HR) ```json { "id": "user_hr", "email": "hr@yaltopia.com", "full_name": "Hanna HR", "phone": "+251900000001", "org_id": "org_yaltopia", "member_id": null, "customer_id": null, "roles": ["HR_ADMIN"], "active_role": "HR_ADMIN", "locale": "en", "avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr", "org_status": "active", "permissions": ["org:read", "org:write_hr", "members:write", "packages:write", "migration:write", "finance:read", "id_card:print"], "demo_profile": { "label": "HR Administrator", "description": "Manage members, departments, and packages" } } ``` ## Sample session (SUPER_USER) ```json { "id": "user_super", "email": "super@yaltopia.com", "full_name": "Selam Super", "roles": ["SUPER_USER", "HR_ADMIN", "FINANCE", "MEMBER"], "active_role": "SUPER_USER", "locale": "am", "avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_super", "permissions": ["*"], "demo_profile": { "label": "Super User", "description": "Full portal access including clinical detail" } } ``` ## Related endpoints - [`../endpoints/auth.md`](../endpoints/auth.md) — login, demo-profiles, `/me/profile`