116 lines
5.6 KiB
Markdown
116 lines
5.6 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`, `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.
|
|
|
|
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.
|