This repository has been archived on 2026-08-11. You can view files and clone it, but cannot push or open issues or pull requests.
Gishen-B2B/docs/backend/endpoints/auth.md
kirukib f2e992e0bd Add multi-method login UI and document auth method contracts.
Surface Google, email, phone, and Telegram on login (mocked), polish the brand panel and fixes for Button/Menu, and keep the backend spec sheet aligned.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 16:12:43 +03:00

312 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Endpoints: Auth
Session and user preference endpoints for Gishen-B2B demo login and future production auth.
**Workspace:** `/Users/kirukib/Desktop/Yaltopia Project/Gishen-B2B`
**Entity:** [`../entities/session-user.md`](../entities/session-user.md)
---
## POST /v1/auth/login
Demo persona login (mock phase). Prefer `demo_user_id`; `demo_role` remains for compatibility.
| | |
|---|---|
| **Auth** | Public |
| **Roles** | — |
### Request
```json
{
"demo_user_id": "user_hr",
"locale": "en"
}
```
Legacy: `{ "demo_role": "HR_ADMIN", "locale": "en" }`
`demo_role`: `SUPER_USER` \| `HR_ADMIN` \| `FINANCE` \| `MEMBER`
Production (future): `{ "email", "password" }` or OAuth code exchange.
### Planned auth methods (UI present; production TBD)
| Method | Future flow | Mock phase |
|--------|-------------|------------|
| Google | OAuth 2.0 / OIDC | Button → toast → demo persona session |
| Email | Magic link or password OTP | Same |
| Phone | SMS OTP (E.164) | Same |
| Telegram | Telegram Login Widget / Mini App deep link | Same |
Demo continues to use `demo_user_id` until platform auth is wired.
### Future endpoints (stubs)
| Method | Path | Notes |
|--------|------|-------|
| `GET` | `/v1/auth/oauth/google/start` | Redirect to Google; callback exchanges code |
| `POST` | `/v1/auth/email/otp/start` | Body `{ "email" }` — send OTP / magic link |
| `POST` | `/v1/auth/email/otp/verify` | Body `{ "email", "code" }` — returns session |
| `POST` | `/v1/auth/phone/otp/start` | Body `{ "phone" }` E.164 |
| `POST` | `/v1/auth/phone/otp/verify` | Body `{ "phone", "code" }` |
| `POST` | `/v1/auth/telegram/callback` | Telegram Login Widget payload verification |
All production methods return the same session envelope as `POST /v1/auth/login`.
### Response 200
```json
{
"data": {
"token": "mock_jwt_...",
"user": {
"id": "user_hr",
"email": "hr@yaltopia.com",
"full_name": "Hanna HR",
"org_id": "org_yaltopia",
"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"]
}
}
}
```
### Errors
| Code | HTTP | When |
|------|------|------|
| `VALIDATION_FAILED` | 400 | Invalid `demo_user_id` / `demo_role` |
| `NOT_FOUND` | 404 | Unknown demo persona |
| `UNAUTHENTICATED` | 401 | Production bad credentials |
### Clinical withhold
N/A — session only.
---
## POST /v1/auth/logout
| | |
|---|---|
| **Auth** | Required |
| **Roles** | Any |
### Response 204
No body.
---
## GET /v1/auth/me
Current session user.
| | |
|---|---|
| **Auth** | Required |
| **Roles** | Any |
### Response 200
```json
{
"data": {
"id": "usr_01HMEM",
"email": "member.demo@acme.et",
"full_name": "Demo Member",
"org_id": "org_01DEMO",
"member_id": "mbr_01DEMO",
"customer_id": "cus_01DEMO",
"roles": ["MEMBER"],
"locale": "am",
"org_status": "active",
"allowance_summary": {
"allowance_remaining": { "amount": "10800.00", "currency": "ETB" }
}
}
}
```
### Errors
| Code | HTTP |
|------|------|
| `UNAUTHENTICATED` | 401 |
---
## PATCH /v1/auth/me/locale
Update user locale preference.
| | |
|---|---|
| **Auth** | Required |
| **Roles** | Any |
### Request
```json
{ "locale": "am" }
```
### Response 200
Returns updated session user with `locale: "am"`.
### Validation
| Rule | Code |
|------|------|
| `locale` in `en`, `am` | `VALIDATION_FAILED` |
### Clinical withhold
N/A.
---
## GET /v1/auth/demo-profiles
List pre-seeded demo personas for `/login` test-user dropdown.
| | |
|---|---|
| **Auth** | Public |
| **Roles** | — |
### Response 200
```json
{
"data": [
{
"id": "user_super",
"email": "super@yaltopia.com",
"full_name": "Selam Super",
"demo_role": "SUPER_USER",
"roles": ["SUPER_USER", "HR_ADMIN", "FINANCE", "MEMBER"],
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_super",
"label": "Super User",
"description": "Full portal access including clinical detail"
},
{
"id": "user_hr",
"email": "hr@yaltopia.com",
"full_name": "Hanna HR",
"demo_role": "HR_ADMIN",
"roles": ["HR_ADMIN"],
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr",
"label": "HR Administrator",
"description": "Members, departments, packages — no clinical detail"
},
{
"id": "user_fin",
"email": "finance@yaltopia.com",
"full_name": "Fikru Finance",
"demo_role": "FINANCE",
"roles": ["FINANCE"],
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_fin",
"label": "Finance Approver",
"description": "Spend, statements, approvals — no clinical detail"
},
{
"id": "user_mem",
"email": "member@yaltopia.com",
"full_name": "Abebe Member",
"demo_role": "MEMBER",
"roles": ["MEMBER"],
"member_id": "mbr_01HMEM001",
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_mem",
"label": "Member",
"description": "Own allowance, orders, prescriptions"
}
]
}
```
Login request may use `{ "demo_user_id": "user_hr" }` (preferred) or legacy `{ "demo_role": "HR_ADMIN" }`.
---
## GET /v1/me/profile
Current user’s profile (account page).
| | |
|---|---|
| **Auth** | Required |
| **Roles** | Any |
### Response 200
```json
{
"data": {
"id": "user_hr",
"email": "hr@yaltopia.com",
"full_name": "Hanna HR",
"org_id": "org_yaltopia",
"org_name": "Yaltopia",
"roles": ["HR_ADMIN"],
"active_role": "HR_ADMIN",
"locale": "en",
"avatar_url": "https://api.dicebear.com/9.x/lorelei/svg?seed=user_hr",
"avatar_initials": "HH",
"member_id": null
}
}
```
---
## PATCH /v1/me/profile
Update display preferences (locale / name stub / active role for multi-role users).
| | |
|---|---|
| **Auth** | Required |
| **Roles** | Any |
### Request
```json
{
"locale": "am",
"active_role": "SUPER_USER"
}
```
`active_role` must be one of the user’s `roles[]` (demo: `user_super` holds all four).
### Response 200
Updated profile object.
### Errors
| Code | HTTP | When |
|------|------|------|
| `VALIDATION_FAILED` | 400 | Invalid locale or role not in `roles[]` |
---
## UI routes
| Route | Purpose |
|-------|---------|
| `/login` | Sign-in methods (Google, email, phone, Telegram — mocked) + demo persona dropdown + locale |
| `/profile` | Own profile — avatar, org, role switch, sign out |
## Clinical withhold notes
Auth endpoints do not expose clinical data. Role list / `active_role` on session determines withhold behavior on downstream endpoints.