diff --git a/changelog.d/maintenance/7786-management-auth-guide.md b/changelog.d/maintenance/7786-management-auth-guide.md new file mode 100644 index 0000000000..54f84a79b9 --- /dev/null +++ b/changelog.d/maintenance/7786-management-auth-guide.md @@ -0,0 +1 @@ +- **docs(auth):** distinguish dashboard sessions, `oma_live_…` Access Tokens, manage-scoped API keys, and inference keys ([#7786](https://github.com/diegosouzapw/OmniRoute/issues/7786)) diff --git a/docs/guides/MANAGEMENT-AUTH.md b/docs/guides/MANAGEMENT-AUTH.md index 25e0d7ae59..31391e4b7f 100644 --- a/docs/guides/MANAGEMENT-AUTH.md +++ b/docs/guides/MANAGEMENT-AUTH.md @@ -1,47 +1,159 @@ --- title: "Management Authentication" version: 3.8.50 -lastUpdated: 2026-08-05 +lastUpdated: 2026-08-20 --- # Management Authentication -OmniRoute uses four distinct credential families for management access. This guide -distinguishes them by purpose, scope, and locality. +OmniRoute has **four credential families** that can authorize management routes. +They are not interchangeable. Inference API keys (`sk-…`) do **not** manage the +server unless they were explicitly granted `manage` or `admin` scope. -| Credential | Scope | Locality | Use Case | -|-------------------------|--------------------|---------------|-----------------------------------| -| Dashboard JWT session | Full management | Localhost | Web dashboard login | -| CLI machine-id token | Full management | Per-machine | `omniroute` CLI commands | -| Scoped `oma_` token | Configurable scope | External | Automation / CI / API access | -| Manage-scope API key | `manage` scope | External | Management API calls | +Canonical implementation: `src/lib/api/requireManagementAuth.ts`. -## Dashboard JWT Session +| Credential | Typical form | Created where | Intended use | Management capability | +|---|---|---|---|---| +| Dashboard session | `auth_token` cookie | Dashboard login | Browser UI | Full dashboard management, subject to CSRF, locality, and always-protected-route rules | +| Local CLI machine token | internal / local | CLI bootstrap (`omniroute` on the same machine) | Local CLI | Local management only | +| Scoped Access Token | `oma_live_…` | **Settings → Access Tokens** or `omniroute connect` | Remote CLI and management API | Must satisfy the route's required `read`, `write`, or `admin` scope | +| Inference API key | `sk-…` (and other API-key prefixes) | **API Manager / API Keys** | `/v1/*` inference | **None** unless the key metadata includes `manage` or `admin` | -Generated on dashboard login (`/api/auth/login`). Stored in HTTP-only cookie. -Valid for the session duration. Cannot be used from external hosts. +`oma_` credentials are management/CLI credentials. They are **not** inference API keys. -## CLI Machine-ID Token +If login/API-key auth is disabled for the server, some management routes may +accept unauthenticated calls. Local-only and always-protected routes still apply +their own rules. Presenting one of these credentials is therefore not universally +mandatory, and possessing one is not universally sufficient without the required +scope and route locality. -Created by `omniroute auth login` on first use. Stored in `~/.omniroute/auth.json`. -Used by the CLI for all management operations. Tied to the machine identity. +Related: [Remote Mode](./REMOTE-MODE.md) (how `oma_live_…` is minted for a remote CLI). -## Scoped `oma_` Access Token +--- -Created via dashboard or CLI with configurable scopes (e.g., `manage`, `read`). -Format: `oma_`. Used for programmatic access from external systems. +## Scope matrices -## Manage-Scope API Key +These two scope vocabularies are **different**. Do not mix them. -Standard API key with the `manage` scope enabled. Created in dashboard API Keys page. -Used for management API calls from external hosts. +### Access Token scopes (`oma_live_…`) -## Header Examples +| Scope | Typical operations | +|---|---| +| `read` | List/status GETs that the token is allowed to see | +| `write` | Mutations (create/update/delete) below admin | +| `admin` | Full remote CLI / connect token (password bootstrap defaults here) | -``` -Authorization: Bearer oma_abc123def456 -Authorization: Bearer -Cookie: omniroute_session= +A token with `read` cannot call a `write` route. Runtime message shape: +`Access token scope '' is insufficient; '' required.` + +### API-key management scopes + +| Scope | Meaning | +|---|---| +| (none) | Inference only. Management routes return 403. | +| `manage` | Management API (same gate as `requireManagementAuth` API-key branch) | +| `admin` | Also satisfies `hasManageScope` (treated as management-capable) | + +Enable `manage` on the key in the API Keys / API Manager UI. Do not reuse a +chat client key for automation unless you deliberately granted that scope. + +--- + +## How to create and revoke + +### Dashboard session + +1. Open `/login`, sign in with the management password (`INITIAL_PASSWORD` on first boot). +2. Cookie `auth_token` is HttpOnly. Browser dashboard uses it automatically. +3. Log out via `/api/auth/logout`. There is no long-lived secret to copy. + +### Local CLI machine token + +1. Run `omniroute` on the **same host** as the server (loopback). +2. The CLI bootstraps a machine-id token under `~/.omniroute/` (chmod 600). +3. This does **not** work from another machine. Use an Access Token for remote CLI. + +### Scoped Access Token (`oma_live_…`) + +1. Dashboard: **Settings → Access Tokens** → create (name + scope). **The secret is shown once.** +2. Or CLI: `omniroute connect ` (password → token). See [Remote Mode](./REMOTE-MODE.md). +3. Header: `Authorization: Bearer oma_live_…` +4. Revoke from the same Access Tokens page (or delete the CLI context). +5. Server stores only a hash. Treat the plaintext like a password. + +### Manage-scoped API key + +1. Dashboard: **API Manager / API Keys** → create or edit a key → enable `manage` (or `admin`). +2. Header: `Authorization: Bearer sk-…` (the key's actual prefix). +3. Revoke or strip `manage` in the same UI. +4. Least privilege for automation that is not the CLI: prefer a `read` Access Token for GET-only jobs; use `manage` on an API key only when the caller must also speak `/v1` and management. + +--- + +## Header format + +```http +Authorization: Bearer oma_live_ +Authorization: Bearer sk- +Cookie: auth_token= ``` -See `docs/reference/API_REFERENCE.md` for endpoint-specific auth requirements. +Do not put management credentials in the URL path or query string. Management +auth is header/cookie only. + +--- + +## Copy-paste examples + +Read-only (list providers). Use a `read` Access Token: + +```bash +curl -sS "$OMNIROUTE_URL/api/providers" \ + -H "Authorization: Bearer oma_live_" +``` + +Modifying (create a provider connection). Use `write`/`admin` Access Token or a +manage-scoped API key: + +```bash +curl -sS -X POST "$OMNIROUTE_URL/api/providers" \ + -H "Authorization: Bearer oma_live_" \ + -H "Content-Type: application/json" \ + -d '{"provider":"openai","apiKey":""}' +``` + +Inference (not management). Ordinary API key, no `manage` required: + +```bash +curl -sS "$OMNIROUTE_URL/v1/models" \ + -H "Authorization: Bearer sk-" +``` + +--- + +## Current runtime errors (do not echo secrets) + +| Situation | Typical status | Message (sanitized) | +|---|---|---| +| No credential | 401 | `Authentication required` | +| Invalid/expired `oma_live_…` | 401 | `Invalid or expired access token` | +| Valid API key without `manage`/`admin` | 403 | `API key lacks 'manage' scope. Enable it in the API Keys dashboard.` | +| Invalid ordinary API key on a management route | 403 | `Invalid management token` | +| Access Token scope too low | 403 | `Access token scope '' is insufficient; '' required.` | + +"Invalid management token" means the bearer was **not** accepted as a management +credential. It does **not** tell you which family to mint. Use the table above: +inference keys need `manage` scope; remote CLI needs `oma_live_…`; the dashboard +uses the session cookie. + +--- + +## Recommended least-privilege choice + +| Caller | Use | +|---|---| +| Browser | Dashboard session | +| CLI on the server host | Machine token | +| CLI on a laptop talking to a remote server | `oma_live_…` from `omniroute connect` | +| CI / scripts (management only) | `oma_live_…` with the smallest scope that works | +| CI that must call both `/v1` and `/api` | API key with `manage` **or** two credentials | diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 79cee7893b..54689a0a57 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -7329,12 +7329,18 @@ components: BearerAuth: type: http scheme: bearer - description: API key obtained from the OmniRoute dashboard + description: > + Two bearer families are accepted. Inference API keys (typically `sk-…`) + authorize `/v1/*`. Management routes also accept `oma_live_…` Access Tokens + (Settings → Access Tokens / `omniroute connect`) and API keys whose metadata + includes `manage` or `admin` scope. See docs/guides/MANAGEMENT-AUTH.md. + Bearer credentials are accepted on management routes that use this scheme; + they are not rejected solely for being Bearer. ManagementSessionAuth: type: apiKey in: cookie name: auth_token - description: Dashboard management session cookie for protected management routes + description: Dashboard management session cookie (auth_token) for protected management routes. Distinct from Bearer Access Tokens and API keys. See docs/guides/MANAGEMENT-AUTH.md. parameters: ResourceId: diff --git a/docs/providers/ZED-DOCKER.md b/docs/providers/ZED-DOCKER.md index 21b096b48c..e3519ea70c 100644 --- a/docs/providers/ZED-DOCKER.md +++ b/docs/providers/ZED-DOCKER.md @@ -103,7 +103,7 @@ The manual import endpoint can also be called directly: ``` POST /api/providers/zed/manual-import Content-Type: application/json -Authorization: Bearer +Authorization: Bearer { "provider": "openai", diff --git a/docs/reference/API_REFERENCE.md b/docs/reference/API_REFERENCE.md index b2f5983b78..359ae4750e 100644 --- a/docs/reference/API_REFERENCE.md +++ b/docs/reference/API_REFERENCE.md @@ -703,6 +703,10 @@ X-OmniRoute-No-Cache: true ## Dashboard & Management +Management routes (`/api/*` except public auth/login) are **not** authorized by +ordinary inference API keys. Credential families, scopes, and curl examples: +[Management Authentication](../guides/MANAGEMENT-AUTH.md). + ### Authentication | Endpoint | Method | Description | @@ -1668,9 +1672,14 @@ See [Security > Guardrails](../security/GUARDRAILS.md) for full details. ## Authentication +See [Management Authentication](../guides/MANAGEMENT-AUTH.md) for the four +credential families (dashboard session, local CLI token, `oma_live_…` Access +Token, manage-scoped API key) and how they differ from inference keys. + - Dashboard routes (`/dashboard/*`) use `auth_token` cookie - Login uses saved password hash; fallback to `INITIAL_PASSWORD` - `requireLogin` toggleable via `/api/settings/require-login` - `/v1/*` routes optionally require Bearer API key when `REQUIRE_API_KEY=true` +- "management token" / "management-scoped API key" in this reference means one of the families in that guide — not an undefined extra secret type > **Breaking change (v3.8.0)** — `/api/v1/agents/tasks/*` and the cooldown management endpoints now require **management auth** (dashboard `auth_token` cookie or a management-scoped API key). Clients that previously called these routes unauthenticated will receive `401 Unauthorized`. See commit `588a0333` (`fix(auth): require management auth for agent and cooldown APIs`). diff --git a/public/openapi.yaml b/public/openapi.yaml index ca00d23623..caa5aac6ea 100644 --- a/public/openapi.yaml +++ b/public/openapi.yaml @@ -5270,12 +5270,18 @@ components: BearerAuth: type: http scheme: bearer - description: API key obtained from the OmniRoute dashboard + description: > + Two bearer families are accepted. Inference API keys (typically `sk-…`) + authorize `/v1/*`. Management routes also accept `oma_live_…` Access Tokens + (Settings → Access Tokens / `omniroute connect`) and API keys whose metadata + includes `manage` or `admin` scope. See docs/guides/MANAGEMENT-AUTH.md. + Bearer credentials are accepted on management routes that use this scheme; + they are not rejected solely for being Bearer. ManagementSessionAuth: type: apiKey in: cookie name: auth_token - description: Dashboard management session cookie for protected management routes + description: Dashboard management session cookie (auth_token) for protected management routes. Distinct from Bearer Access Tokens and API keys. See docs/guides/MANAGEMENT-AUTH.md. parameters: ResourceId: diff --git a/src/app/(dashboard)/dashboard/settings/components/AccessTokensTab.tsx b/src/app/(dashboard)/dashboard/settings/components/AccessTokensTab.tsx index d5eb058a73..d4e9b88f9b 100644 --- a/src/app/(dashboard)/dashboard/settings/components/AccessTokensTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/AccessTokensTab.tsx @@ -126,7 +126,7 @@ export default function AccessTokensTab() {

{L( "accessTokensDescription", - "Scoped tokens that let the omniroute CLI manage this server remotely. Distinct from inference API keys. The secret is shown once." + "Scoped tokens that let the omniroute CLI manage this server remotely. Distinct from inference API keys. The secret is shown once. Automation guide: /docs/guides/MANAGEMENT-AUTH." )}