# Интеграция auth-portal ↔ MikrotikManager Единый вход: пользователь логинится на auth-portal, получает JWT, переходит в MikrotikManager с токеном в URL fragment. Backend MM проверяет JWT и права `mm:*`. ## Архитектура ``` Browser → MikrotikManager UI (нет token) → redirect AUTH_PORTAL_URL/?return_to=…/auth/callback → login → redirect return_to#access_token=… → MM /auth/callback сохраняет token (mmapp_token) → API Authorization: Bearer … ``` Общий секрет: `JWT_SECRET` / `AUTH_JWT_SECRET` (HS256). Issuer: `ISSUER` / `AUTH_ISSUER`. App id в портале: **`mm`** (каталог permissions). ## Локальный запуск ### 1. auth-portal ```bash cd auth-portal pnpm install # JWT_SECRET=dev-secret-change-me # RETURN_TO_ALLOWLIST=.shnt.top,localhost,http://localhost:3000 pnpm --filter @authportal/api dev # :8080 pnpm --filter web dev # :5175 ``` Bootstrap: `admin@shnt.top` / `admin`. В админке выдайте app **mm** и permissions `mm:*`. ### 2. MikrotikManager ```bash cd MikrotikManager npm install ``` Backend `backend/.env`: ```env AUTH_REQUIRED=true AUTH_JWT_SECRET=dev-secret-change-me AUTH_ISSUER=https://auth.shnt.top AUTH_PORTAL_URL=http://localhost:5175 CORS_ORIGIN=http://localhost:3000 ``` Frontend: `NEXT_PUBLIC_AUTH_PORTAL_URL=http://localhost:5175` (или runtime `GET /api/auth/config` через proxy). ```bash npm --prefix backend run dev npm run dev ``` Откройте `http://localhost:3000` → редирект на portal → после логина NavUser показывает имя/email. ## Permissions ↔ API / UI Иерархия: `admin` ⊃ `write` ⊃ `read` в рамках одной секции. | Permission | API (prefix) | UI | |------------|--------------|-----| | `mm:dashboard:read` | sidebar-counts, events (GET) | `/dashboard` | | `mm:servers:read` / `write` | `/api/servers*` | `/servers` | | `mm:filters:read` / `write` | `/api/filters*` | `/filters`, GRE | | `mm:bgp:read` / `write` | `/api/bgp*` | BGP | | `mm:uptime:read` / `write` | `/api/uptime*` | `/uptime` | | `mm:traffic:read` / `write` | `/api/traffic*` | `/traffic` | | `mm:alerts:read` / `write` | `/api/alerts*` | `/alerts` | | `mm:backups:read` / `write` | `/api/backups*` | `/backups` | | `mm:certificates:read` / `write` | `/api/certificates*` | certificates | | `mm:network:read` / `write` | `/api/network*`, OSPF, recursive, probes | network | | `mm:settings:admin` | `/api/system/*`, scheduler, evobgp settings | `/settings` | Без app `mm` в JWT `apps` → **403** на защищённые `/api/*`. `AUTH_REQUIRED=false` — API открыт (dev без portal). ## App Switcher `CURRENT_APP_ID = mm`. Публичный конфиг: `GET {AUTH_PORTAL_URL}/api/v1/app-switcher`. ## Logout (SSO) Очистить `mmapp_token` → редирект на **`{AUTH_PORTAL_URL}/logout`**. ## Production См. общий стек CDN+MM: `deploy/docker-compose.cdn-mm.yml` / `deploy/env.cdn-mm.example`. ```env AUTH_REQUIRED=true AUTH_JWT_SECRET=<тот же JWT_SECRET портала> AUTH_ISSUER=https://auth.shnt.top AUTH_PORTAL_URL=https://auth.shnt.top CORS_ORIGIN=https://mm.shnt.top ``` В portal: `RETURN_TO_ALLOWLIST` включает `https://mm.shnt.top` (или `.shnt.top`). ## Troubleshooting | Симптом | Причина | |---------|---------| | SSO loop | разный `JWT_SECRET` или `ISSUER` | | 403 «Нет доступа к приложению» | нет app `mm` у пользователя | | 403 «Недостаточно прав» | нет нужного `mm:…` | | return_to rejected | origin MM не в `RETURN_TO_ALLOWLIST` |