Version-control the Authentik OIDC blueprint, add full-logout + username regression tests
CI / web (push) Successful in 17s
CI / api (push) Successful in 24s

The Authentik blueprint that provisions the OAuth2 Provider/Application only
lived on CT121's filesystem via ad-hoc scp/pct push -- deploy/authentik/ is
now the source of truth, with redeploy steps in docs/oidc-setup.md.

Also documents two bugs found and fixed while implementing issue #19: the
first RP-Initiated Logout attempt only ended the app-scoped session, and the
provider had no property_mappings so the ID token's username claim was
missing (fell back to a raw sub hash that looked like a leaked session
token). Both are covered by new Playwright regression tests.

The deep-check Fingerbank test now skips instead of failing when its target
device (192.168.1.106) has since been manually labeled known via the
dashboard, rather than assuming it stays unlabeled forever.
This commit is contained in:
2026-07-13 09:19:49 -06:00
parent 74c7bf6ef7
commit 461bd63804
4 changed files with 156 additions and 12 deletions
+58 -6
View File
@@ -8,9 +8,22 @@ button — this is additive, not a replacement, so the existing admin login keep
No API token or admin credentials were needed. Authentik supports **blueprints**
declarative YAML files it applies automatically — so the OAuth2 Provider + Application
were created via `/opt/authentik/blueprints-local/homelab-monitor-oidc.yaml` on CT121,
picked up by the `worker` container on startup. This is purely additive: it doesn't
touch any existing user, group, or admin credential.
were created via a blueprint deployed to `/opt/authentik/blueprints-local/homelab-monitor-oidc.yaml`
on CT121, picked up by the `worker` container on startup. This is purely additive: it
doesn't touch any existing user, group, or admin credential.
**Source of truth is `deploy/authentik/homelab-monitor-oidc.yaml` in this repo** — the
copy on CT121 was hand-edited via `scp`/`pct push` several times before this was
committed here, meaning CT121 was the only record of the current config for a while. If
CT121 is ever rebuilt, redeploy from this file (substituting the real `client_secret`
from Vaultwarden, since Authentik blueprints don't support secret references and the
repo copy uses a placeholder):
```bash
scp deploy/authentik/homelab-monitor-oidc.yaml pve:/tmp/
ssh pve "pct push 121 /tmp/homelab-monitor-oidc.yaml /opt/authentik/blueprints-local/homelab-monitor-oidc.yaml"
ssh pve "pct exec 121 -- docker restart authentik-worker-1"
```
- Blueprint volume mount added to `/opt/authentik/docker-compose.yml` (backed up as
`docker-compose.yml.bak-homelab-monitor` before editing) for both `server` and `worker`.
@@ -21,10 +34,14 @@ touch any existing user, group, or admin credential.
set in `.env`) and `http://192.168.1.103:8090/api/auth/oidc/callback` (LAN fallback, works if the
tunnel/DNS is down).
- Application slug: `homelab-monitor`.
- `property_mappings`: the three default Authentik OpenID scope mappings (openid/profile/email)
— see "Bug: username field showed a raw session-token-looking hash" below for why this matters.
- `invalidation_flow`: `default-invalidation-flow` (full Authentik logout), not the
app-scoped `default-provider-invalidation-flow` — see "Bug: sign out didn't actually
sign out of Authentik" below.
To change anything (redirect URI, flow, scopes), edit the blueprint file on CT121 and
either wait for Authentik's file-watcher or `docker restart authentik-worker-1` — it
re-applies on any change to the file.
To change anything (redirect URI, flow, scopes), edit `deploy/authentik/homelab-monitor-oidc.yaml`,
redeploy via the commands above, and commit the change.
## Bug: LAN-IP issuer sent browsers somewhere they couldn't reach
@@ -69,6 +86,41 @@ Authentik has no dedicated `post_logout_redirect_uri` allowlist field (unlike
provider's DB schema (`\d authentik_providers_oauth2_oauth2provider`) before
implementing, not assumed.
## Bug: sign out didn't actually sign out of Authentik
Issue #19's premise. First implementation was spec-compliant OIDC RP-Initiated Logout —
redirected through Authentik's `end_session_endpoint` with correct `id_token_hint` +
`post_logout_redirect_uri` — but Authentik's `default-provider-invalidation-flow`
(the initial choice) only invalidates *this application's* session, leaving the
underlying Authentik browser cookie valid. A subsequent "Sign in with Authentik" click
would silently re-authenticate with no prompt at all — technically correct per spec
(RP-Initiated Logout is only supposed to end the requesting client's session), but
doesn't satisfy "sign out should sign out of Authentik too."
Fix: switched `invalidation_flow` to `default-invalidation-flow` (Authentik's actual
full-logout flow, designation `Logout` vs. the other's `Logged out of application`).
This is a per-provider setting — doesn't change any other app's own logout behavior.
Verified the difference with a full Playwright click-through both ways: app-scoped
logout → "Sign in with Authentik" silently re-authenticates; full logout → a real
Authentik login form ("Welcome to authentik! Login to continue.") is shown.
## Bug: username field showed a raw session-token-looking hash
The OAuth2Provider was created without any `property_mappings` (scope-to-claims
mappings) attached. Requesting `scope=openid profile email` in the authorization
request doesn't matter if the provider itself isn't configured to *release* the
corresponding claims — the ID token only ever carried the bare required claims
(`sub`/`iss`/`aud`/`exp`/...), confirmed by decoding a real captured ID token rather
than guessing. The app's claim fallback chain (`preferred_username ?? email ?? sub`,
`apps/api/src/auth/oidc.ts`) landed on the raw `sub` value — with `sub_mode:
hashed_user_id`, a long hash — which rendered next to the sign-out button looking like
a leaked session token.
Fix: attached Authentik's three default OpenID scope mappings (`authentik default OAuth
Mapping: OpenID 'openid'/'profile'/'email'`) via `property_mappings` in the blueprint.
`preferred_username` now populates correctly. No app-code change was needed — the fix
belonged entirely on the Authentik provisioning side.
## Credentials
`OIDC_CLIENT_ID`/`OIDC_CLIENT_SECRET` are in CT122's `.env` and saved in Vaultwarden