Commit Graph

10 Commits

Author SHA1 Message Date
jhodgkin 461bd63804 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.
2026-07-13 09:19:49 -06:00
jhodgkin dfdc37eec1 Add e2e coverage for the new-device badge
CI / web (push) Successful in 15s
CI / api (push) Successful in 22s
Cross-checks the rendered .device-badge.new count against the API's
isNew count rather than forcing a synthetic new-device scenario --
that would permanently pollute the production seen_macs table on
every test run. Full end-to-end verification (synthetic device
injected via devices-raw.json, confirmed isNew: true, confirmed 0
false positives across the other 71 real devices) was done manually
against the live deployment and cleaned up afterward; this test
guards the UI/API consistency going forward.

16/16 e2e tests green across 9 spec files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 00:06:04 -06:00
jhodgkin e5ace7692a Add Fingerbank e2e coverage; fix two test races found along the way
CI / web (push) Successful in 16s
CI / api (push) Successful in 22s
- deep-check.spec.ts: new test asserting a Fingerbank ID + confidence
  label appears (targets the Nintendo device specifically, skips if
  it's not currently on the network -- not something the test suite
  controls). First run false-skipped because it checked row.count()
  before waiting for the device table to actually render.
- device-ratio.spec.ts: badge counts and displayed text were read as
  two separate one-shot queries (.count()/.textContent() don't
  auto-retry like expect() matchers), which raced a background poll
  once and failed. Wrapped the whole comparison in expect().toPass()
  so it retries atomically instead. Confirmed fixed: 4/4 clean runs
  with retries disabled.

15/15 e2e tests green across 8 spec files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 23:40:47 -06:00
jhodgkin ee9b6d15cd Add e2e coverage for the known/unknown device ratio
CI / web (push) Successful in 16s
CI / api (push) Successful in 22s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 23:23:38 -06:00
jhodgkin a2067581a5 Add deep-check e2e coverage
CI / web (push) Successful in 16s
CI / api (push) Successful in 23s
Verified via real HTTP path (not just the direct SSH test done while
building it): correctly identified Home Assistant via SSDP, 400 on
malformed IP, 401 unauthenticated, clean empty result for a device
with nothing to find (Echo-type devices deliberately minimize their
LAN footprint -- expected, not a bug).

Fixed the same substring-matching mistake caught earlier in
device-labeling.spec.ts, this time on "known" being a substring of
"unknown" -- switched to matching .device-badge.known specifically.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 23:17:37 -06:00
jhodgkin b84fd373c2 Add device labeling e2e test; add data-ip for reliable row targeting
CI / web (push) Successful in 17s
CI / api (push) Successful in 23s
First test run hit a bug in the test itself: Playwright's hasText
filter does substring matching, so IP 192.168.1.1 matched
192.168.1.10, 192.168.1.100, 192.168.1.171, etc -- flaky/wrong row
selection. Added a data-ip attribute to each row for exact targeting
instead of relying on text content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 23:08:06 -06:00
jhodgkin cdbdd01541 Make the network device table sortable by column
CI / web (push) Successful in 17s
CI / api (push) Successful in 24s
Click a header to sort by it (ascending), click again to reverse.
Status defaults to known-first (its display string sorts that way
naturally, no special-casing needed). IP sorts numerically by octet,
not lexically. Ties fall back to IP order so the table doesn't
reshuffle mid-poll for devices sharing a sort value (e.g. many
unnamed unknowns).

Added a Playwright test verifying IP asc/desc and Name asc against
real rendered data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 22:48:51 -06:00
jhodgkin 48acfcd715 Broaden e2e coverage: invalid credentials, session persistence, API auth
CI / web (push) Successful in 17s
CI / api (push) Successful in 32s
Added: wrong-password rejection (local + OIDC), session survives a
page reload, and API-level checks that /api/hosts, /api/devices,
/api/auth/me all reject unauthenticated requests regardless of what
the UI does.

No new app bugs found this round -- one test assertion was itself
wrong (expected no session cookie on failed login; @fastify/session
issues an anonymous cookie on any response by design, that's normal).
Fixed to assert the property that actually matters: the cookie grants
no access. 9/9 tests green across 4 consecutive full-suite runs with
parallel workers, no flakiness.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 22:38:10 -06:00
jhodgkin 294be26500 fix: OIDC callback sent wrong scheme in token exchange redirect_uri
CI / web (push) Successful in 17s
CI / api (push) Successful in 24s
Fastify only sees plain HTTP -- TLS terminates at NPM/Cloudflare
before reaching this process. Building the callback's currentUrl from
req.headers.host with a hardcoded "http://" sent
redirect_uri=http://monitor.jerodrigged.com/... during the token
exchange, which Authentik rejects (logged as generic "invalid_client"
to the client, but its own event log said plainly: "Invalid redirect
URI used by provider"). Fixed by reusing the known-correct
redirectUri's origin and only taking the query string from the actual
request, instead of trying to infer scheme from headers.

Also fixes the Playwright OIDC test's selectors (Authentik's password
field has no <label> association -- placeholder text, not getByLabel).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 22:27:39 -06:00
jhodgkin 9b5051d3ac Add Playwright e2e tests; fix two real bugs they caught
CI / web (push) Successful in 17s
CI / api (push) Failing after 3h0m1s
fix: OIDC_ISSUER_URL used Authentik's LAN IP (192.168.1.208:9443).
Authentik's discovery doc echoes back whichever host you query it
through, so that LAN IP got baked into authorization_endpoint -- the
URL the *browser* gets redirected to. Anyone off the LAN got sent to
an address they couldn't reach. Authentik was already publicly
exposed at auth.jerodrigged.com (pre-existing NPM proxy host); switched
to that, which also has a real cert so OIDC_ALLOW_INSECURE_TLS could
go back to false. Reported as "signed in via Authentik, redirected to
the local IP, failed."

fix: frontend's request() helper always sent Content-Type:
application/json, even for logout's bodyless POST. Fastify's default
JSON parser rejects an empty body under that content-type (400) --
sign-out silently failed to log the user out. curl-based testing
missed this because curl doesn't set that header without -d. Caught
immediately by the new Playwright local-login test.

e2e/: Playwright suite for local auth and OIDC login. OIDC test uses
a dedicated Authentik test user (blueprint-provisioned, never a real
personal login) so the whole flow can run unattended and repeatedly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 22:22:34 -06:00