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

6.0 KiB
Raw Permalink Blame History

Интеграция 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

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:

VITE_VPS_APP_URL=http://localhost:5173

Bootstrap: [email protected] / admin. В админке выдайте app vps и нужные permissions.

2. vps-tracker

cd vps-tracker
pnpm install

Корень / API env (или shell):

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:

VITE_AUTH_ENABLED=true
VITE_AUTH_PORTAL_URL=http://localhost:5175
pnpm --filter @cfdm/api dev
pnpm --filter web dev   # :5173

Откройте http://localhost:5173 → редирект на portal → после логина обратно в VPS.

Permissions ↔ API / UI

Иерархия: adminwriteread в рамках одной секции.

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.
  • AssignPOST /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 apps403 на весь /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 (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)