Publish documentation / publish (push) Successful in 1m27s
Add PostgreSQL-backed memory, editable evidence with source review and activation, and human-approved archive repairs across the harness, API, and UI. Include migrations, deployment support, regression coverage, and validation documentation. Refresh permissions from validated session roles so existing administrator logins can access newly deployed archive management features.
121 lines
6.0 KiB
Markdown
121 lines
6.0 KiB
Markdown
# Authentication architecture
|
|
|
|
ThothII has two production authentication modes: `local` and generic `oidc`. The host operator
|
|
surface is one CLI, `tht`; there is no separate authentication executable. The backend owns
|
|
opaque browser sessions and authorization, while `tht` owns protected configuration and local-user
|
|
files.
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
BROWSER["Browser"] --> BOUNDARY["Authentication boundary"]
|
|
BOUNDARY --> LOCAL["Local users\nArgon2id hashes"]
|
|
BOUNDARY --> OIDC["OIDC provider\nAuthorization Code PKCE"]
|
|
OIDC --> GROUPS["Groups claim\nexact mapping"]
|
|
LOCAL --> PRINCIPAL["Thoth principal"]
|
|
GROUPS --> PRINCIPAL
|
|
PRINCIPAL --> ROLES["Roles"]
|
|
ROLES --> PERMISSIONS["Permissions"]
|
|
PERMISSIONS --> ROUTES["Protected routes"]
|
|
SECRETS["Mounted secret bundle"] -.-> BOUNDARY
|
|
```
|
|
|
|
## Configuration and trust boundaries
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
See the [local guide](../install/authentication-local.md), [generic OIDC guide](../install/authentication-oidc.md),
|
|
and [Authentik guide](../install/authentik.md) for operator procedures.
|