- W-01: Add date_of_birth validators to RegisterRequest and ProfileUpdate
(reject future dates and years before 1900)
- W-05: Replace .toISOString().slice() with local date formatting for
DatePicker max prop on registration form
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Custom date-picker.tsx with date/datetime modes, portal popup with
month/year dropdowns, min/max constraints, and hidden input for form
validation. Replaces all 10 native <input type="date"> and
<input type="datetime-local"> across LockScreen, SettingsPage,
PersonForm, TodoForm, TodoDetailPanel, TaskForm, TaskDetailPanel,
ProjectForm, ReminderForm, and ReminderDetailPanel. Adds Chromium
calendar icon invert CSS fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>
- 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>
After a failed login, the browser's password manager fires onChange
events on the username/password inputs (clearing or resetting them).
The onChange handlers were calling setLoginError(null), which wiped
the error alert immediately after it appeared.
Fix: remove setLoginError(null) from input onChange handlers. The
error now clears at the start of the next submit attempt via the
existing setLoginError(null) in handleCredentialSubmit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>
- backend: add POST /auth/totp/enforce-setup and /auth/totp/enforce-confirm
endpoints that operate on mfa_enforce_token (not session cookie), generate
TOTP secret/QR/backup codes, verify confirmation code, enable TOTP, clear
mfa_enforce_pending flag, and issue a full session cookie
- frontend: expand LockScreen to five modes — login, first-run setup,
open registration, TOTP challenge, MFA enforcement setup (QR -> verify ->
backup codes), and forced password change; all modes share AmbientBackground
and the existing card layout; registration visible only when
authStatus.registration_open is true
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove instant invalid:ring/border from Input component (was showing
red outline on empty required fields before any interaction)
- Add CSS rule: form[data-submitted] input:invalid shows red border
- Add global submit listener in main.tsx that sets data-submitted on forms
- Add required prop to Labels missing asterisks: PersonForm (First Name),
LocationForm (Location Name), CalendarForm (Name), LockScreen
(Username, Password, Confirm Password)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- C3: Register User, UserSession, NtfySent, TOTPUsage, BackupCode in models/__init__.py
- C4: Enforce settings.user_id NOT NULL after backfill in migration 023, update model
- W4: Rename misleading current_user → current_settings in dashboard.py
- W5: Match NtfySettingsSection initial state defaults to backend (true/1/2)
- W8: Clear lockout banner on username/password input change in LockScreen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend:
- Add rate limiting to login (5 attempts / 5 min window)
- Add secure flag to session cookies with helper function
- Add PIN min-length validation via Pydantic field_validator
- Fix naive datetime usage in todos.py (datetime.now() not UTC)
- Disable SQLAlchemy echo in production
- Remove auto-commit from get_db to prevent double commits
- Add lower bound filter to upcoming events query
- Add SECRET_KEY default warning on startup
- Remove create_all from lifespan (Alembic handles migrations)
Frontend:
- Fix ReminderForm remind_at slice for datetime-local input
- Add window.confirm() dialogs on all destructive actions
- Redirect authenticated users away from login screen
- Replace error: any with getErrorMessage helper in LockScreen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>