From 0dcc14a33a5e8fef0eb734bf7805614fd3f822a6 Mon Sep 17 00:00:00 2001 From: Ahmet Abdullah Gultekin Date: Sat, 30 May 2026 16:43:26 +0000 Subject: [PATCH] docs: auth program roadmap + CLAUDE.md/ROADMAP.md auth sweep (2026-05-30) Add ROADMAP_AUTH_2026-05-30.md (consolidated tracker: shipped passkey hybrid login + no-Firebase approve-login + NFC trust + mobile v5.2.3; in-flight feature-flagged config-driven login; operator + backlog buckets) and a pointer from ROADMAP.md. Bump CLAUDE.md "Last verified" to 2026-05-30 and add the cross-device login surfaces note to Auth Methods. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 9 ++- ROADMAP.md | 4 ++ ROADMAP_AUTH_2026-05-30.md | 111 +++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 ROADMAP_AUTH_2026-05-30.md diff --git a/CLAUDE.md b/CLAUDE.md index dd7a5d6..c4ed948 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ Multi-tenant biometric auth platform | Marmara University CSE4297 | Hexagonal Architecture **Status**: Production deployed. Phases 0-8 complete. ~1,900+ tests. All services healthy. -**Last verified**: 2026-05-29 (2026-05-29: **Card detection client-only + true nano model + launcher/rebrand finish.** Card detection is now **CLIENT-ONLY** — the server `/biometric/card-detect` fallback was removed (web-app #111), so the in-browser ONNX model is the sole path and the bio container needs no card-path rebuild. The shipped card model is **Ayşenur's true 12.3 MB YOLOv8n (opset 12)**, integrated client- and server-side (web-app #109, biometric-processor #116) — replacing the old 51 MB YOLOv8m; user-verified detecting correctly and loading ~4× faster. `amispoof` got an **"Am I Spoof?"** display rebrand (launcher app-switcher tile + amispoof page hero/titles + landing labels; the domain stays lowercase `amispoof.fivucsas.com`). The shared suite-launcher rollout is **finished**, including the verify surface (landing + integrator explainer ONLY — NOT the active auth/login flow) and the authenticated app dashboard. MFA dark-mode "black box" code-input fix + auth-flow editing hardened to create-first (delete→create data-loss + dropped-default bugs) shipped in web-app #108. Marmara's default `APP_LOGIN` flow is now **PASSWORD + pick-one {EMAIL_OTP, TOTP, QR_CODE}** (prod DB; rollback runbook in identity-core-api/docs). Details in `PROJECT_STATUS_2026-05-29.md`. 2026-05-28: **Suite launcher unified + security backlog.** Redesigned shared `` web component (web-app #103) — hosted at `app.fivucsas.com/launcher.js` (ships in web-app `public/` → Hostinger deploy), it is the ONE cross-site app switcher + global EN/TR toggle. Rolled out to `demo`, `docs` (+3 subpages: biometric/identity/sdk), `amispoof` (web-app #104 removed the amispoof auto-skip), and `landing`; deleted every site's bespoke "FIVUCSAS suite" cross-site bar + per-site EN/TR switch, incl. the dashboard TopBar toggle (Settings-page language `` kept). One toggle drives `html[data-lang]` (static sites localize via `[data-lang]` CSS) and fires a `fivucsas:languagechange` CustomEvent → `i18n.changeLanguage` for the React surfaces (dashboard + verify share `web-app/src/i18n/index.ts`; landing listens in `App.tsx`). `links.fivucsas.com` keeps its own controls — it IS the hub. **api #111 (S13)**: TOTP used-code replay prevention — bounded ~120s Redis `SET key 1 EX NX` marker per `(userId, timeStep)`, max ~3 in-window markers/user, in-memory fallback capped 50k; NOT an infinite blacklist; enrollment keeps plain verify (legit retries). **web #102 (F13/F9)**: surfaced swallowed voice-enrollment errors via `formatApiError` + the `OTP_ATTEMPTS_EXHAUSTED` state. **F12** voice threshold verified CORRECT (verify = cosine *similarity* `>=` 0.65; search = pgvector `<=>` cosine *distance* `< 0.6`) — no change. **Prod rebuild + full-repo unmerged-work scan (2026-05-28 cont'd):** rebuilt `identity-core-api` (image was 2 weeks old) → deployed 8 merged-but-undeployed security fixes (S1/S2/S9/S11/S13/S14/F14 + the JWT-aud/MFA-fail-open bundle). The rebuild crash-looped ~11 min because `.env.prod` had `APP_SECURITY_JWT_AUDIENCE=` **blank** (an empty value OVERRIDES the `:fivucsas-api` default; #100 fails fast on blank in prod) — fixed to `fivucsas-api`, see api CLAUDE.md operator note. Docker build also needed api #112: `mvn dependency:go-offline` is now best-effort (a purged upstream `jackson-databind:*-SNAPSHOT` in the transitive closure broke it; the real `mvn package` resolves via the jackson-bom pin). Scanned ALL repos (most "unmerged" branches are squash-merge debris). **Merged:** FIVUCSAS #70 (redacted a still-live partial secret on master), web #94 (ws CVE DoS), bio #104/#108/#109, api #101 + #102 (rebased onto main + ArchUnit store re-frozen via Maven container) + #99 (**V61** `audit_logs.tenant_id NOT NULL` — self-gating: pre-checks 0 NULLs & fails loud, metadata-only ALTER; applies on the next api rebuild), bio #106 (mp.solutions→`mp.tasks.vision.FaceLandmarker` port, bakes `face_landmarker.task`). **Closed superseded:** spoof #18, FIVUCSAS #68, **bio #107** (mislabeled — deleted best.pt + repointed to a non-existent best.onnx, added no model). **Held (need work):** bio #105 (`liveness_errors.py` already on main + failing test → rebase), web #90 (server-validate puzzles — its 2 backend routes are unshipped, soft-passes on 404), spoof #54 (paper-section rebase + pilot-table integrity) + #56 (stacked on #54). Flagged NO-merge: spoof `learned-fuser` branch ("100% accuracy / ACER 0.00%" on a 120-video subset — reproducibility review before any paper use). **Card detection → CLIENT-ONLY** (web-app #106, made client-only in web-app #111): `useCardDetection` runs the YOLO model in-browser (onnxruntime-web, no server round-trip). The server fallback to identity-core-api `/biometric/card-detect` was REMOVED in #111 — the in-browser ONNX model is the only path. The deployed `web-app/public/models/yolo-card-nano.onnx` is now Ayşenur's TRUE **12.3 MB YOLOv8n** (opset 12), delivered + integrated via web-app #109 (client manifest/labels + bucket SHA256) and biometric-processor #116 (`best.onnx` in-repo, `best.pt` dropped). The biometric-processor container does NOT need a rebuild for the card model anymore (server card path removed). 2026-05-21: `links.fivucsas.com` hub — API tile → `/swagger-ui.html` with admin-IP "gated" badge (raw API root returned 401), Turkish i18n role-label fixes (English under `lang=tr` was İ-mangling Latin `i` under uppercase), team contact info, Ayşenur LinkedIn URL fix; poster author contact block + **regenerated A0 PDF/PNG** from `landing-website/public/poster/files/fivucsas-poster.html`; attribution — Ayşe Gülsüm Eren GitHub `@aysegulsum` + `marun.edu.tr` academic emails across `spoof-detector` + `practice-and-test` (forensic git-author records left intact); bilingual TR/EN switchers completed on `bys-demo`/`docs-site`/`verify-widget`. Consolidated into PR #69 → `master` (whole `fix/2026-05-12-bake-mini-fasnet-models` branch). NOTE: `api.fivucsas.com/` returns 401 by design (it's an API origin, not a page); Swagger/`/v3/api-docs`/`/actuator` are admin-IP gated (403 public), OIDC discovery is public (200). Carry-forward from 2026-05-12 / 2026-05-11: 11 PRs shipped across 5 repos + Flyway repair on prod, V59/V60 applied, branch protection on 6 branches, master/main reconciled, INVESTIGATION 2026-05-07 P1 residue closed, tenant onboarding playbook + 8 ADRs + docs/ hierarchy consolidated, spoof-detector blink cache + EAR recalibration paper-P0. **Added today**: parent PR #57 (poster suite: A0 default + 4 style variants compliant with CSE4198 §5.1) + parent PR #58 (archived 18 dated 2026-04/2026-05-04 docs into `archive/2026-05/{audits,plans,reviews,roadmaps,sessions}/`, tidied `.gitignore`); bio PR #99 (closed issue #91: 32 stale unit tests + 3 asyncio-fixture leaks fixed, no production code touched, module-scoped TestClient pattern documented for follow-ups); bio Dependabot #97/#98 in flight (rebased post-#99). Submodule pointer for biometric-processor bumped to post-#99 main.) ## Architecture @@ -123,6 +123,13 @@ FIVUCSAS/ # Parent repo (submodules) PASSWORD | EMAIL_OTP | SMS_OTP | TOTP | FACE | VOICE | FINGERPRINT | HARDWARE_KEY | QR_CODE | NFC_DOCUMENT +**Cross-device login surfaces (2026-05-30, shipped):** **passkey hybrid login** (discoverable +WebAuthn mode — browser/OS resolves the user by `userHandle`, no app needed; folds into the +WebAuthn/HARDWARE_KEY method as its discoverable mode) and **approve-login** (no-Firebase, +number-matching, poll-based cross-device approval; companion to QR_CODE). The config-driven +login work (in-flight, feature-flagged) will surface PASSKEY + APPROVE_LOGIN as selectable +Layer-1 methods in the flow builder — see `ROADMAP_AUTH_2026-05-30.md`. + ## Architectural direction (2026-04-16) **Hosted-first auth.** Primary integration mode is redirective OIDC: tenants call `FivucsasAuth.loginRedirect({...})` → user redirected to `verify.fivucsas.com/login` → MFA → browser returns with `?code=…&state=…` → tenant exchanges at `/oauth2/token`. Widget iframe remains for **inline step-up MFA** only. See `web-app/docs/AUDIT_REPORT_2026-04-16.md` and `web-app/docs/plans/HOSTED_LOGIN_INTEGRATION.md` (PR-1). diff --git a/ROADMAP.md b/ROADMAP.md index 5d6c368..d9cab06 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,5 +1,9 @@ # FIVUCSAS — Product Roadmap +> **Auth program (cross-device / authenticator / config-driven login):** see the consolidated +> tracker `ROADMAP_AUTH_2026-05-30.md` (shipped passkey hybrid login + approve-login + NFC +> trust + mobile v5.2.3; in-flight feature-flagged config-driven login; operator + backlog items). + > Last updated / verified: 2026-05-30 — **stabilize-&-harden backlog COMPLETE** (see "Stabilize & harden — COMPLETE 2026-05-30" directly below) **plus** the same-day Identity & account-linking (Phases 1-5) + ROOT role/user_type unification ship (section after it). Prior 2026-05-29 admin-walkthrough wave (9 PRs) + the 2026-05-12 wave (11 PRs) + the phase-A/B/C/I closures remain valid below. Verbose tier breakdown: `archive/2026-05/reviews/INVESTIGATION_MASTER_2026-05-07.md`. ## Stabilize & harden — COMPLETE 2026-05-30 diff --git a/ROADMAP_AUTH_2026-05-30.md b/ROADMAP_AUTH_2026-05-30.md new file mode 100644 index 0000000..8ab7e99 --- /dev/null +++ b/ROADMAP_AUTH_2026-05-30.md @@ -0,0 +1,111 @@ +# FIVUCSAS — Auth Program Roadmap (2026-05-30) + +> Consolidated tracker for the 2026-05-30 cross-device / authenticator / config-driven +> login program. Source of truth for the auth work; the parent `ROADMAP.md` links here. +> Design principle (settled): **secret generation/storage — TOTP seeds, passkeys, device +> keys — lives ONLY where there is OS-secure storage (native + desktop apps + the +> browser/OS passkey store), NEVER on mobile-web; mobile-web only consumes factors.** +> Plan of record: `~/.claude/plans/shiny-munching-feigenbaum.md`. +> Reversibility rule for any login-path change: `feedback_reversible_risky_changes` +> (ship dark → staging → canary one tenant → broad; feature-flag default OFF). + +--- + +## 1. SHIPPED + DEPLOYED (verified live) + +All items below are merged to `main` (submodules) and deployed/verified in prod on 2026-05-30. + +| Capability | What shipped | PRs | Deploy / verification | +|---|---|---|---| +| **Passkey hybrid web login** | Discoverable + usernameless passkeys. `register-options` → `residentKey=required` + `UV=required`; `WebAuthnCredential` gains `discoverable` + `user_handle` (**Flyway V72**); NEW anonymous `POST /webauthn/passkey/authenticate-options` (empty `allowCredentials`) + `POST /webauthn/passkey/authenticate` (resolves user by `userHandle`, mints session), both `permitAll` in SecurityConfig. Web adds a "Sign in with a passkey" button on `verify-app/HostedLoginApp.tsx` + `LoginPage.tsx` → `navigator.credentials.get()` with empty `allowCredentials` → browser-native hybrid QR ("use your phone"), no app needed. **This is THE cross-device answer.** | api #161, web #137 | api rebuilt, V72 applied to `identity_core`; `/webauthn/passkey/authenticate-options` returns 200 in prod; register→login 201/200 confirmed. web → Hostinger (`app.fivucsas.com` bundle `index-B7OwE7r8.js`) + verify.fivucsas.com Docker rebuild (bundle `index-BBN--UaC.js`, `/login` 200). | +| **No-Firebase number-matching approve-login** | Operator chose NO Firebase → poll-based. Redis-backed (modeled on `QrSessionService`): `POST /auth/approve-login/session {email}` → `{sessionId, matchNumber, …}`, GET poll, `GET /auth/approve-login/pending` (auth'd approver), `POST …/decide {decision, matchNumber}` (mints tokens like QR approve; unknown-email decoy = no oracle). **`matchNumber` is a zero-padded STRING ("07")** — web initially typed it `number` (would drop leading zeros) → fixed. Web initiator + client-apps approver share a stack (`ApproveLoginApi` / `Repository` / `ViewModel`, reuses the `NfcApproval*` convention). | api #161, web #137, client-apps #53 | `/auth/approve-login/session` returns 200 in prod (same rebuild as passkeys). Approver UI SCREEN deferred (gated on Phase 0 — see In-Flight). | +| **NFC chip-trust / serial canonicalization / guest-email** | Chip passive-auth trust wired (api consumes bio `POST /nfc/verify-authenticity`, **fail-closed** — error/`NO_TRUST_STORE`/non-authentic ⇒ reject); NFC serial canonicalized to UPPERHEX-no-separators at the ingest boundary so a mobile-enrolled card matches a web verify and vice-versa; guest-invite email gets EN/TR i18n + tenant name. **Flyway V72** family also covers the NFC credential columns. | api #159, bio #131 | bio + the eMRTD verifier shipped earlier same day; api rebuilt with #159. **Operator-blocked at runtime:** CSCA roots must be dropped into `NFC_CSCA_TRUST_DIR` before any SOD-carrying client passes (serial-only flows unaffected). | +| **Mobile login flicker + MFA-completion fixes** | (1) v5.2.2 — login flicker / can't-pass-MFA: `MfaHandoff` JSON carried in the nav route instead of a fresh `LoginViewModel` factory instance (which had a null `mfaSessionToken` → instant bounce). (2) v5.2.3 — false "Verification failed" after a server `200 AUTHENTICATED`: publish `_authResult` + `Authenticated` FIRST, then run `cacheLoginData`/`registerPushToken` best-effort (`runCatching`); outer catch returns early if an auth result already exists. Regression test `MfaFlowAuthenticatedRegressionTest`. (3) UX: dropped the divergent guest-login button, added show/hide password, fixed system-nav-bar covering the bottom Cancel. | client-apps #44, #46, #52 | Releases **v5.2.2 / v5.2.3** published (signed APK, cert SHA-256 `5e403eca…`, versionCode 9/10). **NOTE:** these are real fixes, but a SEPARATE on-device login bug remains OPEN — see In-Flight / Waiting-on-operator. | +| **Web puzzle/quality + bio liveness** | Client biometric quality scoring + passive-liveness gate (web); liveness on bio `/enroll/multi`; CPU-only server NFC trust verify. | web (WS1), bio #131 | Deployed with the same wave (see parent CLAUDE.md "Operator reality (2026-05-30)"). | +| **Desktop installers** | Desktop Linux/Windows installers — OAuth loopback (RFC 8252) + OS token storage. | client-apps (WS4) | Scaffolding shipped; Phase-2 desktop exit per parent `ROADMAP.md` (2026-06-27 target). | + +**Integration-gate one-time exception (logged here intentionally):** the +`Integration tests (Testcontainers)` gate was made REQUIRED (P1-1, #155) while NEVER +green (deep pre-existing test-infra rot). The operator authorized a **ONE-TIME +admin-merge exception** for the four orthogonal auth PRs above (the gate stays REQUIRED +for everyone else), plus manual cross-tenant staging smoke. #160 began genuinely greening +the gate; the remaining work is tracked below. **Do not treat the gate as trustworthy +until that task closes.** + +Rollback image: `identity-core-api-identity-core-api:rollback-pre-passkeys-20260530`. + +--- + +## 2. IN-FLIGHT + +### Config-driven login (password-as-a-factor + usernameless-in-flow) + +Plan: `~/.claude/plans/shiny-munching-feigenbaum.md`. Turns login into a pure render of the +tenant's auth-flow config: password becomes a normal, removable Layer-1 method (not a hard +gate); usernameless methods (passkey / approve / QR) become flow-aware Layer-1 methods that +hand off to additional factors instead of minting tokens directly; a new unauthenticated +`GET /auth/login-config` endpoint drives the UI; the flow builder gains CHOICE-step editing +and usernameless-Layer-1 marking. + +- **Reversibility (mandatory):** the engine change is gated by the feature flag + **`app.auth.config-driven-login`, default OFF** = byte-identical to current + password-first behavior, per-tenant overridable for a canary. `login-config` returns the + legacy password-first shape when the flag is off; the web UI renders purely from + `login-config`, so flipping the flag reverts everything with **no web redeploy**. +- **Rollout:** ships **dark → staging → canary one tenant → broad**. A tagged rollback + image is kept. Don't flip any tenant's default flow until the engine + UI are proven on a + canary. +- **Status:** api workstreams (A model + B engine + C discovery + F guardrails + G + reconcile) and web workstreams (D UI + E builder + F dialog + G reconcile) are in + progress in worktree-isolated branches; lands with its own PRs. +- **Docs note:** internals are intentionally NOT documented in the per-repo CLAUDE.md + files yet — they land with the PRs. + +--- + +## 3. WAITING ON OPERATOR + +- **Mobile-app login debug-build logcat.** A separate on-device login bug persists even on + v5.2.3: the server returns `200 AUTHENTICATED` (prod logs confirm — no 500, refresh token + created) but the app still shows "Verification failed". Every server-side throw has been + ruled out. Needs the developer's **debug-build `adb logcat`** to name the on-device error. + This Hetzner box cannot run an Android emulator (no `/dev/kvm`). See + `client-apps/docs/MOBILE_TESTING_GUIDE.md`. +- **NFC trust roots + test cards.** Drop **ICAO CSCA roots (Turkey)** into the bio + container's `NFC_CSCA_TRUST_DIR`; provide **physical eID / PACE test cards** for NFC + passive-auth + PACE validation. Until the trust store is populated, any client that SENDS + a SOD is rejected (`NO_TRUST_STORE`); serial-only flows are unaffected. +- **Try passkey + install v5.2.3.** Operator to exercise the new passkey login on a real + device and install the v5.2.3 release (was workaround-using web during the data-cap + window). + +--- + +## 4. TRACKED / BACKLOG + +- **Genuinely green the integration-test gate.** #160 started it; remaining causes include + `java.time.Instant` JDBC bind in seeds, `unique_tenant_email` seed collisions, CI not + running biometric-processor `:8001` / Redis `:6379`. The **one-time admin-merge + exception is logged** (above); the gate stays REQUIRED for everyone else and is to be made + trustworthy. (Task #15.) +- **Marmara 3FA default-flow flip.** Set Marmara's default APP_LOGIN to a 3-step flow + (Layer 1 = CHOICE of all identity-capable methods incl. PASSWORD + usernameless; Layers 2 + & 3 = CHOICE of all methods). **Canary only, after the config-driven engine is proven.** + Apply via the flow API with the default-impact check first; rollback runbook in + `identity-core-api/docs`. +- **Mobile config-driven login.** Point the mobile login at `login-config`, add + PASSKEY / APPROVE_LOGIN / QR branches, stop defaulting to a password form except as the + genuine fallback. **Gated on the Phase-0 mobile login-bug fix** (shared stacks already + merged; screens deferred). + +--- + +## Cross-references + +- Plan: `~/.claude/plans/shiny-munching-feigenbaum.md` +- Reversibility rule: memory `feedback_reversible_risky_changes` +- Program detail: memory `project_auth_enhancements_20260530`, `project_mobile_login_saga_20260530` +- Per-repo specifics: `identity-core-api/CLAUDE.md` (V72, passkey/approve-login endpoints, + integration-gate note), `web-app/CLAUDE.md` (passkey + approve-login UI), `client-apps/CLAUDE.md` + (v5.2.3 fixes + approve-login approver stack) +- Parent product roadmap: `ROADMAP.md`