diff --git a/docs/backend/ADMIN-CREDENTIAL-AUTH-HANDOFF.md b/docs/backend/ADMIN-CREDENTIAL-AUTH-HANDOFF.md new file mode 100644 index 0000000..df93af7 --- /dev/null +++ b/docs/backend/ADMIN-CREDENTIAL-AUTH-HANDOFF.md @@ -0,0 +1,102 @@ +# Admin credential authentication handoff + +## Current production state + +`admin.gorbushka.market` can authenticate through the existing Telegram +session flow. Login/password authentication is not implemented by the live +backend, so the frontend must not validate or embed administrator credentials. + +The existing `/admin-login` Ed25519 page is also not production-ready because +the backend challenge/verify endpoints do not exist. + +## Required backend API + +Tenant identity comes only from nginx's trusted `X-Storefront-Host` header. +Never accept a tenant or marketplace identifier from the login request body. + +### Create session + +```http +POST /api/identity/v1/session +Content-Type: application/json + +{ + "login": "gorbushka", + "password": "" +} +``` + +Success: + +```json +{ + "accessToken": "", + "refreshToken": "", + "expiresAt": "2026-08-21T04:00:00Z", + "mustChangePassword": true, + "user": { + "id": "", + "login": "gorbushka", + "displayName": "Gorbushka administrator", + "roles": ["MARKETPLACE_ADMIN"], + "tenantId": "" + } +} +``` + +Errors: + +- `400` malformed request. +- `401 INVALID_CREDENTIALS` with one generic message for unknown login and + wrong password. +- `403 TENANT_DISABLED` or `TENANT_MISMATCH`. +- `429 RATE_LIMITED` with `Retry-After`. + +### Session lifecycle + +```http +POST /api/identity/v1/session/refresh +DELETE /api/identity/v1/session +POST /api/identity/v1/session/change-password +GET /api/identity/v1/session/permissions +``` + +`change-password` accepts `{ currentPassword, newPassword }`. While +`mustChangePassword` is true, every non-auth admin endpoint returns +`403 PASSWORD_CHANGE_REQUIRED`. + +## Provisioning and security requirements + +- Generate a random one-time bootstrap password. Do not use the documented + deterministic `{slug}2026$` pattern in production. +- Store only an Argon2id password hash with a unique salt. +- Never log passwords, refresh tokens, authorization headers, or session IDs. +- Rate-limit by tenant, login, and source IP; add exponential backoff. +- Rotate refresh tokens and revoke the full token family on reuse. +- Enforce tenant and role authorization on every admin endpoint. Angular + guards are UI only. +- Audit login success/failure, password change, refresh-token reuse, logout, + and lockout without recording secrets. + +## Required nginx invariants + +Backend nginx changes must preserve: + +```nginx +proxy_set_header X-Storefront-Host $storefront_host; +proxy_set_header Origin ""; + +add_header Access-Control-Allow-Headers \ + "Authorization, Content-Type, AdminWebSessionID, WebSessionID, Currency, X-Language, X-Region, X-Requested-With" always; +``` + +For `Origin: https://admin.gorbushka.market`, `$storefront_host` must be +`gorbushka.market`. The API upstream remains `https://127.0.0.1:445` unless +the backend team deliberately changes the listening address. + +## Frontend follow-up after backend delivery + +Add the credential form to the admin-only login shell, submit only over HTTPS, +store the returned admin session separately from customer auth, force the +password-change screen when requested, and keep Telegram as an optional +fallback. Do not expose a non-functional credential form before the API ships. diff --git a/docs/backend/BACKEND-HANDOFF.md b/docs/backend/BACKEND-HANDOFF.md index 0949f1a..438b1e0 100644 --- a/docs/backend/BACKEND-HANDOFF.md +++ b/docs/backend/BACKEND-HANDOFF.md @@ -79,7 +79,7 @@ Nothing here is done yet — this is the setup a backend dev does on day one. 4. Implement the **bootstrap config endpoint** (§1a) — without it the frontend cannot render for any tenant. 5. Implement the Telegram session endpoints — the login flow is fully built client-side and blocked only on these. 6. Implement `GET /api/identity/v1/session/permissions` ([TRACK-S §2](TRACK-S-SECURITY-RBAC-CONTRACT.md)) — frontend route guards derive from it. -7. Seed per-marketplace bootstrap admins ([TRACK-S §8](TRACK-S-SECURITY-RBAC-CONTRACT.md)): login = marketplace slug, password = `{slug}2026$`, `mustChangePassword: true`. +7. Seed per-marketplace bootstrap admins ([TRACK-S §8](TRACK-S-SECURITY-RBAC-CONTRACT.md)): login = marketplace slug, cryptographically random one-time password delivered out of band, `mustChangePassword: true`. Steps 4–6 unblock the entire frontend. Everything after is feature work. diff --git a/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md b/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md index 38dfdeb..cd76820 100644 --- a/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md +++ b/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md @@ -96,7 +96,8 @@ Customer/seller PII is exposed only to roles that need it for their scope (e.g. Each marketplace ships with one bootstrap `MARKETPLACE_ADMIN` account, seeded at provisioning time (Phase 9 launch step): - `login` = marketplace slug (`projectName`) -- `password` = `{projectName}2026$`, flagged `mustChangePassword: true` +- `password` = cryptographically random one-time secret delivered out of band, + flagged `mustChangePassword: true` (never derive it from the marketplace slug) - Login succeeds but every non-auth request 403s with `PASSWORD_CHANGE_REQUIRED` until password is changed. ``` diff --git a/src/app/components/telegram-login/telegram-login.component.html b/src/app/components/telegram-login/telegram-login.component.html index 7c4f357..2cbef88 100644 --- a/src/app/components/telegram-login/telegram-login.component.html +++ b/src/app/components/telegram-login/telegram-login.component.html @@ -9,8 +9,8 @@ -

{{ 'auth.loginRequired' | translate }}

- +

{{ (mode === 'admin' ? 'auth.adminLoginRequired' : 'auth.loginRequired') | translate }}

+ @if (status() === 'checking') {