docs: e2e suite writeup, update CLAUDE.md/README with OIDC verification status
CI / web (push) Successful in 17s
CI / api (push) Successful in 23s

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 22:30:21 -06:00
parent 294be26500
commit 13f69ea1aa
3 changed files with 74 additions and 15 deletions
+26 -11
View File
@@ -40,6 +40,17 @@ One login instead of logging into Proxmox, Zabbix, OMV, and every service separa
- Auth: local (bcrypt + signed session cookie) is **always** available — it's not an exclusive mode
switch. OIDC via Authentik is an *additional* sign-in button, live when `OIDC_ENABLED=true`. See
`docs/oidc-setup.md`. Deliberately additive so OIDC config problems can never lock out the admin login.
Two real bugs found only once someone actually clicked the button (fixed, see `docs/oidc-setup.md` and
`e2e/README.md`): `OIDC_ISSUER_URL` pointed at Authentik's LAN IP, which got baked into the
browser-facing `authorization_endpoint`; and the callback hardcoded `http://` when reconstructing the
current URL (Fastify never sees HTTPS, it terminates upstream), sending the wrong scheme as
`redirect_uri` in the token exchange. Both are exactly the class of bug curl-based verification cannot
catch — real redirect chains and a real IdP login form need a real browser.
- **`e2e/`** (Playwright, done): drives a real browser against the **live** deployment, not a local dev
server. `local-login.spec.ts` and `oidc-login.spec.ts` — the latter uses a dedicated Authentik test
account (`playwright-test`, blueprint-provisioned, path `users/service-accounts`, never a real personal
login) so the full OIDC flow can run unattended. Not wired into CI (hits production, needs secrets) —
run by hand after auth-related changes. See `e2e/README.md` for the bugs it already caught.
- **Device discovery** (done, separate from the collector system above — it's inventory, not per-host
metrics): `scripts/discover-devices.sh` runs via systemd timer **on the CT122 host**, not in Docker
(real ARP entries live in the host's network namespace, not Docker's bridge network). API reads the
@@ -60,7 +71,11 @@ One login instead of logging into Proxmox, Zabbix, OMV, and every service separa
(gitignored, not in the repo). Public key installed on `omv` and `ripper` with a forced command —
see `docs/ssh-collector-key-setup.md`.
- **Authentik OIDC provider**: set up via an Authentik blueprint on CT121 (not a manual UI step) — see
`docs/oidc-setup.md`. Purely additive, didn't touch any existing Authentik user/credential.
`docs/oidc-setup.md`. Purely additive, didn't touch any existing Authentik user/credential. Issuer URL
is the public `https://auth.jerodrigged.com` (a pre-existing NPM proxy host, predates this project) —
**never point this at the LAN IP**, see the bug writeup above.
- **Authentik test account**: `playwright-test` (blueprint-provisioned, `authentik_core.user`, path
`users/service-accounts`), used only by `e2e/tests/oidc-login.spec.ts`. Not a real person's login.
- **Public exposure (done, issue #13 closed)**: `https://monitor.jerodrigged.com` is live. Chain is
Cloudflare Tunnel (existing tunnel `ac3a4440-...`, added a Public Hostname pointing at NPM, not
directly at the dashboard) → NPM proxy host id 14 (`192.168.1.185:80``192.168.1.103:8090`) → app.
@@ -74,9 +89,6 @@ One login instead of logging into Proxmox, Zabbix, OMV, and every service separa
OIDC login still works if the tunnel/DNS is ever down.
- **Vaultwarden**: dashboard admin credentials saved as "Homelab Monitor (dashboard admin)". OIDC client
credentials not yet saved there (pending a `BW_SESSION` from the user).
- **Not yet verified**: nobody has actually clicked "Sign in with Authentik" through a real browser —
the redirect chain and params are confirmed correct via curl, but the final interactive consent/login
step needs a human.
## Zabbix: intentionally skipped, not blocked
@@ -88,16 +100,19 @@ integration entirely rather than finish configuring a service that's being repla
issue #11 is closed as won't-do — if a metrics-alerting panel is wanted later, it should target
Grafana/Prometheus once `homelab#15` lands, as a new issue, not a reopening of #11.
## Status: original backlog complete
## Status: original backlog complete, OIDC verified end-to-end
All 14 issues filed at project start are closed (10 shipped, #11/Zabbix closed as won't-do, no others
skipped). `GET .../issues?state=open` on this repo should return empty. If picking this up again with
no specific ask from the user, there's no queued work — check in with them for what's next rather than
inventing scope. Remaining known gaps, none blocking:
skipped). `GET .../issues?state=open` on this repo should return empty. First real user click-through of
"Sign in with Authentik" surfaced two bugs (LAN-IP issuer, wrong-scheme redirect_uri in token exchange —
both above), fixed, and now covered by `e2e/oidc-login.spec.ts` as regression coverage. Both local and
OIDC login are Playwright-verified passing (5 consecutive runs) as of this writing.
If picking this up again with no specific ask from the user, there's no queued work — check in with them
for what's next rather than inventing scope. Remaining known gaps, none blocking:
- Nobody has clicked "Sign in with Authentik" through an actual browser yet (see note above) — worth
confirming next time a human is at the dashboard.
- OIDC client credentials aren't in Vaultwarden yet (Proxmox token and admin password are) — needs a
`BW_SESSION` from the user, see prior session's request in the conversation history if resuming soon.
`BW_SESSION` from the user.
- `diskPaths` in `hosts.yaml` vs. the remote forced-command scripts on omv/ripper is a manual-sync point
(documented, not automated — only 2 hosts, hasn't been worth it).
- `e2e/` isn't wired into CI — run by hand (`cd e2e && npm test`) after any auth-related change.
+4 -4
View File
@@ -1,12 +1,12 @@
# Homelab Monitor
A single dashboard for the health of everything in the homelab (documented in
[`jhodgkin/homelab`](https://git.jerodrigged.com/jhodgkin/homelab)): Proxmox host + LXC
CPU/mem/disk/pressure, and — as the backlog fills in — SSH-monitored bare-metal boxes, a LAN
device inventory, and Zabbix alerts. One login instead of N.
[`jhodgkin/homelab`](https://git.jerodrigged.com/jhodgkin/homelab)): Proxmox host + LXC + bare-metal
CPU/mem/disk/pressure, historical sparklines, and a LAN device inventory. Local auth always available,
plus an optional "Sign in with Authentik" OIDC button. Live at https://monitor.jerodrigged.com.
Project status, architecture, and how to resume work: see [`CLAUDE.md`](./CLAUDE.md).
Roadmap: [Gitea issues](https://git.jerodrigged.com/jhodgkin/homelab-monitor/issues), milestone `v1-dashboard`.
Playwright e2e tests against the live deployment: see [`e2e/README.md`](./e2e/README.md).
## Local development
+44
View File
@@ -0,0 +1,44 @@
# E2E tests
Playwright tests that drive a real browser against the **live** deployment
(`https://monitor.jerodrigged.com` by default) — not a local dev server or mocked
backend. They exist specifically because curl-based verification missed real bugs that
only show up with an actual browser (cookies, redirects, DOM interaction).
## Setup
```bash
cd e2e
npm install
npx playwright install chromium
cp .env.example .env # fill in LOCAL_PASSWORD and OIDC_PASSWORD
npm test
```
`OIDC_USERNAME`/`OIDC_PASSWORD` should be the dedicated `playwright-test` Authentik
account (blueprint-provisioned on CT121, path `users/service-accounts`) — **never** a
real personal login. See `docs/oidc-setup.md`.
## Not wired into CI
These hit the live production dashboard and a live Authentik instance, and need
credentials as secrets — deliberately not run automatically on every push. Run by hand
after auth-related changes.
## Bugs these caught on first write (all fixed, kept as regression coverage)
1. **Local logout silently failed.** Frontend always sent `Content-Type:
application/json` even for logout's bodyless POST; Fastify's default JSON parser
rejects that combination (400). curl testing missed it — curl doesn't set that
header without `-d`.
2. **OIDC login redirected to a LAN IP.** `OIDC_ISSUER_URL` used Authentik's internal
address; its discovery document echoes back whichever host you query it through,
so that LAN IP ended up as the *browser-facing* `authorization_endpoint`.
3. **OIDC token exchange rejected with `invalid_client`.** The callback handler
hardcoded `http://` when reconstructing the current URL (Fastify never sees HTTPS —
it terminates upstream), which sent the wrong scheme as `redirect_uri` in the token
exchange. Authentik's own event log said plainly "Invalid redirect URI used by
provider"; the error surfaced to the app as a generic `invalid_client`.
None of these were caught by earlier curl-based verification — browser-driven auth
flows (cookies, real redirect chains, a real IdP's login form) need a real browser.