docs(auth): document local OIDC and Authentik operation

This commit is contained in:
2026-08-18 03:07:33 +02:00
parent 6ec5b76c54
commit f4f38717e1
17 changed files with 407 additions and 0 deletions
+14
View File
@@ -10,6 +10,20 @@
Last updated: 2026-08-13 (P2–P6 accepted; P7 live preprocessing PASS on PSD).
> Point a fresh session here ("read PROJECT_STATE.md") before substantial work.
### Task 14 authentication documentation — implementation status (2026-08-18)
- Documentation now describes local Argon2id users, ordinary and remembered session expiry,
revision invalidation, generic OIDC direct groups claims, exact group-role mapping, Authentik
group-view-only catalog checks, tht auth/tht doctor ordering, diagnostics, CSRF, and restore
reauthentication.
- Task 14 documentation smoke and strict documentation build are release evidence for the docs
scope only. Browser OIDC callback E2E, native Windows behavioral execution, PSD/manual test
identities, and external L2 remain pending Task 15/release gates.
- Task 13 has two parked restore-lock preconditions that remain mandatory before certification:
lock before target-dependent preflight with archive bytes/hashes staged and revalidated inside the
lock immediately before extraction; and an opaque installation-bound transaction capability or
closure replacing convention-only lock-held helpers.
### P3 effective configuration and `.tht-dwh` — implementation complete, automated PASS, manual PASS (2026-08-13)
- **Scope:** P3 (PRD D3): a versioned shared canonicalizer produces the non-secret effective
+3
View File
@@ -4,6 +4,9 @@ ThothII is a human-reviewed NL-to-SQL workflow with a React frontend and a Fasti
core. The portable deployment runs exactly two application services; data services remain
external in this profile, except for the mandatory internal semantic services bundled in Compose.
Authentication is configured through the single host CLI tht: see the [local authentication guide](docs/install/authentication-local.md),
[generic OIDC guide](docs/install/authentication-oidc.md), and [manual acceptance matrix](docs/testing/authentication-manual-acceptance.md).
## Docker Compose: local startup
Requirements: Docker Engine with Compose v2. The mandatory stack is `frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`. DWH and LLM remain external,
+55
View File
@@ -0,0 +1,55 @@
# 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.
## 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 stable roles are `user` and `admin`; `admin` inherits `user` and adds installation, session,
Pi, workspace, secret-management, and diagnostic permissions.
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 with
`oidc_groups_claim_invalid`. 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.
## 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.
+3
View File
@@ -4,6 +4,9 @@
ThothII è un **datamart builder human-in-the-loop**: trasforma una domanda in linguaggio naturale in SQL validato (ed eventualmente un datamart dbt) attraverso un **workflow deterministico a 8 fasi NL→SQL**, in cui il modello *propone* e un revisore umano *decide* ai gate.
L'autenticazione di produzione usa local oppure OIDC generico; il solo CLI operatore è tht.
Per sessioni, ruoli, gruppi, diagnostica e ripristino vedere la [documentazione autenticazione](authentication.md).
## I tre progetti indipendenti
```
+3
View File
@@ -1,5 +1,8 @@
# ThothII — Guida utente
Per login, **Remember me**, ruoli, invalidazione delle sessioni, gruppi OIDC e ripristino, vedere
la [guida autenticazione locale](install/authentication-local.md) e la [guida OIDC generica](install/authentication-oidc.md).
Questa guida accompagna passo-passo chi deve **preparare** il repository dei workspace, **usare
gli strumenti** ThothII per quel repository e **usare l'applicazione** per fare domande in
linguaggio naturale e ottenere SQL validato. Usa parole semplici ed esempi; i dettagli tecnici
+2
View File
@@ -8,6 +8,8 @@ La documentazione è divisa in due aree:
Come funziona il sistema: architettura, specifiche di design delle singole funzionalità, piani di implementazione, report di test. Parte da qui: [Panoramica dell'architettura](architecture/overview.md).
Per autenticazione locale, OIDC generico, Authentik e accettazione PSD: [documentazione autenticazione](architecture/authentication.md).
Per installare l'applicazione in Docker nei quattro contesti operativi, usando il file env,
`compose.yaml`, l'overlay locale/server e il bundle di secret montato:
[Installazione Docker nei quattro contesti](installazione-docker-4-contesti.md).
+70
View File
@@ -0,0 +1,70 @@
# Local authentication
Use local mode for a standalone PC or Mac. Configure it through `tht`; passwords are entered at an
echo-free prompt or read from a protected `--password-file`, never from a command argument.
## Bootstrap
After the installation descriptor and protected secret bundle exist, configure the first enabled
administrator:
```sh
tht --installation /absolute/path/thothii-installation.yaml auth configure \
--mode local --public-url http://127.0.0.1:8080 \
--admin-user <operator-user> --admin-display-name <display-name> \
--password-file /absolute/path/protected-password-file
```
The password file is temporary operator input: keep it private and remove it after configuration.
The resulting `users.yaml` contains Argon2id hashes, never plaintext passwords. To use prompts,
omit the admin and password options in an interactive terminal. `tht setup` performs the same
bootstrap before it starts the stack.
The non-secret local `auth.yaml` has this exact shape:
~~~yaml
version: 1
mode: local
publicUrl: http://127.0.0.1:8080
session:
regularTtlSeconds: 43200
regularIdleSeconds: 7200
rememberTtlSeconds: 2592000
rememberIdleSeconds: 604800
oidcTtlSeconds: 28800
local:
usersFile: users.yaml
~~~
## User administration
```sh
tht auth user list [--json]
tht auth user add <username> --role user|admin [--display-name <name>] [--password-file <file>]
tht auth user set-password <username> [--password-file <file>]
tht auth user enable <username>
tht auth user disable <username>
tht auth user grant <username> --role user|admin
tht auth user revoke <username> --role user|admin
tht auth user logout-all <username> --yes
```
User commands are unavailable in OIDC mode. The last enabled administrator cannot be disabled or
demoted. Every password, role, enabled-state, and `logout-all` change increments the user’s
`authRevision`, invalidating its sessions. `tht auth status --json` is redacted and suitable for
machine use; JSON output is pristine on stdout.
## Session behavior and recovery
An ordinary login expires after 2 hours idle or 12 hours absolute. Selecting **Remember me** makes
the cookie persistent and changes the limits to 7 days idle or 30 days absolute. Remembered
sessions survive a browser and backend restart, but not a user revision change, configuration
revision change, logout, or restore. Restore does not include sessions or OIDC state and requires
every user to authenticate again.
If access is lost, use `tht auth user set-password`, `enable`, role changes, or `logout-all` as
appropriate, then log in again. Do not copy passwords, hashes, cookies, CSRF values, or secret
values into tickets, logs, or evidence.
Check readiness with `tht auth check`; add `--json` for the machine contract. Use
`tht doctor --json` for the aggregate installation report.
+83
View File
@@ -0,0 +1,83 @@
# Generic OIDC authentication
OIDC mode supports a standards-based provider. The browser flow is generic: Authorization Code,
PKCE S256, state, nonce, issuer/signature/audience/expiry validation, and the fixed callback
`<publicUrl>/api/auth/oidc/callback`. The browser and API must use the same origin; configure the
reverse proxy to preserve that public origin and callback path.
Configure the installation with `tht`:
```sh
tht auth configure --mode oidc --public-url <https-public-origin> \
--issuer <https-issuer> --client-id <client-id> \
--authentik-base-url <https-provider-origin> \
--user-group 'TOT Users' --admin-group 'TOT Admin'
```
The OIDC client secret is supplied through the protected secret bundle under the exact key
`THT_OIDC_CLIENT_SECRET`; it is never written into `auth.yaml`. The default scopes are exactly
`openid`, `profile`, and `email`.
The non-secret OIDC configuration has this exact shape (replace angle-bracket placeholders with
operator values):
~~~yaml
version: 1
mode: oidc
publicUrl: <https-public-origin>
session:
regularTtlSeconds: 43200
regularIdleSeconds: 7200
rememberTtlSeconds: 2592000
rememberIdleSeconds: 604800
oidcTtlSeconds: 28800
oidc:
issuer: <https-issuer>
clientId: <client-id>
clientSecretRef: THT_OIDC_CLIENT_SECRET
scopes: [openid, profile, email]
groupsClaim: groups
groupCatalog:
driver: authentik
baseUrl: <https-provider-origin>
apiTokenRef: THT_AUTHENTIK_API_TOKEN
authorization:
groupRoles:
TOT Users: [user]
TOT Admin: [admin]
~~~
## The groups claim is mandatory
The ID token must contain a direct, non-empty `groups` array of strings. ThothII does not follow
distributed claims, overage links, or indirect provider expansion. Missing or malformed claims fail
closed with `oidc_groups_claim_invalid`.
Configured group names are exact and case-sensitive. The union of matched mappings determines the
Thoth roles. A token with no mapped group is authenticated but has no role and cannot use protected
routes. Additional provider groups are ignored silently, without an error or warning. Group
existence is separately proven by the configured catalog adapter; this is why a generic OIDC
provider may authenticate while still failing installation readiness.
## Checks and diagnostics
Run the static check first:
```sh
tht auth check
tht auth check --json
```
For a provider that advertises Device Authorization, `tht auth check --interactive` presents a
verification URI and one-time user code on the terminal, waits for completion, and validates a
real ID token including `groups`. It is an operator check, not a replacement for browser login.
Static checks cover configuration, secret references, group mappings, file safety, and session
storage. Live checks then cover discovery, issuer/JWKS, provider access, and configured groups.
`tht doctor` runs the authentication check after configuration and before service/workspace checks.
Workspace validation includes static authentication readiness; workspace Test adds live OIDC and
group-catalog checks. Any authentication failure makes the workspace non-activatable.
The redacted diagnostic codes include `oidc_secret_missing`, `oidc_discovery_unreachable`,
`oidc_issuer_mismatch`, `oidc_jwks_unreachable`, `oidc_groups_claim_invalid`,
`oidc_mapped_group_missing`, and `oidc_mapped_group_ambiguous`.
+34
View File
@@ -0,0 +1,34 @@
# Authentik provider setup
Authentik is the first certified provider for PSD acceptance. The ThothII browser protocol remains
generic OIDC; these steps configure the provider-specific group catalog only.
1. Create an OAuth2/OIDC application and provider in Authentik. Register exactly
`<publicUrl>/api/auth/oidc/callback` as the callback and enable `openid`, `profile`, and `email`.
2. Configure the provider so the ID token contains a direct `groups` array of strings. Verify the
claim with a disposable test identity before running acceptance.
3. Create a dedicated API service account for the group catalog. Grant group-view-only privilege;
do not grant write, user-management, or directory-administration privilege. Put its bearer value
in the protected bundle under `THT_AUTHENTIK_API_TOKEN`.
4. Create or confirm the exact groups `TOT Users` and `TOT Admin`. Map them explicitly in
`auth.yaml` to `user` and `admin`, respectively. Keep other upstream groups out of the mapping.
5. Run the static check and then the live interactive check:
```sh
tht auth check
tht auth check --interactive
tht doctor --json
```
6. Run workspace Validate and then workspace Test. Test must prove discovery/JWKS, catalog access,
and every configured group. The diagnostic result must contain no secret values.
Only configured exact group names are queried. Additional Authentik or directory groups are ignored
silently, without a warning. A mapped group absent from Authentik fails closed with
`oidc_mapped_group_missing`; an ambiguous exact-name result uses
`oidc_mapped_group_ambiguous`. A group visible only in an upstream directory but not represented
in Authentik is missing from ThothII’s catalog and must not be treated as present.
Rotate the two credentials independently through the protected secret-file procedure, then repeat
`tht auth check` and workspace Test. Never put either value in this guide, YAML, shell history,
diagnostic output, or acceptance evidence.
+11
View File
@@ -169,6 +169,17 @@ services; retain TLS and authentication even when co-located.
## Build ThothII and thothctl
The canonical local Compose smoke uses the base file plus the local profile. Keep this exact
base+profile command available for install verification:
~~~sh
docker compose --env-file deploy/env/local.env + -f compose.yaml -f deploy/compose.local.yaml up --build -d
~~~
After the stack is ready, configure and check authentication with the single host CLI tht; see
the [local authentication guide](authentication-local.md). Authentication configuration is
installation-global and is checked before workspace tests.
From the repository root, macOS/Linux/WSL2 users run:
```sh
+7
View File
@@ -1,5 +1,12 @@
# Policlinico San Donato — setup workspace (nuova gestione)
Authentication acceptance is documented in the [manual authentication matrix](../testing/authentication-manual-acceptance.md).
Use generic OIDC with Authentik as the certified group catalog, map only the exact TOT Users and
TOT Admin groups, then run tht auth check, tht auth check --interactive, workspace Validate,
and workspace Test in that order. Browser callback E2E, native Windows execution, approved PSD
manual identities, external L2, and the two parked restore-lock preconditions remain pending the
Task 15/release gates.
Guida operativa per collegare ThothII al DWH di PSD con il nuovo sistema (registry Git + descriptor
v3 + `thothctl`).
+4
View File
@@ -4,6 +4,10 @@ This example assumes Caddy runs on the Linux host, ThothII `frontend` listens on
`127.0.0.1:8080`, public DNS points to the host, and a separate authentication gateway validates
the user's real login/session. Replace the domain and auth-gateway address.
For ThothII-managed generic OIDC, preserve the configured public origin and the exact callback
`/api/auth/oidc/callback`; the browser and API remain one same-origin surface. Run
`tht auth check` and workspace Test after proxy changes.
## Trust boundary
Caddy is the only public listener. It provides automatic HTTPS, performs `forward_auth`, and
+4
View File
@@ -4,6 +4,10 @@ This example assumes Nginx runs on the Linux host, ThothII `frontend` listens on
`127.0.0.1:8080`, and a separate authentication gateway validates the user's real login/session.
Replace the documentation domain, certificate paths, and auth-gateway address.
For ThothII-managed generic OIDC, preserve the configured public origin and the exact callback
`/api/auth/oidc/callback`; the browser and API remain one same-origin surface. Run
`tht auth check` and workspace Test after proxy changes.
## Trust boundary
Nginx is the only public listener. It terminates TLS, performs an `auth_request`, and proxies only
+5
View File
@@ -1,5 +1,10 @@
# Install ThothII on a Linux server
Server authentication uses generic OIDC with the reverse proxy preserving the configured public
origin and callback path. Follow the [OIDC guide](authentication-oidc.md), [Authentik guide](authentik.md)
when applicable, and the [authentication acceptance matrix](../testing/authentication-manual-acceptance.md).
The host authentication CLI is tht; use tht auth check before workspace Validate/Test.
This guide is for an installer with basic Linux administration and very basic Docker knowledge.
It deploys the same Compose distribution used on a local PC: the mandatory application is exactly
`frontend` plus `core`, and pinned Pi is inside `core`. The server does not need host Pi, Node.js,
@@ -0,0 +1,46 @@
# Authentication manual acceptance
This is a release-gate checklist, not evidence. Use one ordinary PSD test identity and one admin
PSD test identity supplied through the approved test-identity process. Record only sanitized
pass/fail results, timestamps, build identity, and diagnostic codes. Do not record names, internal
URLs, directory/LDAP details, tokens, passwords, hashes, cookies, or realistic secret examples.
## Preconditions and ordering
1. Resolve the two parked Task 13 restore preconditions before certification: acquire the lifecycle
lock before any target-dependent preflight and stage/revalidate archive bytes and hashes inside
that lock immediately before extraction; replace convention-only
`createWithDependenciesLockHeld` with an opaque installation-bound transaction capability or
closure so lock-held primitives cannot be called without the capability.
2. Run `tht auth check`, then `tht auth check --interactive` where Device Authorization is
available, then workspace Validate and workspace Test. Run `tht doctor --json` and confirm its
`authentication` check precedes service and workspace checks.
3. Confirm the exact direct `groups` claim for both identities and the mappings `TOT Users → user`
and `TOT Admin → admin`. Confirm extra upstream groups are ignored without warning.
## Matrix
| Scenario | Expected result |
|---|---|
| Ordinary identity opens its own application/session routes | Allowed; admin-only routes return `403`. |
| Admin identity opens admin routes | Allowed according to the `admin` permission set. |
| Token omits `groups` | Authentication fails closed with `oidc_groups_claim_invalid`. |
| Token has malformed, indirect, or overage groups | Authentication fails closed with `oidc_groups_claim_invalid`. |
| Token has no mapped group | Principal has no role; protected routes return `403`; no warning is emitted. |
| A configured group is absent from Authentik | Check fails with `oidc_mapped_group_missing`. |
| Catalog token is wrong or lacks group-view-only access | Check fails redacted as catalog unavailable/unauthorized. |
| Mapped group is renamed | The next check fails closed until configuration and provider agree. |
| Token adds an unrelated group | Login and authorization are unchanged; no warning is emitted. |
| Backend restarts with Remember me | Remembered local session survives within its TTL. |
| Password/role/enable revision changes | Affected local sessions are rejected and reauthentication is required. |
| CSRF or cross-origin mutation is attempted | Request is rejected. |
| Logout | Cookie expires and the server session is deleted. |
| Provider outage | Live check and OIDC login fail closed; no credential is exposed. |
| Restore is completed | Sessions and OIDC state are absent; all users must reauthenticate. |
## Status at Task 14
Documentation and deterministic contract checks are the Task 14 scope. Browser OIDC callback E2E,
native Windows behavioral execution, the two parked restore-lock preconditions above, PSD/manual
identities, and any external L2 execution remain pending Task 15/release gates. Do not mark this
matrix PASS until those gates have actual retained evidence.
+6
View File
@@ -48,9 +48,15 @@ markdown_extensions:
nav:
- Home: index.md
- Guida utente: guida-utente.md
- Autenticazione: architecture/authentication.md
- Accettazione autenticazione: testing/authentication-manual-acceptance.md
- Setup Policlinico San Donato: install/psd-workspace-setup.md
- ThothII (Documentazione Tecnica):
- Panoramica Architettura: architecture/overview.md
- Autenticazione: architecture/authentication.md
- Installazione autenticazione locale: install/authentication-local.md
- OIDC generico: install/authentication-oidc.md
- Authentik: install/authentik.md
- Installazione Docker (4 contesti): installazione-docker-4-contesti.md
- Gestione delle memory: gestione-memory.md
- Skill operative: skills.md
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -euo pipefail
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
docs=(
"$root/docs/architecture/authentication.md"
"$root/docs/install/authentication-local.md"
"$root/docs/install/authentication-oidc.md"
"$root/docs/install/authentik.md"
"$root/docs/testing/authentication-manual-acceptance.md"
"$root/docs/architecture/overview.md"
"$root/docs/install/local.md"
"$root/docs/install/server.md"
"$root/docs/install/psd-workspace-setup.md"
"$root/docs/install/reverse-proxy-caddy.md"
"$root/docs/install/reverse-proxy-nginx.md"
"$root/docs/guida-utente.md"
"$root/docs/index.md"
"$root/README.md"
"$root/PROJECT_STATE.md"
)
for path in "${docs[@]}"; do
[[ -f "$path" ]] || { echo "auth docs smoke: missing $path" >&2; exit 1; }
done
corpus=$(mktemp)
trap 'rm -f "$corpus"' EXIT
cat "${docs[@]}" >"$corpus"
required=(
"tht auth"
"groups"
"TOT Admin"
"THT_OIDC_CLIENT_SECRET"
"THT_AUTHENTIK_API_TOKEN"
"Remember me"
"oidc_mapped_group_missing"
)
for term in "${required[@]}"; do
rg -Fq "$term" "$corpus" || { echo "auth docs smoke: missing required term: $term" >&2; exit 1; }
done
if rg -n -i 'thothii-admin|thothctl[[:space:]]+auth' "$corpus"; then
echo "auth docs smoke: forbidden authentication CLI wording" >&2
exit 1
fi
if rg -n -i 'password[[:space:]]*[:=][[:space:]]*(?!<|YOUR|REPLACE|CHANGE|FILE|PROMPT)' --pcre2 "$corpus"; then
echo "auth docs smoke: plaintext password example" >&2
exit 1
fi
if rg -n -i 'unmapped[^.]{0,100}(generate|produce|emit|cause)[^.]{0,100}(warning|warn)|unmapped[^.]{0,100}warning(s)?[[:space:]]+(are|is)[[:space:]]+emitted' "$corpus"; then
echo "auth docs smoke: misleading warning claim for unmapped groups" >&2
exit 1
fi
echo "auth docs smoke: required terms and forbidden wording checks passed"