bcfebbc9ae
feat(backend): Phase 1 passwordless login — migration, models, toggle endpoints, unlock, delete guard, admin controls
...
- Migration 062: adds users.passwordless_enabled and system_config.allow_passwordless (both default false)
- User model: passwordless_enabled field after must_change_password
- SystemConfig model: allow_passwordless field after enforce_mfa_new_users
- auth.py login(): block passwordless-enabled accounts from password login path (403) with audit log
- auth.py auth_status(): change has_passkeys query to full COUNT, add passkey_count + passwordless_enabled to response
- auth.py get_current_user(): add /api/auth/passkeys/login/begin and /login/complete to lock_exempt set
- passkeys.py: add PasswordlessEnableRequest + PasswordlessDisableRequest schemas
- passkeys.py: PUT /passwordless/enable — verify password, check system config, require >= 2 passkeys, set flag
- passkeys.py: POST /passwordless/disable/begin — generate user-bound challenge for passkey auth ceremony
- passkeys.py: PUT /passwordless/disable — verify passkey auth response, clear flag, update sign count
- passkeys.py: PasskeyLoginCompleteRequest.unlock field — passkey re-auth into locked session without new session
- passkeys.py: delete guard — 409 if passwordless user attempts to drop below 2 passkeys
- schemas/admin.py: add passwordless_enabled to UserListItem + UserDetailResponse; add allow_passwordless to SystemConfigResponse + SystemConfigUpdate; add TogglePasswordlessRequest
- admin.py: PUT /users/{user_id}/passwordless — admin-only disable (enabled=False only), revokes all sessions, audit log
- admin.py: update_system_config handles allow_passwordless field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:15:39 +08:00
3d22568b9c
Add user connections, notification centre, and people integration
...
Implements the full User Connections & Notification Centre feature:
Phase 1 - Database: migrations 039-043 adding umbral_name to users,
profile/social fields to settings, notifications table, connection
request/user_connection tables, and linked_user_id to people.
Phase 2 - Notifications: backend CRUD router + service + 90-day purge,
frontend NotificationsPage with All/Unread filter, bell icon in sidebar
with unread badge polling every 60s.
Phase 3 - Settings: profile fields (phone, mobile, address, company,
job_title), social card with accept_connections toggle and per-field
sharing defaults, umbral name display with CopyableField.
Phase 4 - Connections: timing-safe user search, send/accept/reject flow
with atomic status updates, bidirectional UserConnection + Person records,
in-app + ntfy notifications, per-receiver pending cap, nginx rate limiting.
Phase 5 - People integration: batch-loaded shared profiles (N+1 prevention),
Ghost icon for umbral contacts, Umbral filter pill, split Add Person button,
shared field indicators (synced labels + Lock icons), disabled form inputs
for synced fields on umbral contacts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:10:16 +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
8582b41b03
Add user profile fields + IAM search, email column, detail panel
...
Backend:
- Migration 037: add email, first_name, last_name to users table
- User model: add 3 profile columns
- Admin schemas: extend UserListItem/UserDetailResponse/CreateUserRequest
with profile fields, email validator, name field sanitization
- _create_user_defaults: accept optional preferred_name kwarg
- POST /users: set profile fields, email uniqueness check, IntegrityError guard
- GET /users/{id}: join Settings for preferred_name, include must_change_password/locked_until
Frontend:
- AdminUser/AdminUserDetail types: add profile + detail fields
- useAdmin: add CreateUserPayload profile fields + useAdminUserDetail query
- CreateUserDialog: optional profile section (first/last name, email, preferred name)
- IAMPage: search bar filtering on username/email/name, email column in table,
row click to select user with accent highlight
- UserDetailSection: two-column detail panel (User Info + Security & Permissions)
with inline role editing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:40:20 +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
fbc452a004
Implement Stage 6 Track A: PIN → Username/Password auth migration
...
- New User model (username, argon2id password_hash, totp fields, lockout)
- New UserSession model (DB-backed revocation, replaces in-memory set)
- New services/auth.py: Argon2id hashing, bcrypt→Argon2id upgrade path, URLSafeTimedSerializer session/MFA tokens
- New schemas/auth.py: SetupRequest, LoginRequest, ChangePasswordRequest with OWASP password strength validation
- Full rewrite of routers/auth.py: setup/login/logout/status/change-password with account lockout (10 failures → 30-min, HTTP 423), IP rate limiting retained as outer layer, get_current_user + get_current_settings dependencies replacing get_current_session
- Settings model: drop pin_hash, add user_id FK (nullable for migration)
- Schemas/settings.py: remove SettingsCreate, ChangePinRequest, _validate_pin_length
- Settings router: rewrite to use get_current_user + get_current_settings, preserve ntfy test endpoint
- All 11 consumer routers updated: auth-gate-only routers use get_current_user, routers reading Settings fields use get_current_settings
- config.py: add SESSION_MAX_AGE_DAYS, MFA_TOKEN_MAX_AGE_SECONDS, TOTP_ISSUER
- main.py: import User and UserSession models for Alembic discovery
- requirements.txt: add argon2-cffi>=23.1.0
- Migration 023: create users + user_sessions tables, migrate pin_hash → User row (admin), backfill settings.user_id, drop pin_hash
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 04:12:37 +08:00