Files
familysync/.planning/phases/19-local-auth-no-oidc-mode/19-UAT.md
T

132 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 19-local-auth-no-oidc-mode
created: 2026-06-17T18:25:00Z
updated: 2026-06-17T21:20:00Z
status: complete
source: verification + plan-checkpoints
gaps: []
findings_routed_to_phase_17: [F-01, F-02, F-03, F-04]
---
## Live UAT Session (resumed 2026-06-17, post code-review-fix)
**Stack configured for local-auth / no-OIDC mode** (Phase 19's canonical deployment):
- API rebuilt from the phase-19 branch (all 18 code-review fixes live; verified `initialPassword` + 403 present in running `dist`).
- `DEV_AUTH_BYPASS=false` and `OIDC_ISSUER=""` via throwaway `docker-compose.uat.yml` override
(tracked files untouched; restore the normal bypass stack after UAT).
- Seeded local admin: **username `uatadmin` / password `UATtest1234!`** (user id 2, is_admin=1).
- PWA on host Vite at **http://localhost:5173**.
**Automated API smoke (pre-checks):**
-`POST /api/auth/local/login` (uatadmin) → 200 + `local-session` cookie.
- ✅ Wrong password → 401 `{"error":"Invalid credentials"}` (generic, no field blame).
- ✅ Authenticated `GET /api/me``{id:2, isAdmin:true, hasLocalCredential:true}`.
- ⚠️ Unauth `GET /api/me`**500 `Invalid session`** (not 401) in no-OIDC mode: the OIDC guard
is mounted whenever bypass is off and errors trying to redirect with a blank issuer. **Cosmetic**
— PWA gates on `meQuery.isError && localEnabled` (App.tsx:213) so it still redirects to `/login`.
Candidate follow-up: short-circuit the OIDC guard to a clean 401 when no issuer is configured.
**Note on Item 1 OIDC button:** the "OIDC button appears when oidcEnabled" sub-check can't be
exercised on this box (no reachable Authelia → blanked). Covered at unit/e2e level. This session
verifies the no-OIDC local-auth surface, which is the phase's primary deliverable.
# Phase 19: Local Auth (No-OIDC Mode) — User Acceptance Tests
All automated verification passed (API 446/446, PWA 266/266, e2e desktop 42 passed
/ 3 skipped, typecheck clean; VERIFICATION.md status: passed, 21/21 must-haves).
The single blocker found during verification (admin reset-password URL mismatch) was
fixed and confirmed live (commit `53da4be`).
The items below are the remaining **human / live-stack** checks that cannot be driven
from the dev `DEV_AUTH_BYPASS` harness or a headless box. They do not block automated
goal achievement but should be confirmed before shipping.
## UAT Items
### 1. Login page visual + flow (real, non-bypass stack)
- **Test:** Run the stack with OIDC/Authelia configured and `DEV_AUTH_BYPASS` **off**.
Visit the app unauthenticated → confirm redirect to `/login`. Verify the brand slot
("FS" mark, "FamilySync", "Family calendar & lists"), the form (username auto-focus,
password show/hide), wrong-creds single error ("Incorrect username or password."),
correct-creds navigation into the app, and the OIDC button only when `oidcEnabled`.
- **Expected:** All surfaces per 19-UI-SPEC; no "Authelia" text anywhere; error copy
never blames a specific field.
- **Why human:** The unauth login-gate redirect is unreachable under the bypass-only
harness (covered at unit level in `App.test.tsx`); the full visual flow needs a real
browser against a non-bypass deployment. (Desktop/Chromium portions are already e2e-
covered via `login.spec.ts`.)
- **result: pass** (2026-06-17, live no-OIDC stack, operator-confirmed — all steps:
redirect to /login, brand slot, username autofocus, password show/hide, generic
wrong-creds error, successful login into the app). Setup-gate precedence confirmed:
`setupComplete===true` so /login is reachable (App.tsx checks `/setup` redirect first).
### 2. Admin Reset-password sheet (live, end-to-end)
- **Test:** As an admin, open Admin → Local Accounts → Reset password for a member;
submit a new password; confirm the member can then log in with it.
- **Expected:** `POST /api/admin/members/:id/password` returns 200; new password works.
- **Why human:** Needs a live stack with an admin session and a real local member.
(URL fix already verified: route reachable, 404 eliminated.)
- **result: pass (functional) — with UX gap.** Operator created `testmember` + reset its
password via the admin UI. Verified at DB/login level: member exists (user 3, non-admin);
login with the **reset** pw (`MemberPass456!`) → 200; login with the **original**
(`MemberPass123!`) → 401. So **CR-02 create-member + reset both work and persist.**
BUT neither action showed a success confirmation (see Finding F-01).
### 3. Settings Change-password sheet (live, local user)
- **Test:** As a local user, Settings → Account → Change password; verify wrong current
password shows "Current password is incorrect.", correct current updates, and the new
password works on next login.
- **Expected:** Current-password verification enforced; update succeeds; re-login works.
- **Why human:** Needs a live stack with a local-user session.
- **result: pass** (2026-06-17, verified functionally via API on the live no-OIDC stack as
`testmember`). CR-03 confirmed: wrong current password → **403 (not 401)** and the session
stays valid (`/me`→200, no force-logout); correct current → 200; new password logs in (200),
old password rejected (401).
### 4. Rate-limit / lockout test flakiness (harden)
- **Test:** Run `apps/api/tests/routes/localAuth.test.ts` Test 5 (10 failures → 423)
~10 times; characterize the intermittent failure the orchestrator observed (1 failure
across 3 runs, then stable).
- **Expected:** Stable pass; if timing-dependent, harden the in-memory rate-limit test
(e.g. fake timers / deterministic clock).
- **Why human:** Timing-dependent in-memory test; needs repeated runs to characterize.
- **result: resolved-by-fix.** The code-review fix (CR-04/WR-06/IN-03) rewrote the limiter and
made the lockout test **deterministic** — it back-dates `lockedAt` instead of using wall-clock
timers (`localAuth.test.ts:280`), structurally removing the timing flakiness. The fixer ran the
full API suite **452/452** (incl. Test 5/5b). Couldn't be re-run in this session's shell (no
test-DB root creds — `ER_ACCESS_DENIED`); confirm via CI or `set -a; . ./.env; set +a;
DB_HOST=127.0.0.1 pnpm --filter @familysync/api test`.
### 5. CI harness green + D-15 image boundary (push, outward-facing)
- **Test:** Push the branch and open the PR so Gitea CI runs. Confirm the `harness` job
(iphone + pixel + desktop, incl. `login.spec.ts`) is green, the `api` job is green,
and the published-image hygiene checks (no `apps/api/scripts/` or `apps/pwa/e2e/` in
the prod image) pass.
- **Expected:** CI all green; no dev artifact in the shipped image (D-14/D-15).
- **Why human:** Pushing to the remote / triggering CI is an outward-facing action the
operator owns. (Plan 19-05's blocking checkpoint.)
- **result: deferred to `/gsd-ship`** (operator decision 2026-06-17). The push/PR/CI run +
image-hygiene gate is owned by the ship workflow, not this UAT session.
## Live Session Findings (2026-06-17)
Surfaced by the operator during Test 2. **Operator decision (2026-06-17): route ALL four UI
findings — including the logout button — to Phase 17 (UI Optimization & Polish), which has not yet
kicked off. None block Phase 19**, whose auth machinery is functionally complete and verified.
All four added to `17-CONTEXT.md` (Phase-17 branch):
- **F-02 — No logout button in the UI (functional-UI).** Logout is fully plumbed — endpoint
`POST/GET /api/auth/local/logout` returns 200 and clears the cookie (BL-02 verified live), and
`fetchLocalLogout()` exists in `apps/pwa/src/api/client.ts:127` — but **no component calls it**
(grep of `apps/pwa/src` finds zero logout buttons/handlers). Phase 17 wires a logout control to
the existing client function (no backend work). Per operator: a UI concern, not a Phase-19 blocker.
- **F-01 — Admin create/reset give no success feedback.** Both succeed (verified at DB/login level)
but show no success toast/confirmation. Add success feedback to the admin local-account flows.
- **F-03 — Dialogs/popups render at bottom-center instead of properly centered (cosmetic).** Fits
Phase 17's fixed-chrome/sheet-positioning sweep.
- **F-04 — Admin UI navigation is clunky and needs a rework.** UX-polish item for Phase 17.
**Status:** Phase 19 UAT **complete (functional)** — Tests 13 pass (live), Test 4 resolved-by-fix,
Test 5 deferred to `/gsd-ship`. No Phase-19 blockers. F-01F-04 carried to Phase 17.