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>
312 lines
6.7 KiB
Markdown
312 lines
6.7 KiB
Markdown
# 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.
|