9 Commits

Author SHA1 Message Date
cc460df5d4 Phase 2: Add passkey frontend UI
New files:
- PasskeySection.tsx: Passkey management in Settings > Security with
  registration ceremony (password -> browser prompt -> name), credential
  list, two-click delete with password confirmation

Changes:
- types/index.ts: PasskeyCredential type, has_passkeys on AuthStatus
- api.ts: 401 interceptor exclusions for passkey login endpoints
- useAuth.ts: passkeyLoginMutation with dynamic import of
  @simplewebauthn/browser (~45KB saved from initial bundle)
- LockScreen.tsx: "Sign in with a passkey" button (browser feature
  detection, not per-user), Fingerprint icon, error handling
- SecurityTab.tsx: PasskeySection between Auto-lock and TOTP
- package.json: Add @simplewebauthn/browser ^10.0.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:50:06 +08:00
e8109cef6b Add required email + date of birth to registration, shared validators, partial index
- S-01: Extract _EMAIL_REGEX, _validate_email_format, _validate_name_field
  shared helpers in schemas/auth.py — used by RegisterRequest, ProfileUpdate,
  and admin.CreateUserRequest (eliminates 3x duplicated regex)
- S-04: Migration 038 replaces plain unique constraint on email with a
  partial unique index WHERE email IS NOT NULL
- Email is now required on registration (was optional)
- Date of birth is now required on registration, editable in settings
- User model gains date_of_birth (Date, nullable) column
- ProfileUpdate/ProfileResponse include date_of_birth
- Registration form adds required Email, Date of Birth fields
- Settings Profile card adds Date of Birth input (save-on-blur)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:21:11 +08:00
45f3788fb0 Add preferred name + email to registration, profile card to settings
Registration form now collects optional preferred_name and email fields.
Settings page Profile card expanded with first name, last name, and email
(editable via new GET/PUT /api/auth/profile endpoints). Email uniqueness
enforced on both registration and profile update. No migrations needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:02:42 +08:00
1aeb725410 Fix issues from QA review: hash upgrade ordering, interceptor scope, guard
- W-01: Move is_active check before hash upgrade so disabled accounts
  don't get their password hash silently mutated on rejected login
- W-02: Narrow interceptor exclusion to specific auth endpoints instead
  of blanket /auth/* prefix (future-proofs against new auth routes)
- W-03: Add null guard on optimistic setQueryData to handle undefined
  cache gracefully instead of spreading undefined
- S-01: Clear loginError when switching from register back to login mode
- S-03: Add detail dict to auth.login_blocked_inactive audit event

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 02:13:48 +08:00
b2d81f7015 Block inactive user login + fix login flicker + inline error alerts
- Backend: reject is_active=False users with HTTP 403 after password
  verification but before session creation (prevents last_login_at
  update, lockout reset, and MFA token issuance for disabled accounts)
- Frontend: optimistic setQueryData on successful login eliminates the
  form flash between mutation success and auth query refetch
- LockScreen: replace lockoutMessage + toast.error with unified
  loginError inline alert for 401/403/423 responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 01:21:06 +08:00
72e00f3a69 Fix QA review #2: backup code flow, audit filters, schema hardening
C-01: verifyTotp now sends backup_code field when in backup mode
C-02: Backup code input filter allows alphanumeric chars (not digits only)
W-01: Audit log ACTION_TYPES aligned with actual backend action strings
W-02: Added extra="forbid" to SetupRequest and LoginRequest schemas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 04:59:29 +08:00
d8bdae8ec3 Implement multi-user RBAC: database, auth, routing, admin API (Phases 1-6)
Phase 1: Add role, mfa_enforce_pending, must_change_password to users table.
Create system_config (singleton) and audit_log tables. Migration 026.

Phase 2: Add user_id FK to all 8 data tables (todos, reminders, projects,
calendars, people, locations, event_templates, ntfy_sent) with 4-step
nullable→backfill→FK→NOT NULL pattern. Migrations 027-034.

Phase 3: Harden auth schemas (extra="forbid" on RegisterRequest), add
MFA enforcement token serializer with distinct salt, rewrite auth router
with require_role() factory and registration endpoint.

Phase 4: Scope all 12 routers by user_id, fix dependency type bugs,
bound weather cache (SEC-15), multi-user ntfy dispatch.

Phase 5: Create admin router (14 endpoints), admin schemas, audit
service, rate limiting in nginx. SEC-08 CSRF via X-Requested-With.

Phase 6: Update frontend types, useAuth hook (role/isAdmin/register),
App.tsx (AdminRoute guard), Sidebar (admin link), api.ts (XHR header).

Security findings addressed: SEC-01, SEC-02, SEC-03, SEC-04, SEC-05,
SEC-06, SEC-07, SEC-08, SEC-12, SEC-13, SEC-15.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:06:25 +08:00
7f0ae0b6ef Stage 6 Phase 0-1: Foundation — Switch component, new types, useAuth rewrite, useSettings cleanup
- Create switch.tsx: pure Tailwind Switch toggle, shadcn/ui pattern (forwardRef, cn(), role=switch, aria-checked)
- types/index.ts: extend Settings with all ntfy fields; add LoginSuccessResponse, LoginMfaRequiredResponse, LoginResponse discriminated union, TotpSetupResponse
- useAuth.ts: rewrite with username/password login, ephemeral mfaToken state, totpVerifyMutation, mfaRequired boolean, full mutation exports
- useSettings.ts: remove changePinMutation logic; keep deprecated changePin/isChangingPin stubs for compile compatibility until SettingsPage is rewritten in Phase 3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 04:02:55 +08:00
1f6519635f Initial commit 2026-02-15 16:13:41 +08:00