diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index 977c8d49..f70879e8 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -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 diff --git a/README.md b/README.md index fee46447..83a521de 100644 --- a/README.md +++ b/README.md @@ -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, diff --git a/docs/architecture/authentication.md b/docs/architecture/authentication.md new file mode 100644 index 00000000..0bbea56e --- /dev/null +++ b/docs/architecture/authentication.md @@ -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. diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 0ab59001..3d8c9f6e 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -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 ``` diff --git a/docs/guida-utente.md b/docs/guida-utente.md index db9c6f5e..bf8e744c 100644 --- a/docs/guida-utente.md +++ b/docs/guida-utente.md @@ -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 diff --git a/docs/index.md b/docs/index.md index e6ac4f2b..89682d8b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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). diff --git a/docs/install/authentication-local.md b/docs/install/authentication-local.md new file mode 100644 index 00000000..8e95f1df --- /dev/null +++ b/docs/install/authentication-local.md @@ -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 --admin-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 --role user|admin [--display-name ] [--password-file ] +tht auth user set-password [--password-file ] +tht auth user enable +tht auth user disable +tht auth user grant --role user|admin +tht auth user revoke --role user|admin +tht auth user logout-all --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. diff --git a/docs/install/authentication-oidc.md b/docs/install/authentication-oidc.md new file mode 100644 index 00000000..ac61a228 --- /dev/null +++ b/docs/install/authentication-oidc.md @@ -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 +`/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 \ + --issuer --client-id \ + --authentik-base-url \ + --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: +session: + regularTtlSeconds: 43200 + regularIdleSeconds: 7200 + rememberTtlSeconds: 2592000 + rememberIdleSeconds: 604800 + oidcTtlSeconds: 28800 +oidc: + issuer: + clientId: + clientSecretRef: THT_OIDC_CLIENT_SECRET + scopes: [openid, profile, email] + groupsClaim: groups +groupCatalog: + driver: authentik + baseUrl: + 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`. diff --git a/docs/install/authentik.md b/docs/install/authentik.md new file mode 100644 index 00000000..f75a9195 --- /dev/null +++ b/docs/install/authentik.md @@ -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 + `/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. diff --git a/docs/install/local.md b/docs/install/local.md index 86fdaf77..654a5fb8 100644 --- a/docs/install/local.md +++ b/docs/install/local.md @@ -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 diff --git a/docs/install/psd-workspace-setup.md b/docs/install/psd-workspace-setup.md index a5084dfb..1e86c332 100644 --- a/docs/install/psd-workspace-setup.md +++ b/docs/install/psd-workspace-setup.md @@ -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`). diff --git a/docs/install/reverse-proxy-caddy.md b/docs/install/reverse-proxy-caddy.md index 78690e53..bb90b62c 100644 --- a/docs/install/reverse-proxy-caddy.md +++ b/docs/install/reverse-proxy-caddy.md @@ -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 diff --git a/docs/install/reverse-proxy-nginx.md b/docs/install/reverse-proxy-nginx.md index 849a6fd9..7cb5b70e 100644 --- a/docs/install/reverse-proxy-nginx.md +++ b/docs/install/reverse-proxy-nginx.md @@ -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 diff --git a/docs/install/server.md b/docs/install/server.md index 3f108f4d..1e7f2c41 100644 --- a/docs/install/server.md +++ b/docs/install/server.md @@ -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, diff --git a/docs/testing/authentication-manual-acceptance.md b/docs/testing/authentication-manual-acceptance.md new file mode 100644 index 00000000..aa4269d6 --- /dev/null +++ b/docs/testing/authentication-manual-acceptance.md @@ -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. diff --git a/mkdocs.yml b/mkdocs.yml index 1e3b3001..2f24d3d0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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 diff --git a/scripts/auth-docs-smoke.sh b/scripts/auth-docs-smoke.sh new file mode 100755 index 00000000..2e4e2330 --- /dev/null +++ b/scripts/auth-docs-smoke.sh @@ -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"