Files
auth-portal/docs/integrate-vps-tracker.md
DenozordecandCursor 57e34ff5e9
Build and Push Auth Portal Docker Image / build-and-push (push) Successful in 1m46s
Build and Push Auth Portal Docker Image / create-release (push) Skipped
feat(admin): ingest аудита из apps и users 1:1 с Sheet журнала
Добавлен POST /api/v1/ingest/audit, фильтры source_app/user_id, last_login_at; таблица пользователей по solution-users-1 с журналом в Sheet.

Co-authored-by: Cursor <[email protected]>
2026-07-21 13:24:28 +07:00

139 lines
6.0 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.
# Интеграция auth-portal ↔ VPS Tracker
Единый вход: пользователь логинится на auth-portal, получает JWT, переходит в VPS Tracker с токеном в URL fragment. VPS API проверяет JWT и права `vps:*`.
## Архитектура
```
Browser → VPS UI (нет token)
→ redirect AUTH_PORTAL_URL/?return_to=http://localhost:5173/auth/callback
→ login
→ redirect return_to#access_token=…
→ VPS /auth/callback сохраняет token
→ API Authorization: Bearer …
```
Общий секрет: `JWT_SECRET` / `AUTH_JWT_SECRET` (HS256). Issuer: `ISSUER` / `AUTH_ISSUER`.
## Локальный запуск
### 1. auth-portal
```bash
cd auth-portal
pnpm install
cp .env.example .env
# JWT_SECRET=dev-secret-change-me
# RETURN_TO_ALLOWLIST=.shnt.top,localhost,http://localhost:5173
pnpm --filter @authportal/api dev # :8080
pnpm --filter web dev # :5175
```
В `apps/web/.env.local`:
```env
VITE_VPS_APP_URL=http://localhost:5173
```
Bootstrap: `[email protected]` / `admin`. В админке выдайте app **vps** и нужные permissions.
### 2. vps-tracker
```bash
cd vps-tracker
pnpm install
```
Корень / API env (или shell):
```env
AUTH_REQUIRED=true
AUTH_JWT_SECRET=dev-secret-change-me
AUTH_ISSUER=https://auth.shnt.top
AUTH_PORTAL_URL=http://localhost:5175
```
`apps/web/.env.local`:
```env
VITE_AUTH_ENABLED=true
VITE_AUTH_PORTAL_URL=http://localhost:5175
```
```bash
pnpm --filter @cfdm/api dev
pnpm --filter web dev # :5173
```
Откройте http://localhost:5173 → редирект на portal → после логина обратно в VPS.
## Permissions ↔ API / UI
Иерархия: `admin``write``read` в рамках одной секции.
| Permission | API | UI |
|------------|-----|-----|
| `vps:dashboard:read` | `GET /api/dashboard/*` | `/dashboard` |
| `vps:vps:read` | `GET /api/vps`, projects, reports-related reads | `/vps`, `/tariffs`, `/projects`, аналитика |
| `vps:vps:write` | POST/PUT/PATCH/DELETE `/api/vps`, projects | create/edit/delete VPS |
| `vps:accounts:read` | GET providers, provider-accounts | `/providers`, `/accounts` |
| `vps:accounts:write` | мутации providers/accounts | формы аккаунтов |
| `vps:payments:read` | GET payments, balance-ledger | `/payments`, `/balance` |
| `vps:payments:write` | мутации payments / ledger | формы платежей |
| `vps:sync:write` | POST `/api/sync/*`, GET sync status | `/sync-journal`, кнопка sync |
| `vps:spaces:admin` | управление всеми пространствами, grants из main | space switcher (все spaces), share/assign |
| `vps:settings:admin` | `/api/settings`, backup, audit, migrate | `/settings`, `/audit`, `/spaces` |
## Пространства (spaces)
Данные VPS Tracker изолированы по `spaceId`. Заголовок **`X-Space-Id`**.
- **space-main** — основное (админский пул); существующие данные мигрируют сюда.
- **personal** — автосоздание `space-user-{userId}` при первом входе.
- **Share (ACL)** — `POST /api/spaces/:id/vps/:vpsId/share` — VPS остаётся в исходном space.
- **Assign** — `POST /api/spaces/:id/vps/:vpsId/assign` — перенос; `providerAccountId` сбрасывается.
- Участники: `GET/POST/PATCH/DELETE /api/spaces/:id/members` (userId из auth-portal).
- Env: `VPS_MAIN_SPACE_OWNER_USER_ID` — owner для main при bootstrap.
Без app `vps` в JWT `apps`**403** на весь `/api/*` (кроме health и CFDM integration).
`AUTH_REQUIRED=false` — auth выключен (удобно для локальной разработки без portal); данные в `space-main`.
## App Switcher
Публичный конфиг: `GET {AUTH_PORTAL_URL}/api/v1/app-switcher`. VPS chrome читает его через `ensureAuthConfig().portalUrl`; offline fallback — defaults с ids `cfdm` | `vps` | `bgp`.
Редактор только на портале: **Админка → Ссылки приложений** (`/admin/apps`). В VPS Settings → Integrations — read-only ссылка.
`CURRENT_APP_ID = vps`. Фильтр меню по JWT `apps[]` при наличии claims.
## Audit ingest
Dual-write локального журнала в portal: [`integrate-audit-ingest.md`](./integrate-audit-ingest.md) (`source_app: vps`).
## Troubleshooting
| Симптом | Причина |
|---------|---------|
| 401 на API | Нет/битый Bearer; разные `JWT_SECRET` |
| 403 «нет доступа к приложению» | В portal не выдан app `vps` |
| 403 на write | Только `*:read` в permissions |
| Loop на login | `return_to` не в `RETURN_TO_ALLOWLIST` |
| Infinite SSO / 429 | Просроченный JWT в portal localStorage; или разный `JWT_SECRET`/`ISSUER`. Portal чистит expired token; VPS блокирует повторный handoff 12с |
| «Выйти» сразу возвращает в приложение | Старый клиент редиректил на `/?return_to=…` при живой portal-сессии. Нужен редирект на **`/logout`** (см. ниже) |
| CORS | Portal и VPS на разных origin — fragment handoff не требует CORS для token; public app-switcher GET тоже CORS-open |
## Logout (SSO)
«Выйти» в приложении: очистить локальный JWT → `AUTH_PORTAL_URL/logout` (без `return_to`).
Портал на `/logout`: `POST /api/v1/auth/logout` (revoke refresh cookie) → `clearToken()` → форма логина.
## Production
- Один `JWT_SECRET` в secret store обоих сервисов
- `ISSUER=https://auth.shnt.top`
- `RETURN_TO_ALLOWLIST=.shnt.top,https://vps.shnt.top`
- `AUTH_REQUIRED=true`, `VITE_AUTH_ENABLED=true`
- `VITE_VPS_APP_URL=https://vps.shnt.top` (portal)