docs: document multiple auth providers and the callback URL change
This commit is contained in:
@@ -362,7 +362,8 @@ password-change endpoint at all, so there is no competing writer for the hash.
|
||||
|
||||
- **Bootstrap** — first run has no users. `GET /auth/bootstrap-status` drives `/setup`, `POST /auth/bootstrap` creates the first org plus its owner.
|
||||
- **Local auth** — email + password (bcrypt), `POST /auth/login`.
|
||||
- **OIDC** — configured _per org_ (`org_oidc`), issuer + client ID + encrypted client secret. `/auth/oidc/start` → `/auth/oidc/callback`.
|
||||
- **Auth providers** — configured _per instance_ in `auth_providers`, any number of them, each named and independently enabled. Issuer, client ID and an encrypted client secret per provider. `/auth/oidc/:providerId/start` → `/auth/oidc/:providerId/callback`. Presets (Entra, Google, Okta, GitHub) are a Go table in `server/internal/auth/presets.go` and expand to a real issuer on save, so nothing downstream knows a preset existed. GitHub is OAuth2 rather than OIDC and takes its own branch, requiring an address that is both primary **and** verified — an unverified address is not proof of control.
|
||||
- **Local login** — `settings.local_login_enabled`, a `*bool` because absent must mean enabled; a plain bool would disable password sign-in fleet-wide at upgrade. `services.CheckLockout` refuses any change leaving neither local login nor an enabled provider, and is enforced in the service layer so the settings path and the provider path cannot disagree.
|
||||
- **Sessions** — opaque 32-byte hex ID in the `km_session` cookie, session body stored in Redis with a 24h TTL.
|
||||
- **Roles** — `owner`, `admin`, `member`. `/api/settings` and `/api/org/*` require owner or admin.
|
||||
- **Host/org guard** — `APP_ROOT_LABEL` (default `vantage`) defines the app root label. A request to `<slug>.vantage.<tld>` resolves that org from the slug and rejects sessions belonging to a different one. Org lookups are cached for 60s.
|
||||
@@ -422,7 +423,8 @@ GET /install /install.ps1 # dynamic agent install scripts
|
||||
GET /update /update.ps1
|
||||
GET /auth/bootstrap-status
|
||||
POST /auth/bootstrap /auth/login /auth/logout
|
||||
GET /auth/me /auth/oidc/start /auth/oidc/callback
|
||||
GET /auth/me
|
||||
GET /auth/providers # {local_enabled, providers:[{id,name,preset}]} — no issuer, client ID or secret
|
||||
GET /api/secrets/:group/values # bearer token (ESO)
|
||||
```
|
||||
|
||||
@@ -450,7 +452,8 @@ agent GET /agent/latest-version
|
||||
settings GET,PUT /settings · POST /settings/secrets-token (owner|admin)
|
||||
licence GET /license · POST /license (POST: self-hosted only)
|
||||
org GET,POST /org/users · PUT /org/users/:id/role · DELETE /org/users/:id
|
||||
GET,PUT /org/oidc (owner|admin)
|
||||
providers GET,POST /auth/providers · PUT,DELETE /auth/providers/:id
|
||||
POST /auth/providers/:id/{test,ack-notice} · GET /auth/presets (owner|admin)
|
||||
```
|
||||
|
||||
`GET /license` reports `deployment`, and **`POST /license` answers 409 `cloud_managed` when it is `cloud`**. A cloud instance's licence is written by `admin/internal/inject` straight into the database and never through this endpoint, so the refusal cannot break injection — it only stops a customer pasting over a licence they do not own. `web/` hides the paste form and points at the HQ portal instead, but as with `hq`-managed users, the API is the boundary and the UI is the courtesy.
|
||||
@@ -524,7 +527,7 @@ Paddle is merchant of record; `admin/internal/paddle` is a thin REST client (no
|
||||
|
||||
## MongoDB Collections
|
||||
|
||||
`servers` · `keys` · `assignments` · `orgs` · `users` · `org_oidc` · `settings` · `secrets` · `workflows` · `workflow_steps` · `workflow_runs` · `workflow_log_lines` · `workflow_log_seq` · `monitors` · `incidents` · `monitor_rollups` · `notification_channels` · `console_sessions` · `audit_logs` · `migrations`
|
||||
`servers` · `keys` · `assignments` · `orgs` · `users` · `auth_providers` · `settings` · `secrets` · `workflows` · `workflow_steps` · `workflow_runs` · `workflow_log_lines` · `workflow_log_seq` · `monitors` · `incidents` · `monitor_rollups` · `notification_channels` · `console_sessions` · `audit_logs` · `migrations`
|
||||
|
||||
Every document except `migrations` carries `org_id`. Struct definitions are the source of truth — see `server/internal/models/`.
|
||||
|
||||
@@ -536,6 +539,7 @@ Notes that are not obvious from the structs:
|
||||
- `assignments.revoked_at: null` means active. Revocation is soft, preserving audit history.
|
||||
- `workflow_runs.steps_snapshot` freezes the resolved steps so editing the library never rewrites history.
|
||||
- `console_sessions.token_consumed_at` is set atomically to enforce one-time use.
|
||||
- `auth_providers.provider_id` is a short random identifier, not the Mongo `_id`: it appears in the callback URL a customer pastes into their IdP, and an `_id` there would publish a database key. `callback_notice` marks a provider migrated from the old single-provider shape, whose redirect URI therefore changed.
|
||||
- `workflow_log_lines` is keyed `(run_id, server_id, seq)` — the index is not an optimisation, every read is a range scan over it. `workflow_log_seq` holds one counter document per `run_id/server_id`, which is what lets two pods interleave into one ordered log. Neither carries `instance_id`: they are reached only through a run, and a run is already scoped.
|
||||
- `users.auth_source` is `local`, `oidc` or `hq`. An `hq` user was projected from a Vantage HQ account and carries `hq_user_id`; HQ owns its role, password and existence.
|
||||
|
||||
@@ -550,6 +554,7 @@ Admin's own database is separate and holds `accounts` · `admin_instances` · `l
|
||||
- `0001_default_org_backfill`
|
||||
- `0002_settings_org_backfill` (must run before 0003 — 0003 can create a `default` org, which pushes 0002 into its ambiguous multi-org branch)
|
||||
- `0003_missed_org_scopes`
|
||||
- `0005_auth_providers` — copies each `instance_oidc` document into `auth_providers`, ciphertext verbatim rather than decrypted and re-encrypted, so it does not need `KEY_ENCRYPTION_KEY` and cannot strand an instance's SSO configuration that has none set.
|
||||
|
||||
Index builders (`EnsureAuthIndexes`, `EnsureSettingsIndexes`) are fatal on failure; `EnsureSecretIndexes` and `EnsureWorkflowIndexes` only warn.
|
||||
|
||||
|
||||
@@ -66,9 +66,11 @@ Go to **Settings → Access**. Add members with a role:
|
||||
|
||||
Settings and organisation management require `owner` or `admin`.
|
||||
|
||||
If you would rather not manage passwords, configure OIDC instead see
|
||||
[Settings](../vantage/settings.md#single-sign-on-oidc). OIDC is configured per
|
||||
organisation, and the client secret is stored encrypted.
|
||||
If you would rather not manage passwords, configure single sign-on instead:
|
||||
see [Settings](../vantage/settings.md#single-sign-on). You can add more than
|
||||
one identity provider; each gets its own button on the login page, and no
|
||||
buttons appear at all until at least one provider is configured. Client
|
||||
secrets are stored encrypted.
|
||||
|
||||
## Next
|
||||
|
||||
|
||||
@@ -16,6 +16,16 @@ this instance behaves" produced two half-pages and a nav entry nobody could
|
||||
distinguish from Settings. The old path still redirects.
|
||||
:::
|
||||
|
||||
:::danger Upgrading breaks existing single sign-on until you re-register the callback URL
|
||||
Callback URLs are now per provider instead of one shared URL for the whole
|
||||
instance. If you already had single sign-on configured, it was carried
|
||||
forward automatically, but its callback URL changed and **sign-in through it
|
||||
will fail until you copy the new callback URL from its settings card and
|
||||
register it with your identity provider**. The migrated provider's card shows
|
||||
a dismissable warning as a reminder. Password sign-in is not affected by this
|
||||
change, so an administrator can always sign in locally to make the update.
|
||||
:::
|
||||
|
||||
## Access
|
||||
|
||||
### Members
|
||||
@@ -42,20 +52,46 @@ change would be overwritten by the next sync and would leave two writers for one
|
||||
password hash. Manage them from [People and roles](../hq/people-and-roles.md).
|
||||
:::
|
||||
|
||||
### Single sign-on (OIDC)
|
||||
### Single sign-on
|
||||
|
||||
Configured per organisation:
|
||||
Add as many identity providers as you need: one instance can have several at
|
||||
once, each with its own name, its own button on the login page and its own
|
||||
callback URL.
|
||||
|
||||
| Field | |
|
||||
| ------------- | ---------------------------- |
|
||||
| Issuer | Your provider's issuer URL |
|
||||
| Client ID | |
|
||||
| Client secret | Stored AES-256-GCM encrypted |
|
||||
Pick a provider from the list of presets:
|
||||
|
||||
Sign-in then goes `/auth/oidc/start` → your provider → `/auth/oidc/callback`.
|
||||
| Preset | You provide |
|
||||
| ---------------------- | ------------------------------------------- |
|
||||
| Microsoft Entra ID | Directory (tenant) ID |
|
||||
| Google Workspace | nothing further, the issuer is fixed |
|
||||
| Okta | Your Okta org domain |
|
||||
| GitHub | Client ID and client secret only |
|
||||
| Other (OpenID Connect) | The issuer URL of your identity provider |
|
||||
|
||||
Local and OIDC users coexist. Keep at least one local owner: if the provider is
|
||||
misconfigured or unreachable, a local account is the way back in.
|
||||
Every provider also needs a **Client ID** and **Client secret**; the secret is
|
||||
stored AES-256-GCM encrypted and never shown again after you save it.
|
||||
|
||||
:::info GitHub requires a verified primary email
|
||||
Vantage signs a person in by their email address. GitHub is asked for the
|
||||
account's addresses and only accepts one that is **both** the account's
|
||||
primary address **and** marked verified: an address GitHub has not confirmed
|
||||
is not proof anyone controls it.
|
||||
:::
|
||||
|
||||
#### Callback URL
|
||||
|
||||
Each provider gets its own callback URL, shown on its settings card with a
|
||||
copy button. This is the address you register with the identity provider when
|
||||
you set up the application on their side: each provider is registered
|
||||
separately, even if you have several with the same identity provider.
|
||||
|
||||
#### Turning off password sign-in
|
||||
|
||||
You can disable local (email and password) sign-in once at least one provider
|
||||
is enabled. Vantage refuses to save a change that would leave nobody able to
|
||||
sign in, whether that change comes from the local login toggle or from
|
||||
disabling the last enabled provider. Keep at least one option open until every
|
||||
person who needs access can reach the new one.
|
||||
|
||||
## Monitoring
|
||||
|
||||
|
||||
Reference in New Issue
Block a user