# Интеграция 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: `admin@shnt.top` / `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:settings:admin` | `/api/settings`, backup, audit, migrate | `/settings`, `/audit` | Без app `vps` в JWT `apps` → **403** на весь `/api/*` (кроме health и CFDM integration). `AUTH_REQUIRED=false` — auth выключен (удобно для локальной разработки без portal). ## Troubleshooting | Симптом | Причина | |---------|---------| | 401 на API | Нет/битый Bearer; разные `JWT_SECRET` | | 403 «нет доступа к приложению» | В portal не выдан app `vps` | | 403 на write | Только `*:read` в permissions | | Loop на login | `return_to` не в `RETURN_TO_ALLOWLIST` | | CORS | Portal и VPS на разных origin — fragment handoff не требует CORS для token | ## 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)