Skip to content

feat(02): PIN security and reauth - #44

Open
tpaulshippy wants to merge 7 commits into
mainfrom
feature/roadmap-02-pin-reauth
Open

feat(02): PIN security and reauth#44
tpaulshippy wants to merge 7 commits into
mainfrom
feature/roadmap-02-pin-reauth

Conversation

@tpaulshippy

@tpaulshippy tpaulshippy commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Roadmap 02: PIN Security & Reauth

Implements #54.

What changed

  • Backend: PIN hashing (bcrypt), lockout (5 failures → 15-min lock), POST /api/auth/reauthenticate returning time-limited parent session token, ParentReauthRequired permission on mutations
  • Frontend: PinWrapper keypad UI, auto-attach X-Parent-Reauth header on unsafe methods, lockout display, PIN set/change flows
  • Tests: 117 backend (35 new), 67 frontend (16 new), e2e walkthrough

Evidence

PIN Reauth screenshot

https://github.com/tpaulshippy/bots/raw/feature/roadmap-02-pin-reauth/evidence/pr44-pin-reauth.mp4

tpaulshippy and others added 6 commits August 25, 2026 11:27
Implements the backend half of docs/roadmap/02-pin-security-and-reauth.md:

- UserAccount: pin_hash + lockout fields replace plaintext integer pin
  (0039 adds fields, 0040 hashes legacy pins, 0041 drops the column).
- GET /api/user now returns hasPin/cost/timezone and never exposes the
  PIN or its hash.
- POST /api/user validates 4-8 digit PINs; changes require currentPin
  plus a valid X-Parent-Reauth session; first set needs neither.
- New POST /api/auth/reauthenticate verifies the PIN and returns a
  15-minute signed parent capability token; wrong attempts report
  remainingAttempts, with 423 lockout after 5 failures for 15 minutes.
- Teen-delegated tokens are always denied reauthentication (403).
- Bot/profile writes and account deletion require the reauth header;
  reads stay open for kid paths.
- seed_e2e_pin_reauth management command for Detox e2e runs.

Tests: bots/tests/test_pin_reauth.py (hash-at-rest, redaction,
reauth success/failure/lockout/unlock, teen denial, mutation gating,
legacy-pin data migration).

Refs: docs/roadmap/02-pin-security-and-reauth.md
Implements the frontend half of docs/roadmap/02-pin-security-and-reauth.md:

- pinStorage.ts: no more plaintext @user_pin (legacy key is scrubbed on
  login); caches only the hasPin flag plus an in-memory parent session
  token/expiry from POST /auth/reauthenticate.
- apiClient.ts: automatically sends X-Parent-Reauth on unsafe methods
  when a parent session exists.
- PinWrapper.tsx: numeric keypad calls the real reauthenticate endpoint,
  surfaces remaining attempts and lockout errors, and re-locks via an
  AppState foreground check when the session expires.
- setPin.tsx: current/new/confirm fields, 4-8 digit validation, error
  surfacing; change requires current PIN per API contract.
- settings.tsx: blocking "Set a PIN to protect parent controls" card
  replaces the open menu when hasPin is false; PinWrapper now gates the
  whole screen including the usage bar; uses new cost/hasPin fields.
- login.tsx: removes the client-side cached-PIN gate and dead
  attemptReauthWithPin token flow; refreshes the hasPin flag instead.
- testIDs added for all new interactive elements (+ drawer navigation).
- Fixes pre-existing typecheck errors in mocks/tests so tsc is green.

Tests: pinStorage, account API and PinWrapper suites added.

Refs: docs/roadmap/02-pin-security-and-reauth.md
Adds front/e2e/02-pin-reauth.e2e.js modeled on chatImageUpload.e2e.js:
drives the keypad gate, remaining-attempts messaging, successful
unlock, Set Pin confirm validation, and server-side lockout against a
seeded backend (seed_e2e_pin_reauth; header comment documents seeding,
env, and the run command). Adds a testID to the shared header back
button so the flow can reach the drawer.

Refs: docs/roadmap/02-pin-security-and-reauth.md
- Backend: remove unused PIN_LOCKOUT_MINUTES import, fix I001 ordering
- Frontend: add jest globals to eslint config
tpaulshippy pushed a commit that referenced this pull request Aug 26, 2026
Screenshot now shows the Set PIN screen with New PIN and Confirm fields.
Screenshot now shows the Set PIN screen with New PIN and Confirm fields.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant