docs(auth): address authentication guide review
This commit is contained in:
@@ -14,8 +14,15 @@ read from the mounted secret bundle and are never placed in YAML, command argume
|
||||
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.
|
||||
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.
|
||||
@@ -24,15 +31,54 @@ Authentik is the first certified group-catalog adapter, not a special browser lo
|
||||
## 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.
|
||||
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. Workspace Test is the aggregate live workspace and
|
||||
authentication validation.
|
||||
|
||||
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`.
|
||||
|
||||
@@ -51,7 +51,10 @@ authorization:
|
||||
|
||||
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`.
|
||||
closed. A browser callback exposes only HTTP 401 `oidc_callback_failed`; it never reveals whether
|
||||
the claim was missing, malformed, indirect, or overage-style. The redacted diagnostic and
|
||||
interactive device-flow contract uses `oidc_groups_claim_invalid` for invalid group-claim or
|
||||
device-flow identity results.
|
||||
|
||||
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
|
||||
@@ -61,7 +64,16 @@ provider may authenticate while still failing installation readiness.
|
||||
|
||||
## Checks and diagnostics
|
||||
|
||||
Run the static check first:
|
||||
The surfaces have distinct semantics and this order is recommended:
|
||||
|
||||
1. Workspace Validate performs static authentication validation without contacting the provider.
|
||||
2. `tht auth check` performs live, non-interactive authentication diagnosis, including discovery,
|
||||
issuer/JWKS, catalog credentials, and all configured mapped groups.
|
||||
3. `tht auth check --interactive` repeats live diagnosis and additionally validates a device-flow
|
||||
identity and its direct `groups` claim when Device Authorization is available.
|
||||
4. Workspace Test performs aggregate live workspace and authentication validation.
|
||||
|
||||
The live CLI forms are:
|
||||
|
||||
```sh
|
||||
tht auth check
|
||||
@@ -72,12 +84,11 @@ For a provider that advertises Device Authorization, `tht auth check --interacti
|
||||
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.
|
||||
`tht doctor` emits this exact ordered report: `descriptor`, `files`, `docker`, `compose`,
|
||||
`configuration`, `authentication`, `services`, `core-http`, `frontend-http`,
|
||||
`workspace-registry`, `workflow`, `pi`. Its authentication entry is live and non-interactive.
|
||||
Any authentication failure makes Workspace Validate or Workspace Test non-activatable according
|
||||
to that surface's static or live scope.
|
||||
|
||||
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`.
|
||||
The complete closed diagnostic-code union and exact role-to-permission expansion are in the
|
||||
[authentication architecture](../architecture/authentication.md).
|
||||
|
||||
@@ -12,7 +12,8 @@ generic OIDC; these steps configure the provider-specific group catalog only.
|
||||
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:
|
||||
5. Run Workspace Validate for static authentication validation. Then run live non-interactive
|
||||
diagnosis, followed by the optional device-flow identity check:
|
||||
|
||||
```sh
|
||||
tht auth check
|
||||
@@ -20,8 +21,10 @@ generic OIDC; these steps configure the provider-specific group catalog only.
|
||||
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.
|
||||
6. Run Workspace Test for aggregate live workspace and authentication validation. It must prove
|
||||
discovery/JWKS, catalog access, and every configured group. The diagnostic result must contain
|
||||
no secret values. `tht doctor --json` reports `authentication` after `configuration` and before
|
||||
`services` in its exact ordered checklist.
|
||||
|
||||
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
|
||||
|
||||
@@ -173,7 +173,7 @@ The canonical local Compose smoke uses the base file plus the local profile. Kee
|
||||
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
|
||||
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
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
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
|
||||
TOT Admin groups, then run Workspace Validate, `tht auth check`, `tht auth check --interactive`,
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,37 +1,50 @@
|
||||
# Put ThothII behind Caddy
|
||||
|
||||
This example assumes Caddy runs on the Linux host, ThothII `frontend` listens only 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.
|
||||
Choose exactly one authentication mode. Direct ThothII-managed OIDC and deprecated upstream
|
||||
authentication are mutually exclusive proxy contracts; never combine their directives.
|
||||
|
||||
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.
|
||||
## Direct ThothII-managed OIDC
|
||||
|
||||
## Trust boundary
|
||||
Use this mode when `auth.yaml` has `mode: oidc`. Caddy terminates TLS and proxies every request to
|
||||
`frontend`; ThothII performs login, callback validation, session creation, and authorization.
|
||||
Caddy must not apply `forward_auth` or another external authentication gateway.
|
||||
|
||||
Caddy is the only public listener. It provides automatic HTTPS, performs `forward_auth`, and
|
||||
proxies only to `frontend`; frontend then sends same-origin `/api` requests to private `core`.
|
||||
Never send the public reverse proxy to core port 8787.
|
||||
|
||||
The authentication gateway must return 2xx only after validating a real credential or session.
|
||||
The ordered `route` below deletes every public/private Thoth identity request header before auth.
|
||||
Only on auth success does `copy_headers` rename normalized response claims into the private
|
||||
`X-Thoth-Trusted-*` headers consumed by frontend.
|
||||
Forwarding identity headers alone does not authenticate a user.
|
||||
|
||||
Do not replace the authentication gateway with static `header_up` values, a network allowlist, or
|
||||
browser-provided identity. The admin claim must come from reviewed identity-provider authorization.
|
||||
|
||||
## Example configuration
|
||||
|
||||
Save a reviewed site block in the Caddyfile. Caddy obtains and renews TLS certificates for the real
|
||||
DNS name; use the organization's approved ACME issuer or certificate policy.
|
||||
The public `/api/auth/oidc/login` and `/api/auth/oidc/callback` paths pass unchanged through the
|
||||
same proxy as the rest of `/api`. The configured `publicUrl` must match the browser origin.
|
||||
|
||||
```caddyfile
|
||||
thoth.example.com {
|
||||
thoth.example.invalid {
|
||||
reverse_proxy 127.0.0.1:8080 {
|
||||
# No URI rewrite: OIDC login and callback paths reach frontend unchanged.
|
||||
flush_interval -1
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Proto https
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/thoth-access.log
|
||||
format json
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After reload, run Workspace Validate for static validation, `tht auth check` for live,
|
||||
non-interactive authentication diagnosis, and then Workspace Test for aggregate live validation.
|
||||
|
||||
## Deprecated upstream migration mode
|
||||
|
||||
Use this section only while the installation explicitly uses deprecated `upstream` mode. Do not
|
||||
use it with `mode: oidc` or `mode: local`. Here an external authentication gateway owns login and
|
||||
Caddy applies `forward_auth` before forwarding normalized private identity headers to `frontend`.
|
||||
|
||||
Forwarding identity headers alone does not authenticate a user. The authentication gateway returns
|
||||
2xx only after validating its own credential or session. Clear browser-supplied public and trusted
|
||||
headers before the subrequest, and map identity only from the successful auth response.
|
||||
|
||||
```caddyfile
|
||||
thoth.example.invalid {
|
||||
route {
|
||||
# Remove untrusted browser claims before the auth subrequest.
|
||||
request_header -X-Authenticated-User
|
||||
request_header -X-Thoth-Principal-Issuer
|
||||
request_header -X-Thoth-Principal-Subject
|
||||
@@ -53,27 +66,23 @@ thoth.example.com {
|
||||
}
|
||||
|
||||
reverse_proxy 127.0.0.1:8080 {
|
||||
# Disable response batching so SSE reaches the browser immediately.
|
||||
flush_interval -1
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/thoth-access.log
|
||||
format json
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Configure log processing to remove cookies, authorization data, query strings, and identity
|
||||
headers. Keep Caddy's private keys and state outside the ThothII source/operator directories.
|
||||
## Trust boundary
|
||||
|
||||
Caddy is the only public listener and proxies only to loopback `frontend`, never directly to
|
||||
`core`. Configure access logs to omit cookies, authorization data, query strings, and identity
|
||||
headers. Keep Caddy keys and state outside ThothII source and operator directories.
|
||||
|
||||
## Validate and reload
|
||||
|
||||
Keep the public firewall rule closed while validating. Confirm ThothII responds only on loopback,
|
||||
format a review copy if desired, validate the active file, then reload through the service manager:
|
||||
Keep the public firewall closed while validating:
|
||||
|
||||
```sh
|
||||
curl --fail http://127.0.0.1:8080/health
|
||||
@@ -81,21 +90,9 @@ caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
sudo systemctl reload caddy
|
||||
```
|
||||
|
||||
Do not remove `forward_auth` if validation fails. Correct the Caddy version, adapter syntax,
|
||||
authentication upstream, DNS, or certificate policy instead.
|
||||
|
||||
## Test authentication and SSE
|
||||
|
||||
Open the firewall only after all of these pass:
|
||||
|
||||
1. An unauthenticated HTTPS request is redirected to login or returns 401/403.
|
||||
2. Supplying forged `X-Thoth-Principal-*`, `X-Thoth-Is-Admin`, or `X-Thoth-Trusted-*` request
|
||||
headers does not grant access.
|
||||
3. A real authenticated non-admin can use the application but cannot open Pi Management.
|
||||
4. A real authenticated admin can use Pi Management.
|
||||
5. A browser session receives live model updates without batching; a protected
|
||||
`curl --no-buffer` request using a real short-lived login cookie is also acceptable. Delete the
|
||||
cookie jar immediately afterward.
|
||||
|
||||
Re-run these checks after changing the identity provider, authentication gateway, Caddy, or
|
||||
ThothII release.
|
||||
For direct OIDC, verify the login path redirects to the configured provider, the callback reaches
|
||||
ThothII unchanged, forged identity headers grant nothing, and SSE is unbuffered. For deprecated
|
||||
upstream mode, additionally verify the external gateway rejects unauthenticated traffic and only
|
||||
its 2xx response can create trusted identity headers.
|
||||
|
||||
@@ -1,44 +1,66 @@
|
||||
# Put ThothII behind Nginx
|
||||
|
||||
This example assumes Nginx runs on the Linux host, ThothII `frontend` listens only 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.
|
||||
Choose exactly one authentication mode. Direct ThothII-managed OIDC and deprecated upstream
|
||||
authentication are mutually exclusive proxy contracts; never combine their locations or headers.
|
||||
|
||||
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.
|
||||
## Direct ThothII-managed OIDC
|
||||
|
||||
## Trust boundary
|
||||
Use this mode when `auth.yaml` has `mode: oidc`. Nginx terminates TLS and proxies every request
|
||||
to loopback `frontend`. ThothII owns OIDC login, callback validation, browser sessions, and
|
||||
authorization. No external `auth_request` or authentication gateway belongs in this server.
|
||||
|
||||
Nginx is the only public listener. It terminates TLS, performs an `auth_request`, and proxies only
|
||||
to `frontend`; frontend then uses its private same-origin `/api` route to reach `core`. Never proxy
|
||||
the public listener directly to core port 8787.
|
||||
|
||||
The authentication gateway must return 2xx only after validating a real credential or session. It
|
||||
returns normalized `X-Thoth-Principal-*` and `X-Thoth-Is-Admin` response headers. Nginx clears all
|
||||
client-supplied public and private-hop identity headers and copies only those successful auth
|
||||
response values into `X-Thoth-Trusted-*` on the private hop to frontend.
|
||||
Forwarding identity headers alone does not authenticate a user.
|
||||
|
||||
Do not substitute a static header, network allowlist, or client-provided header for the
|
||||
authentication gateway. The admin value must come from reviewed identity-provider authorization,
|
||||
not from a username supplied by the browser.
|
||||
|
||||
## Example configuration
|
||||
|
||||
Install an Nginx build that includes `ngx_http_auth_request_module`. Save a reviewed version of
|
||||
this server block under the host's Nginx configuration directory:
|
||||
The `location /` block below has a `proxy_pass` without a replacement URI, so public
|
||||
`/api/auth/oidc/login` and `/api/auth/oidc/callback` are forwarded unchanged. The configured
|
||||
`publicUrl` must match the browser origin.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name thoth.example.com;
|
||||
server_name thoth.example.invalid;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name thoth.example.com;
|
||||
server_name thoth.example.invalid;
|
||||
|
||||
ssl_certificate /etc/nginx/tls/thoth/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/tls/thoth/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
location / {
|
||||
# No auth_request and no URI rewrite: ThothII receives OIDC paths unchanged.
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 3600s;
|
||||
add_header X-Accel-Buffering no always;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After reload, run Workspace Validate for static validation, `tht auth check` for live,
|
||||
non-interactive authentication diagnosis, and then Workspace Test for aggregate live validation.
|
||||
|
||||
## Deprecated upstream migration mode
|
||||
|
||||
Use this section only while the installation explicitly uses deprecated `upstream` mode. Do not
|
||||
use it with `mode: oidc` or `mode: local`. In this mode an external authentication gateway owns
|
||||
login, and Nginx applies `auth_request` before forwarding normalized private identity headers.
|
||||
|
||||
Forwarding identity headers alone does not authenticate a user. The authentication gateway returns
|
||||
2xx only after validating its own credential or session.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name thoth.example.invalid;
|
||||
|
||||
ssl_certificate /etc/nginx/tls/thoth/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/tls/thoth/privkey.pem;
|
||||
@@ -51,8 +73,6 @@ server {
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
|
||||
# The auth service derives identity from the real login/session, never these headers.
|
||||
proxy_set_header X-Authenticated-User "";
|
||||
proxy_set_header X-Thoth-Principal-Issuer "";
|
||||
proxy_set_header X-Thoth-Principal-Subject "";
|
||||
@@ -75,7 +95,6 @@ server {
|
||||
auth_request_set $thoth_is_admin
|
||||
$upstream_http_x_thoth_is_admin;
|
||||
|
||||
# Discard browser claims. Carry only successful auth-subrequest values to frontend.
|
||||
proxy_set_header X-Authenticated-User "";
|
||||
proxy_set_header X-Thoth-Principal-Issuer "";
|
||||
proxy_set_header X-Thoth-Principal-Subject "";
|
||||
@@ -85,7 +104,6 @@ server {
|
||||
proxy_set_header X-Thoth-Trusted-Principal-Subject $thoth_principal_subject;
|
||||
proxy_set_header X-Thoth-Trusted-Principal-Display-Name $thoth_principal_display_name;
|
||||
proxy_set_header X-Thoth-Trusted-Is-Admin $thoth_is_admin;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
@@ -93,8 +111,6 @@ server {
|
||||
proxy_set_header Connection "";
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# SSE must reach the browser without response buffering or cache delay.
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 3600s;
|
||||
@@ -103,13 +119,15 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
Keep the certificate private key outside the ThothII tree. Do not log cookies, authorization
|
||||
headers, auth response bodies, or trusted identity headers.
|
||||
## Trust boundary
|
||||
|
||||
Nginx is the only public listener and proxies only to loopback `frontend`, never directly to
|
||||
`core`. Keep private keys outside the ThothII tree. Do not log cookies, authorization headers,
|
||||
OIDC callback query values, authentication bodies, or trusted identity headers.
|
||||
|
||||
## Validate and reload
|
||||
|
||||
First keep the public firewall rule closed. Confirm ThothII responds only on loopback, validate the
|
||||
full Nginx configuration, then reload without stopping existing connections:
|
||||
Keep the public firewall closed while validating:
|
||||
|
||||
```sh
|
||||
curl --fail http://127.0.0.1:8080/health
|
||||
@@ -117,20 +135,9 @@ sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
If `nginx -t` reports that `auth_request` is unknown, install the distribution package/module that
|
||||
provides it. Do not remove authentication to make the syntax check pass.
|
||||
|
||||
## Test authentication and SSE
|
||||
|
||||
Open the firewall only after all of these pass:
|
||||
|
||||
1. An unauthenticated HTTPS request is redirected to login or returns 401/403.
|
||||
2. Supplying forged `X-Thoth-Principal-*`, `X-Thoth-Is-Admin`, or `X-Thoth-Trusted-*` request
|
||||
headers does not grant access.
|
||||
3. A real authenticated non-admin can use the application but cannot open Pi Management.
|
||||
4. A real authenticated admin can use Pi Management.
|
||||
5. A browser session receives live model updates for longer than the default proxy timeout without
|
||||
batching; a protected `curl --no-buffer` test using a real short-lived login cookie is also
|
||||
acceptable. Delete the cookie jar immediately afterward.
|
||||
|
||||
Re-run these checks after changing the identity provider, auth gateway, Nginx, or ThothII release.
|
||||
For direct OIDC, verify login redirects to the configured provider, callback traffic reaches
|
||||
ThothII unchanged, forged identity headers grant nothing, and SSE is unbuffered. For deprecated
|
||||
upstream mode, additionally verify the external gateway rejects unauthenticated traffic and only
|
||||
its 2xx response can create trusted identity headers.
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
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.
|
||||
The host authentication CLI is `tht`: Workspace Validate is static, `tht auth check` is live and
|
||||
non-interactive, `--interactive` adds device-flow identity validation, and Workspace Test is the
|
||||
aggregate live gate.
|
||||
|
||||
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
|
||||
|
||||
@@ -12,10 +12,13 @@ URLs, directory/LDAP details, tokens, passwords, hashes, cookies, or realistic s
|
||||
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`
|
||||
2. Run Workspace Validate first; it is the static authentication gate. Run `tht auth check` for
|
||||
live non-interactive diagnosis, then `tht auth check --interactive` where Device Authorization
|
||||
is available, then Workspace Test for aggregate live validation.
|
||||
3. Run `tht doctor --json` and confirm this exact report order: `descriptor`, `files`, `docker`,
|
||||
`compose`, `configuration`, `authentication`, `services`, `core-http`, `frontend-http`,
|
||||
`workspace-registry`, `workflow`, `pi`.
|
||||
4. 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
|
||||
@@ -24,18 +27,19 @@ URLs, directory/LDAP details, tokens, passwords, hashes, cookies, or realistic s
|
||||
|---|---|
|
||||
| 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`. |
|
||||
| Browser callback token omits `groups` | Callback returns HTTP 401 `oidc_callback_failed`; the internal reason is not exposed. |
|
||||
| Browser callback token has malformed, indirect, or overage groups | Callback returns HTTP 401 `oidc_callback_failed`; the internal reason is not exposed. |
|
||||
| Interactive diagnostic receives missing or invalid groups | Diagnostic fails 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. |
|
||||
| Catalog token is wrong or lacks group-view-only access | Live check fails redacted with `oidc_group_catalog_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. |
|
||||
| Provider outage | Live check reports `oidc_discovery_unreachable`; browser login fails closed without exposing credentials. |
|
||||
| Restore is completed | Sessions and OIDC state are absent; all users must reauthenticate. |
|
||||
|
||||
## Status at Task 14
|
||||
|
||||
Reference in New Issue
Block a user