luckberg
07787177d4
Update CLAUDE.md
...
Publish / publish (push) Successful in 11s
Added local auth
2026-06-18 07:12:17 -04:00
Lucas Berger
1e2cc52659
docs: refresh project documentation against current codebase
Publish / publish (push) Successful in 26s
2026-06-18 06:44:29 -04:00
luckberg
18d3ee6a4f
Merge pull request 'Phase 19: Local Auth (No-OIDC Mode)' ( #23 ) from gsd/phase-19-local-auth-no-oidc-mode into main
...
Publish / publish (push) Successful in 1m25s
Reviewed-on: #23
2026-06-18 06:24:58 -04:00
Lucas Berger and Claude Opus 4.8
b6490feff4
fix(19): satisfy CI fast-checks + secret scan
...
CI / changes (pull_request) Successful in 9s
CI / api (pull_request) Successful in 3m2s
CI / fast-checks (pull_request) Successful in 4m20s
CI / security (pull_request) Successful in 1m14s
CI / harness (pull_request) Successful in 6m56s
CI / gate (pull_request) Successful in 2s
Lint (eslint --max-warnings 0):
- index.ts: disable no-unsafe-argument on the type-only Context mismatch when
delegating to the OIDC handler inside the local-session skip wrapper
- localAuth.ts: handleLogout is sync (no await) — drop async (require-await)
- devBypass.ts: disable detect-possible-timing-attacks on the public well-known
dev-placeholder string compare (not a secret comparison)
- remove dead code / unused bindings flagged by no-unused-vars: makeTestApp
(localSession.test), makeUnauthContext + BrowserContext import (login.spec),
unused memberId (admin.test), unused txSelectCount counter (me.test)
- localAuthMiddleware.test / me.test: fix unused + reflow-detached
eslint-disable directives
Format: prettier --write across the 20 Phase-19 files that were never formatted.
Secret scan (gitleaks): allowlist two false positives — the synthetic >=32-char
TEST_SECRET in localSession.test.ts, and .planning/ design prose (a generic-api-key
regex hit on "credential atomically, 409-equivalent"). Neither is a real secret.
Verified locally: format:check, lint, typecheck, md:lint, gitleaks (no leaks),
PWA 266/266, API 452/452.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-17 23:05:15 -04:00
Lucas Berger
91ab9d1f78
docs(19): ship phase 19 — PR #23
2026-06-17 22:49:45 -04:00
Lucas Berger
abf7be782a
docs(phase-19): add security threat verification (28/28 closed, threats_open: 0)
CI / changes (pull_request) Successful in 10s
CI / fast-checks (pull_request) Failing after 2m17s
CI / api (pull_request) Successful in 2m51s
CI / security (pull_request) Failing after 12s
CI / harness (pull_request) Successful in 5m9s
CI / gate (pull_request) Failing after 2s
2026-06-17 22:47:45 -04:00
Lucas Berger
af0a70ccec
test(19): complete UAT — Tests 1-3 pass live, 4 resolved-by-fix, 5→ship; 4 UI findings routed to Phase 17
2026-06-17 22:32:52 -04:00
Lucas Berger
73dd6a2383
docs(19): re-review after fixes — status clean (0 critical/warning, 2 info)
2026-06-17 20:50:25 -04:00
Lucas Berger
9cccf17ef9
chore: enable MemPalace discuss-time recall + KG mirror
2026-06-17 20:45:39 -04:00
Lucas Berger
5f74ae965d
docs(19): add code review fix report
2026-06-17 20:42:51 -04:00
Lucas Berger
cef2c66de5
merge(19): land code-review fixes (CR-01..04, BL-01..04, WR-01..07, IN-01..04)
2026-06-17 20:39:49 -04:00
Lucas Berger
2691dd0f95
fix(19): IN-04 enforce single-use OIDC-link nonce to prevent state replay
2026-06-17 20:37:09 -04:00
Lucas Berger
f02521dd02
fix(19): IN-03 evict stale loginAttempts entries to bound in-memory map growth
2026-06-17 20:35:43 -04:00
Lucas Berger
e392bf2eb7
fix(19): IN-02 add lockstep test pinning inlined scrypt params against canonical verifyPassword
2026-06-17 20:34:58 -04:00
Lucas Berger
f2fc1404d4
fix(19): IN-01 validate LOCAL_SESSION_EXPIRES coercion (fallback on NaN/non-positive)
2026-06-17 20:34:14 -04:00
Lucas Berger
4bd6b2c057
fix(19): WR-05 add no-echo tests for admin create-member and me password hook sites
2026-06-17 20:33:46 -04:00
Lucas Berger
32bdd1e92d
fix(19): WR-07 reject route ids with trailing garbage via strict integer parse
2026-06-17 20:32:26 -04:00
Lucas Berger
4cf2ad4bff
fix(19): WR-06 stop sliding the rate-limit cooldown window on rejected attempts
2026-06-17 20:31:07 -04:00
Lucas Berger
30ad25c026
fix(19): WR-03 make scrypt hashing async (threadpool) to avoid event-loop starvation DoS
2026-06-17 20:30:28 -04:00
Lucas Berger
322929aebe
fix(19): WR-02+WR-04 centralize OIDC config (env-or-app_config) and discover auth endpoint
2026-06-17 20:26:47 -04:00
Lucas Berger
c4d8d76a4c
fix(19): WR-01 parse reset-admin flags explicitly and stop echoing username
2026-06-17 20:25:18 -04:00
Lucas Berger
40666e1cc5
fix(19): BL-04 keep context oidcIss/oidcSub null for local users (no fabricated identity sentinels)
2026-06-17 20:24:01 -04:00
Lucas Berger
71537601ce
fix(19): BL-03 reject OIDC-link bind on empty iss/sub or mismatched local session (takeover guard)
2026-06-17 20:22:37 -04:00
Lucas Berger
cd095e5b67
fix(19): BL-02 match Secure attribute on logout cookie deletion to issue-time logic
2026-06-17 20:21:51 -04:00
Lucas Berger
3674b255b2
fix(19): BL-01 enforce LOCAL_SESSION_SECRET length floor in devSessionCookieMiddleware
2026-06-17 20:21:17 -04:00
Lucas Berger
b083cb7193
fix(19): CR-04 scope login lockout to username, add TTL auto-expiry + admin-reset unlock
2026-06-17 20:20:43 -04:00
Lucas Berger
6ef8e03f8c
fix(19): CR-03 return 403 for wrong current password so change-password does not log user out
2026-06-17 20:17:32 -04:00
Lucas Berger
93c47b38aa
fix(19): CR-02 send initialPassword + map 409 conflict in fetchCreateMember
2026-06-17 20:15:58 -04:00
Lucas Berger
1688f229e0
fix(19): CR-01 align OIDC-link client contract with server (authorizationUrl)
2026-06-17 20:15:23 -04:00
Lucas Berger and Claude Opus 4.8
46eaf070ea
chore: enable MemPalace for project and ignore per-project files
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-17 20:12:50 -04:00
Lucas Berger
4b635784e4
docs(19): add code review report
2026-06-17 20:08:26 -04:00
Lucas Berger
37fd896ce9
docs(phase-19): mark phase complete (verification passed, 5/5 plans)
2026-06-17 20:00:21 -04:00
Lucas Berger
0aca22f743
docs(19): verification passed (21/21) after blocker fix; UAT for live/CI items
2026-06-17 20:00:00 -04:00
Lucas Berger and Claude Opus 4.8
53da4be62b
fix(19): correct admin reset-password client URL (AUTH-LOCAL-08 blocker)
...
VERIFICATION.md found a cross-layer URL mismatch: fetchAdminResetPassword
POSTed to /api/admin/members/:id/reset-password but the API registers the
route as /api/admin/members/:id/password (admin.ts), so the Admin reset sheet
404'd on every submit. Confirmed live: old path -> 404, correct path -> 400
(route reached). Unit tests missed it because API tests hit the real path
directly and PWA tests mock the fetcher — no test crossed both layers.
Fix the client URL and add a URL-contract regression test that pins the exact
path (asserts fetch is called with /api/admin/members/:id/password).
PWA 266/266 (+1), typecheck clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-17 19:58:17 -04:00
Lucas Berger
9b10d875d4
docs(phase-19): update tracking after wave 4
2026-06-17 17:44:10 -04:00
Lucas Berger and Claude Opus 4.8
17a531550a
fix(19): resolve post-merge wave-4 harness integration gaps
...
Two issues surfaced only when plans 19-04 (login UI) and 19-05 (Option C
bypass + login.spec) were merged together and run against the real stack —
neither executor could catch them in isolation:
1. LOCAL_SESSION_SECRET was added to the CI harness (ci.yml) but not to the
local dev stack (docker-compose.dev.yml). Without it the real-login success
path (POST /api/auth/local/login) 503s when signing the session cookie, so
the e2e round-trip failed. Add the same fixed dev-only value to the dev
compose override (dev-only target; never a production secret).
2. login.spec test 1 assumed clearing the local-session cookie yields a
logged-out state, but under the always-on DEV_AUTH_BYPASS devAuthBypass()
injects DEV_USER into /api/me regardless of any cookie — a logged-out state
is architecturally unreachable in this bypass-only harness. Reframe the test
to drive /login directly (validating the real-browser render of all brand +
form surfaces) and move the unauthenticated root->/login redirect-gate
coverage to a unit test in App.test.tsx where meQuery.isError is controllable.
Result: API 446/446, PWA 265/265 (+2 gate tests), e2e desktop 42 passed / 3
skipped (all login specs green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-17 17:43:11 -04:00
Lucas Berger
11977fddf4
chore: merge executor worktree (worktree-agent-ad5628ea37541e256)
2026-06-17 17:28:49 -04:00
Lucas Berger
c7142b5fe5
chore: merge executor worktree (worktree-agent-a71adf90ed21b044c)
2026-06-17 17:28:48 -04:00
Lucas Berger
63beb74650
docs(19-04): complete PWA login UI + account management surfaces plan
2026-06-17 17:26:07 -04:00
Lucas Berger
eba0bb095d
docs(19-05): complete dev-bypass rework + harness + CI plan (checkpoint)
2026-06-17 17:22:39 -04:00
Lucas Berger
19c45eb069
feat(19-04): AdminPage LOCAL ACCOUNTS + SettingsSheet change-password / link-OIDC
...
- Add hasLocalCredential to AdminMember type (mirrors API extension from plan 19-02)
- Add createMember mutation + Surface 11A inline add-member form in AdminPage
- Add Surface 11B Reset-password button in MemberRow (hasLocalCredential gate)
- Add ResetPasswordSheet component (bottom-sheet, role=dialog, focus-managed, Escape closes)
- Add Surface 12 Change-password row in SettingsSheet (hasLocalCredential gate)
- Add Surface 13 Link-OIDC identity row in SettingsSheet (hasLocalCredential + oidcEnabled gate)
- Add ChangePasswordSheet component (current/new/confirm fields, change-password mutation)
- Add LinkOidcSheet component (confirmation dialog; uses generic OIDC copy per D-06, no provider branding)
- Fix: update InstructionSheet.test.tsx to wrap with QueryClientProvider (Rule 1 - now uses useQuery)
- Fix: remove stale eslint-disable in App.test.tsx (lint --max-warnings 0 would fail)
- All 263 tests pass; typecheck clean; lint clean
2026-06-17 17:21:20 -04:00
Lucas Berger
1f94dc5eb7
feat(19-05): global-setup local_credentials seed + login.spec.ts + CI harness env
...
- global-setup.ts: TRUNCATE local_credentials + seed devuser/devpass (PHC scrypt inline)
- Create login.spec.ts: real-login-form e2e (gate redirect, wrong-password error, correct login)
- ci.yml: add LOCAL_SESSION_SECRET dev value + local_credentials seed step in harness job
- Fix all test mocks: add devSessionCookieMiddleware no-op to vi.mock(devBypass.js) blocks
in admin/setup/push/lists/localAuth/authMode/requireAdmin tests (Rule 1 - Bug: missing export)
- Full API suite: 446/446 tests pass; pnpm typecheck: exit 0
2026-06-17 17:21:05 -04:00
Lucas Berger
32d0408774
feat(19-04): LoginPage (Surfaces 1-10) + App.tsx authModeQuery gate + /login route
...
- Create LoginPage with BrandSlot, username/password form, show/hide toggle
- Four error states: invalid credentials, rate-limit, locked, server (all per UI-SPEC)
- OIDC method divider + 'Login with OIDC' button rendered only when oidcEnabled
- Accessibility: role=main, h1 in BrandSlot, h2 Sign in, aria-live error banner, 44px targets
- Focus management: username autofocus, Enter navigates username→password→submit
- App.tsx: add authModeQuery (queryKey ['authMode'], staleTime 60s)
- App.tsx: add /login standalone route (sibling of /setup, no AppNav/BottomTabBar)
- App.tsx: login gate after setup gate — meQuery error + localEnabled → Navigate /login
- App.tsx: OidcRedirect helper for OIDC-only mode (meQuery error + !localEnabled + oidcEnabled)
- Fix App.test.tsx to include fetchAuthMode mock and hasLocalCredential in user fixture
2026-06-17 17:14:18 -04:00
Lucas Berger
82391874ee
feat(19-05): add break-glass reset-admin CLI (dev-only, .dockerignore'd)
...
- Create apps/api/scripts/reset-admin.ts (AUTH-LOCAL-11, D-13, D-15)
- Dev-only guard as FIRST executable statement (NODE_ENV=production throws)
- Inline scrypt PHC hashPassword (cannot import compiled TS, Pitfall 11)
- Parse --username/--password from argv; never log password value (T-19-26)
- --dry-run validates args + DB connection without writing
- Upserts users (is_admin=true) + local_credentials rows idempotently
- Script excluded from prod image via .dockerignore apps/api/scripts/ (IMG-02)
- dry-run: exit=0, no password in output verified
2026-06-17 17:13:04 -04:00
Lucas Berger
3094df84c8
feat(19-05): Option C — devSessionCookieMiddleware issues real local-session cookie under bypass
...
- Add devSessionCookieMiddleware() to devBypass.ts (production hard-guard FIRST)
- Issues local-session JWT cookie for DEV_USER when no cookie present under bypass
- Pure no-op when NODE_ENV=production, DEV_AUTH_BYPASS!=true, or secret not set
- Mount devSessionCookieMiddleware() after devAuthBypass() in index.ts
- Existing devBypass tests: 3/3 pass; typecheck: exit 0
2026-06-17 17:11:31 -04:00
Lucas Berger
869cdc26c8
feat(19-04): add LoginError, fetchAuthMode/login/logout + auth fetchers, BrandSlot, brand-seam tokens
...
- Add LoginError class (4 codes: invalid/rate-limit/locked/server) mirroring SessionExpiredError shape
- Add hasLocalCredential to MeUser interface
- Add fetchAuthMode, fetchLocalLogin, fetchLocalLogout (pre-auth endpoints)
- Add fetchChangePassword, fetchCreateMember, fetchAdminResetPassword, fetchLinkOidc
- Create BrandSlot component with Phase-17-ready placeholder (48px circle, FS initials, h1, tagline)
- Add --brand-logo-* CSS custom properties to tokens.css (Phase 17 seam)
2026-06-17 17:10:30 -04:00
Lucas Berger
1cf572a2b9
docs(phase-19): update tracking after wave 3
2026-06-17 17:06:30 -04:00
Lucas Berger
b2f3182ef4
chore: merge executor worktree (worktree-agent-a11c9f6d35e8d8721)
2026-06-17 17:01:05 -04:00
Lucas Berger
e0d471a5d5
docs(19-03): complete local-auth middleware wiring plan
2026-06-17 17:00:42 -04:00
Lucas Berger
9b569efeab
feat(19-03): wire /callback link branch, OIDC-guard skip, de-Authelia comments
...
- /callback: reads signed state, extracts linkUserId, calls linkOidcToUser after OIDC session set; OidcLinkConflictError redirects to /?error=oidc-link-conflict
- OIDC guard: oidcAuthMiddleware() factory called once at construction, handler invoked per-request inside skip-when-user-set wrapper (D-03)
- middleware.ts: de-Authelia-ize comments — generic OIDC identity provider language (D-06, AUTH-LOCAL-18)
- localAuthMiddleware.ts: cast to typeof DEV_USER for ContextVariableMap type compatibility
- All 446 tests pass; typecheck clean
2026-06-17 16:59:06 -04:00