160 lines
8.3 KiB
Markdown
160 lines
8.3 KiB
Markdown
# Authentication architecture
|
|
|
|
ThothII supports `local` and generic `oidc` through protected `auth.yaml`, plus
|
|
the trusted-proxy `upstream` path used by Omics. The host operator surface is
|
|
one CLI, `tht`; there is no separate authentication executable. The backend owns
|
|
authorization in all paths and opaque browser sessions only in local/OIDC.
|
|
`tht` owns protected local/OIDC configuration and local-user files; upstream
|
|
identity is supplied per request by the authenticated server proxy.
|
|
|
|
Presentation is separate: [full/embedded rendering](application-shell.md) does
|
|
not select authentication. The Mac uses full/local; Omics uses embedded/upstream;
|
|
a standalone server can use full/OIDC. Do not configure a second ThothII OIDC
|
|
login simply because Omics itself authenticates users through Authentik.
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
BROWSER["Browser"] --> BOUNDARY["Authentication boundary"]
|
|
BOUNDARY --> LOCAL["Local users\nArgon2id hashes"]
|
|
BOUNDARY --> OIDC["OIDC provider\nAuthorization Code PKCE"]
|
|
BOUNDARY --> UPSTREAM["Trusted proxy\nVerified portal session"]
|
|
OIDC --> GROUPS["Groups claim\nexact mapping"]
|
|
LOCAL --> PRINCIPAL["Thoth principal"]
|
|
GROUPS --> PRINCIPAL
|
|
UPSTREAM --> PRINCIPAL
|
|
PRINCIPAL --> ROLES["Roles"]
|
|
ROLES --> PERMISSIONS["Permissions"]
|
|
PERMISSIONS --> ROUTES["Protected routes"]
|
|
SECRETS["Mounted secret bundle"] -.-> BOUNDARY
|
|
```
|
|
|
|
## Configuration and trust boundaries
|
|
|
|
The following protected-file configuration applies to local/OIDC. Upstream uses
|
|
`AUTH_MODE=upstream` without a mounted `auth.yaml` or authentication runtime
|
|
projection. The backend refuses both authorities together. `AUTH_MODE=none`
|
|
and `mock` are development/test modes, not production fallbacks. The exact
|
|
upstream setup, header contract, proxy hops and origin checks are in the
|
|
[server integration guide](../install/authentication-upstream.md).
|
|
|
|
The installation descriptor points to an operator-controlled authentication directory. It contains
|
|
non-secret `auth.yaml` and, for local mode, `users.yaml`. POSIX installations use a private
|
|
directory and owner-only regular files; Windows uses equivalent owner-only ACLs. Secret values are
|
|
read from the mounted secret bundle and are never placed in YAML, command arguments, logs, JSON
|
|
diagnostics, or browser storage.
|
|
|
|
Local users have Argon2id password hashes, stable IDs, enabled state, roles, and `authRevision`.
|
|
The production role expansion from `backend/src/auth/config.ts` is exact:
|
|
|
|
| Role | Permissions |
|
|
|---|---|
|
|
| `user` | `session.use` |
|
|
| `admin` | `session.use`, `session.read_all`, `session.manage_all`, `settings.manage`, `workspace.manage`, `workspace.secrets.manage`, `database.manage`, `memory.manage`, `evidence.manage`, `pi.manage`, `auth.diagnostics.read` |
|
|
|
|
`admin` therefore includes the ordinary `session.use` permission. No other role or permission
|
|
label is part of the production catalog.
|
|
|
|
After validating a browser session, the backend expands its roles through the current permission
|
|
catalog on every request. The permissions saved at login are a historical snapshot, so existing
|
|
administrator sessions can use newly deployed administration features without signing in again.
|
|
Session expiry, revocation and local-user role validation still apply before role expansion.
|
|
|
|
OIDC is provider-neutral at the browser protocol boundary. Authorization Code + PKCE, issuer,
|
|
signature, audience, expiry, state, and nonce are validated before a principal is created.
|
|
Authentik is the first certified group-catalog adapter, not a special browser login mode.
|
|
|
|
## Group authorization
|
|
|
|
This section describes **ThothII's direct OIDC login**, not the embedded Omics
|
|
path. Omics checks its own capability and administrator status and supplies
|
|
normalized identity headers; ThothII does not repeat the OIDC groups exchange.
|
|
|
|
OIDC must return a direct, non-empty `groups` claim whose value is a JSON array of strings.
|
|
Missing, malformed, indirect, or overage-style claims fail closed. The browser callback returns
|
|
HTTP 401 with the generic code `oidc_callback_failed`; it does not expose the internal reason.
|
|
Authentication diagnostics and interactive device-flow validation use
|
|
`oidc_groups_claim_invalid` for invalid group-claim/identity results. Exact configured external
|
|
group names map to Thoth roles and then to permissions. A user with no mapped group is
|
|
authenticated but receives no role and gets `403` from protected routes. Unmapped upstream groups
|
|
are ignored silently, without an error or warning.
|
|
|
|
Every configured mapping is checked by the configured group-catalog adapter. Authentik checks the
|
|
exact group name and reports `oidc_mapped_group_missing` when it cannot find it. The dedicated
|
|
Authentik API service account has group-view-only privilege; it is separate from the OIDC client.
|
|
|
|
## Diagnostics and ordering
|
|
|
|
The closed production diagnostic-code union is:
|
|
|
|
```text
|
|
auth_ready
|
|
auth_config_incomplete
|
|
auth_config_invalid
|
|
auth_session_store_invalid
|
|
local_user_registry_invalid
|
|
local_admin_missing
|
|
oidc_secret_missing
|
|
oidc_discovery_unreachable
|
|
oidc_issuer_mismatch
|
|
oidc_jwks_unreachable
|
|
oidc_group_catalog_unreachable
|
|
oidc_group_catalog_unauthorized
|
|
oidc_mapped_group_missing
|
|
oidc_mapped_group_ambiguous
|
|
oidc_groups_claim_invalid
|
|
oidc_device_flow_unavailable
|
|
```
|
|
|
|
Workspace Validate performs static authentication validation without provider connectivity.
|
|
`tht auth check` performs live, non-interactive diagnosis: static safety plus OIDC discovery,
|
|
issuer/JWKS, group-catalog authentication, and exact configured-group existence. Adding
|
|
`--interactive` runs that same live diagnosis and then validates a device-flow identity when the
|
|
provider supports Device Authorization. Aggregate live workspace and authentication validation is
|
|
available through the installation diagnostics.
|
|
|
|
The ordered `tht doctor` report is exactly: `descriptor`, `files`, `docker`, `compose`,
|
|
`configuration`, `authentication`, `services`, `core-http`, `frontend-http`,
|
|
`workspace-registry`, `workflow`, `pi`. Its `authentication` entry is the live non-interactive
|
|
diagnosis against the healthy running core; later checks may be skipped when an earlier
|
|
prerequisite fails.
|
|
|
|
## Browser sessions
|
|
|
|
This section applies only to **local and direct OIDC**. Upstream reuses the
|
|
portal's authenticated session at the proxy boundary, not a ThothII cookie;
|
|
its `/me` response has `session: null` and `csrfToken: null`.
|
|
|
|
The browser receives only an opaque `HttpOnly`, `SameSite=Lax` cookie named `thothii_session`.
|
|
State-changing cookie requests require the in-memory CSRF token, same-origin `Origin`, and Fetch
|
|
Metadata checks when present. The frontend never stores bearer tokens or session secrets in Web
|
|
Storage.
|
|
|
|
- Ordinary local sessions: 2-hour idle and 12-hour absolute expiry; closing the browser removes
|
|
the non-persistent cookie.
|
|
- **Remember me** local sessions: 7-day idle and 30-day absolute expiry; they survive browser and
|
|
ThothII restarts.
|
|
- OIDC sessions: at most 8 hours and never beyond the validated ID-token expiry.
|
|
|
|
Local password, role, enabled-state, and logout-all changes increment `authRevision` and invalidate
|
|
affected sessions. Authentication configuration revision changes invalidate all sessions after
|
|
reload. Logout deletes the server record. Backup restore excludes active sessions and OIDC state,
|
|
recreates empty private auth-state directories, and therefore forces reauthentication.
|
|
|
|
Full local/OIDC logout calls `POST /auth/logout`, revokes the server session and
|
|
clears its cookie. It does not call the identity provider's global logout. If the
|
|
provider still has an SSO session, the next OIDC login can complete without
|
|
another password prompt. Embedded has no ThothII logout control: use the portal.
|
|
|
|
## Access revalidation
|
|
|
|
The frontend treats `/me` as the access authority. In embedded it does not fetch
|
|
`/auth/config` or offer local/OIDC login. On focus, pageshow, visibility return
|
|
and event-stream reconnection it rechecks access. A 401/403 from this probe clears
|
|
protected state; a 403 on one operation is not automatically an app-wide logout.
|
|
Neither the DOM adapter nor the proxy's initial SSE check guarantees instantaneous
|
|
revocation of streams already open in other tabs.
|
|
|
|
See the [local guide](../install/authentication-local.md), [generic OIDC guide](../install/authentication-oidc.md),
|
|
[Authentik guide](../install/authentik.md), [upstream integration](../install/authentication-upstream.md),
|
|
and [manual acceptance matrix](../testing/authentication-manual-acceptance.md).
|