feat: add app-level authentication foundation
This commit is contained in:
+20
-6
@@ -22,6 +22,7 @@
|
||||
## 3. Основные ресурсы
|
||||
|
||||
- `workspaces`
|
||||
- `auth`
|
||||
- `memberships`
|
||||
- `invitations`
|
||||
- `operations`
|
||||
@@ -60,7 +61,20 @@
|
||||
- `POST /invitations` возвращает metadata invitation и одноразовый `invite_token`;
|
||||
- `invite_token` доступен только в create-response и не возвращается повторно в list endpoints.
|
||||
|
||||
### 5.2. Operations
|
||||
### 5.2. Auth and session
|
||||
|
||||
- `POST /api/auth/login`
|
||||
- `POST /api/auth/logout`
|
||||
- `GET /api/auth/session`
|
||||
|
||||
Контракт:
|
||||
|
||||
- `POST /login` принимает `email` и `password`;
|
||||
- при успешном логине backend выставляет `HttpOnly` session cookie;
|
||||
- `GET /session` возвращает текущего пользователя и memberships;
|
||||
- `POST /logout` инвалидирует текущую session.
|
||||
|
||||
### 5.3. Operations
|
||||
|
||||
- `GET /api/admin/workspaces/{workspace_id}/operations`
|
||||
- `POST /api/admin/workspaces/{workspace_id}/operations`
|
||||
@@ -75,7 +89,7 @@
|
||||
- `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/export`
|
||||
- `POST /api/admin/workspaces/{workspace_id}/operations/import`
|
||||
|
||||
### 5.3. Samples and descriptors
|
||||
### 5.4. Samples and descriptors
|
||||
|
||||
- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/input-json`
|
||||
- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/output-json`
|
||||
@@ -84,7 +98,7 @@
|
||||
- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/descriptors/descriptor-set`
|
||||
- `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/grpc/services`
|
||||
|
||||
### 5.4. Upstream auth profiles
|
||||
### 5.5. Upstream auth profiles
|
||||
|
||||
- `GET /api/admin/workspaces/{workspace_id}/auth-profiles`
|
||||
- `POST /api/admin/workspaces/{workspace_id}/auth-profiles`
|
||||
@@ -92,7 +106,7 @@
|
||||
- `PATCH /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
||||
- `DELETE /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}`
|
||||
|
||||
### 5.5. Agents
|
||||
### 5.6. Agents
|
||||
|
||||
- `GET /api/admin/workspaces/{workspace_id}/agents`
|
||||
- `POST /api/admin/workspaces/{workspace_id}/agents`
|
||||
@@ -105,7 +119,7 @@
|
||||
- `POST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindings`
|
||||
- `DELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindings/{operation_id}`
|
||||
|
||||
### 5.6. Platform API keys
|
||||
### 5.7. Platform API keys
|
||||
|
||||
- `GET /api/admin/workspaces/{workspace_id}/platform-api-keys`
|
||||
- `POST /api/admin/workspaces/{workspace_id}/platform-api-keys`
|
||||
@@ -118,7 +132,7 @@
|
||||
- `secret` доступен только в create-response;
|
||||
- list endpoints возвращают только metadata, `prefix`, `status`, `scopes` и `last_used_at`.
|
||||
|
||||
### 5.7. Observability
|
||||
### 5.8. Observability
|
||||
|
||||
- `GET /api/admin/workspaces/{workspace_id}/logs`
|
||||
- `GET /api/admin/workspaces/{workspace_id}/logs/{log_id}`
|
||||
|
||||
@@ -378,35 +378,33 @@ UI-файлы:
|
||||
|
||||
Что уже есть реально:
|
||||
|
||||
- внешний `Basic Auth` на `nginx`
|
||||
- login screen и mock redirect flow
|
||||
|
||||
Что отсутствует:
|
||||
Что уже есть:
|
||||
|
||||
- session auth backend;
|
||||
- login endpoint;
|
||||
- logout endpoint;
|
||||
- current user endpoint
|
||||
|
||||
Отдельный конфликт:
|
||||
|
||||
- это не баг реализации, а отсутствие целого auth слоя;
|
||||
- login page пока не должна позиционироваться как production-ready flow.
|
||||
- `POST /api/auth/login`;
|
||||
- `POST /api/auth/logout`;
|
||||
- `GET /api/auth/session`;
|
||||
- protected page guard через backend session.
|
||||
|
||||
Простой итог:
|
||||
|
||||
- пока не интегрировать как реальный backend flow;
|
||||
- оставить как временный mock screen до отдельного решения по auth.
|
||||
- login уже перешел на live backend flow;
|
||||
- защищенные страницы проверяют server-side session;
|
||||
- `localStorage` больше не должен быть source of truth для auth.
|
||||
|
||||
## 5. Отдельные UI-vs-backend конфликты
|
||||
|
||||
### 5.1. Login vs Basic Auth
|
||||
|
||||
UI предполагает встроенный login, backend сейчас защищен внешним `Basic Auth`.
|
||||
Конфликт закрыт. UI использует встроенный login, backend перешел на app-level session auth.
|
||||
|
||||
Решение:
|
||||
|
||||
- не делать вид, что login уже рабочий;
|
||||
- отложить интеграцию login до отдельного auth этапа.
|
||||
- использовать `POST /api/auth/login`, `POST /api/auth/logout`, `GET /api/auth/session`;
|
||||
- хранить auth state в `HttpOnly` cookie;
|
||||
- использовать `localStorage` только как UI mirror для display state и не считать его source of truth.
|
||||
|
||||
### 5.2. Agent draft/edit lifecycle
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ Crank - платформа для публикации внешних API в в
|
||||
Отдельный слой, не связанный с upstream auth:
|
||||
|
||||
- `User`
|
||||
- `UserSession`
|
||||
- `Membership`
|
||||
- `Invitation`
|
||||
- `PlatformApiKey`
|
||||
@@ -215,6 +216,7 @@ GraphQL в MCP публикуется как фиксированная опер
|
||||
- `AgentOperationBinding`
|
||||
- `AuthProfile`
|
||||
- `PlatformApiKey`
|
||||
- `UserSession`
|
||||
- `InvocationLog`
|
||||
- `UsageRollup`
|
||||
|
||||
|
||||
+13
-4
@@ -194,12 +194,15 @@
|
||||
|
||||
Что уже есть:
|
||||
|
||||
- внешний `Basic Auth` на уровне `nginx`.
|
||||
- mock `login.html` и `login.js`;
|
||||
- `User`, `Membership`, `Invitation` и `PlatformApiKey` как часть access layer.
|
||||
|
||||
Чего не хватает:
|
||||
|
||||
- либо собственный auth/session backend;
|
||||
- либо временный согласованный bridge, если login screen оставляем как demo flow.
|
||||
- app-level auth/session backend;
|
||||
- password hash storage;
|
||||
- session cookie;
|
||||
- current user endpoint.
|
||||
|
||||
## 5. Архитектурные конфликты, которые нужно разобрать отдельно
|
||||
|
||||
@@ -232,7 +235,13 @@
|
||||
|
||||
Решение:
|
||||
|
||||
- зафиксировать временную и целевую auth model отдельно.
|
||||
- убрать внешний `Basic Auth` как основной способ входа;
|
||||
- реализовать app-level auth/session backend;
|
||||
- отделить browser session auth от `PlatformApiKey`.
|
||||
|
||||
Статус:
|
||||
|
||||
- закрыто в `feat/auth-foundation`.
|
||||
|
||||
## 6. Приоритет реализации
|
||||
|
||||
|
||||
+29
-4
@@ -182,10 +182,35 @@
|
||||
- `id`
|
||||
- `email`
|
||||
- `display_name`
|
||||
- `password_hash`
|
||||
- `status`
|
||||
- `created_at`
|
||||
|
||||
### 3.9. `Membership`
|
||||
Пароль:
|
||||
|
||||
- хранится только как `Argon2id` hash;
|
||||
- plaintext пароль не сохраняется;
|
||||
- верификация использует `password_pepper` из env.
|
||||
|
||||
### 3.9. `UserSession`
|
||||
|
||||
Поля:
|
||||
|
||||
- `id`
|
||||
- `user_id`
|
||||
- `secret_hash`
|
||||
- `status`
|
||||
- `expires_at`
|
||||
- `last_seen_at`
|
||||
- `created_at`
|
||||
|
||||
Секрет:
|
||||
|
||||
- браузеру выдается только opaque session token;
|
||||
- в persistent storage сохраняется только `secret_hash`;
|
||||
- подпись и верификация используют `session_secret` из env.
|
||||
|
||||
### 3.10. `Membership`
|
||||
|
||||
Поля:
|
||||
|
||||
@@ -194,7 +219,7 @@
|
||||
- `role`
|
||||
- `created_at`
|
||||
|
||||
### 3.10. `InvitationToken`
|
||||
### 3.11. `InvitationToken`
|
||||
|
||||
Поля:
|
||||
|
||||
@@ -211,7 +236,7 @@
|
||||
- полный invite token показывается только один раз при создании;
|
||||
- в persistent storage сохраняется только `token_hash`.
|
||||
|
||||
### 3.11. `InvocationLog`
|
||||
### 3.12. `InvocationLog`
|
||||
|
||||
Продуктовая запись о вызове tool.
|
||||
|
||||
@@ -230,7 +255,7 @@
|
||||
- `response_preview`
|
||||
- `created_at`
|
||||
|
||||
### 3.12. `UsageRollup`
|
||||
### 3.13. `UsageRollup`
|
||||
|
||||
Агрегированная статистика по периоду.
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
- `workspaces`
|
||||
- `users`
|
||||
- `user_sessions`
|
||||
- `memberships`
|
||||
- `invitation_tokens`
|
||||
- `operations`
|
||||
@@ -166,9 +167,24 @@
|
||||
- `id`
|
||||
- `email`
|
||||
- `display_name`
|
||||
- `password_hash`
|
||||
- `status`
|
||||
- `created_at`
|
||||
|
||||
### `user_sessions`
|
||||
|
||||
- `id`
|
||||
- `user_id`
|
||||
- `secret_hash`
|
||||
- `status`
|
||||
- `expires_at`
|
||||
- `last_seen_at`
|
||||
- `created_at`
|
||||
|
||||
Ограничения:
|
||||
|
||||
- `unique (user_id, id)`
|
||||
|
||||
### `memberships`
|
||||
|
||||
- `workspace_id`
|
||||
|
||||
+1
-1
@@ -221,5 +221,5 @@ CD для MVP должен:
|
||||
- TLS и renewal сертификатов;
|
||||
- backup для `postgres` и artifact storage;
|
||||
- секреты вне репозитория;
|
||||
- ограничение доступа к `admin-api`;
|
||||
- app-level auth для `admin-api` и bootstrap admin credentials в env;
|
||||
- rollback на предыдущий image tag или предыдущую compose revision.
|
||||
|
||||
+12
-2
@@ -107,16 +107,26 @@ Local development:
|
||||
|
||||
- локальное окружение должно иметь доступ к `PostgreSQL`;
|
||||
- локальный storage;
|
||||
- упрощенная auth-модель admin-api.
|
||||
- app-level auth и bootstrap admin user через `.env`.
|
||||
|
||||
Demo/deployment:
|
||||
|
||||
- `PostgreSQL`;
|
||||
- локальный или сетевой storage;
|
||||
- включенная auth-защита admin-api;
|
||||
- включенная app-level auth-защита admin-api;
|
||||
- стабильный `Streamable HTTP` endpoint для MCP.
|
||||
- containerized runtime через `Docker` и `docker-compose`.
|
||||
|
||||
### Auth env
|
||||
|
||||
Для app-level auth нужны:
|
||||
|
||||
- `CRANK_SESSION_SECRET`
|
||||
- `CRANK_PASSWORD_PEPPER`
|
||||
- `CRANK_BOOTSTRAP_ADMIN_EMAIL`
|
||||
- `CRANK_BOOTSTRAP_ADMIN_PASSWORD`
|
||||
- `CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME`
|
||||
|
||||
## 8.1. Delivery artifacts
|
||||
|
||||
Для production-like запуска проект должен поставляться с:
|
||||
|
||||
Reference in New Issue
Block a user