Skip to content

feat(auth): #28 OAuth messaging password β€” modal setup mode + unlock polish#84

Merged
TortoiseWolfe merged 4 commits intomainfrom
feat/oauth-messaging-password-28
May 7, 2026
Merged

feat(auth): #28 OAuth messaging password β€” modal setup mode + unlock polish#84
TortoiseWolfe merged 4 commits intomainfrom
feat/oauth-messaging-password-28

Conversation

@TortoiseWolfe
Copy link
Copy Markdown
Owner

Closes #28.

Summary

OAuth users (Google/GitHub) now see ReAuthModal in setup mode when they first land on /messages without encryption keys, instead of being redirected to the full-page form. The page at /messages/setup is preserved as a deep-link fallback. US-2 polish + provider badge + clearer "separate from your $provider login" subtext per wireframe 02.

First feature to walk the v1.0.2 cascade as a hard gate end-to-end.

What changed

New helper β€” src/lib/messaging/welcome/send-welcome-message.ts extracts the welcome-message dispatch from app/messages/setup/page.tsx:133-149 so both modal-setup and page-setup share one implementation.

ReAuthModal extension β€” src/components/auth/ReAuthModal/ReAuthModal.tsx grows a mode: 'unlock' | 'setup' state. Mode is decided at open time by keyManagementService.hasKeys() for OAuth users:

  • setup: password + confirm fields, "Create a Messaging Password" title, lossless-recovery warning, calls initializeKeys, dispatches welcome message
  • unlock: existing single-password flow, calls deriveKeys (unchanged behavior)

Provider badge ("Signed in via Google" / "Signed in via GitHub") renders in both modes when isOAuthUser(user). Unlock copy for OAuth users now explicitly differentiates the messaging password from the OAuth login.

EncryptionKeyGate β€” flips the no-keys redirect: OAuth users get setNeedsReAuth(true) (modal opens in setup mode); email users still redirect to /messages/setup so the browser's password manager benefits from a real <form> context.

Tests β€” 7 new unit tests (RED β†’ GREEN per Constitution II), full unit suite stays at 3261/3261 green. New Playwright spec at tests/e2e/messaging/oauth-setup-modal.spec.ts runs the US-3 email-user regression in CI; US-1/US-2 OAuth scenarios are .skip()'d pending an OAuth test fixture (documented inline).

Cascade artifacts (constitution v1.0.2)

  • features/auth-oauth/013-oauth-messaging-password/spec.md β€” Clarifications session 2026-05-06 (5 Qβ†’A bullets), FR-018..022 added
  • features/auth-oauth/013-oauth-messaging-password/plan.md β€” Constitution Check pass on all 6 principles
  • features/auth-oauth/013-oauth-messaging-password/tasks.md β€” 19 tasks across 6 phases
  • features/auth-oauth/013-oauth-messaging-password/analysis.md β€” FR-to-task traceability matrix, file-path alignment, all 6 categories PASS
  • features/auth-oauth/013-oauth-messaging-password/checklists/implementation-readiness.md β€” plan-readiness validation
  • Wireframes 01 (setup) + 02 (unlock) β€” PASS validate.py v5.4

Test plan

  • Full unit suite passes (3261/3261)
  • Type-check clean
  • Lint clean
  • Pre-push hooks all green
  • Wireframe validate.py still PASS for both SVGs
  • CI: full E2E sharded across chromium-msg / firefox-msg / webkit-msg includes the US-3 regression
  • CI: pa11y AAA still green on the 4 audited URLs (none changed by this feature, but the gate stays honest)
  • Manual: OAuth Google sign-in β†’ /messages β†’ modal in setup mode β†’ submit β†’ keys initialize, inbox visible (post-merge smoke; OAuth flows can't be automated without dedicated fixtures)
  • Manual: OAuth GitHub sign-in β†’ same flow
  • Manual: returning OAuth user with cleared IndexedDB β†’ modal in unlock mode with provider badge + subtext

Refs

πŸ€– Generated with Claude Code

TurtleWolfe and others added 4 commits May 6, 2026 22:26
…tions

Phase A β€” wireframe gate:
- validate.py v5.4 returns 0 errors on both 01-oauth-password-setup.svg
  and 02-oauth-password-unlock.svg (annotation text now uses #1f2937 /
  #374151 instead of #6b7280 the 2026-01-16 review flagged).
- Refreshed both .issues.md files to "Status: PASSED" with history
  table showing the v5.0 fail β†’ v5.4 pass transition.
- Added ## UI Mockup section to spec.md per Constitution v1.0.2
  Principle III, linking both wireframes with sign-off date.

Phase B β€” clarifications encoded:
- Updated the stale Implementation Status block (was "Not Started, no
  OAuth modal found"; now reflects the partial-ship reality:
  isOAuthUser/getOAuthProvider already shipped, /messages/setup
  already shows password+confirm, ReAuthModal already
  OAuth-aware on unlock).
- Added ## Clarifications / ### Session 2026-05-06 with 5 Q→A bullets
  per /speckit.clarify convention: setup-mode trigger, no persistence
  of messaging password, no recovery flow, provider badge UX, and
  modal-vs-page primary path.
- Added FR-018 through FR-022 encoding the clarification answers.

Unblocks /speckit.plan via the harness shipped in PR #83.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Continues the v1.0.2 cascade for OAuth Messaging Password (issue #28).

- checklists/implementation-readiness.md: "unit-tests-for-English"
  validation that plan.md and spec.md together carry enough info to
  drive /speckit.tasks and /speckit.implement without re-asking
  decisions. All 7 sections PASS.

- tasks.md: 19 tasks ordered T001..T019 across 6 phases. US-1 (T002-T008)
  ships modal setup mode + EncryptionKeyGate redirect-flip. US-2
  (T009-T012) layers in unlock-mode polish + provider badge. US-3
  (T013-T015) is regression-only. T016-T019 polish (Storybook stories,
  AAA re-verify, manual smoke + screenshots, wireframe re-validate).
  RED tests authored before GREEN per Constitution II.

- analysis.md: cross-artifact consistency check. FR-to-task traceability
  matrix shows every one of FR-001..022 has a task or already-shipped
  marker. User stories independently testable per their checkpoints.
  Plan-vs-tasks file paths align. Constitution v1.0.2 compliance
  verified across all 6 principles. Flags scripts/constitution-check.py
  layout bug as a separate one-liner.

Recommendation in analysis: proceed to /speckit.implement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…olish

Closes the gap where OAuth users (Google/GitHub) had no in-modal way to
create the messaging password used to derive ECDH keys for E2E messaging.
Today they were redirected to /messages/setup; per FR-021 they now see
the modal in setup mode inline. The setup page is preserved as a deep-
link fallback per FR-022.

Implementation per features/auth-oauth/013-oauth-messaging-password/plan.md:

- New helper: src/lib/messaging/welcome/send-welcome-message.ts.
  Extracted from app/messages/setup/page.tsx so both modal-setup and
  page-setup share one welcome-message dispatch (cleaner long-term per
  memory rule). setup/page.tsx now imports and calls the helper.

- ReAuthModal grows a 'mode' state ('unlock' | 'setup') decided at open
  time by inspecting hasKeys() when an OAuth user opens the modal:
    setup mode  β†’ password + confirm fields, "Create a Messaging
                  Password" title, "Create Messaging Password" submit
                  button, lossless-recovery warning copy, calls
                  initializeKeys + dispatches welcome message
    unlock mode β†’ existing single-password flow, calls deriveKeys
                  (unchanged behavior)
  Provider badge ("Signed in via Google" / "Signed in via GitHub")
  renders in both modes when isOAuthUser(user) is true (FR-020).
  Unlock-mode description for OAuth users now explicitly says "This is
  separate from your <Provider> login" per US-2 wireframe 02.

- EncryptionKeyGate.tsx flips the no-keys redirect: OAuth users get
  setNeedsReAuth(true) (modal opens in setup mode); email users still
  get the full-page redirect to /messages/setup so the browser's
  password manager can offer auto-fill on a real <form>.

- ReAuthModal stories updated with documentation describing both modes.
  Storybook stories rely on real AuthProvider; the per-mode behavioral
  differentiation is covered by the unit tests rather than per-story
  mocks.

7 new RED tests authored before implementation per Constitution II:
  - setup-mode title renders for OAuth user without keys
  - confirm-password field renders in setup mode
  - submit calls initializeKeys (not deriveKeys) in setup mode
  - mismatched passwords block submit + show alert
  - provider badge renders with provider name in setup mode
  - provider badge does NOT render for email users (FR-014)
  - existing OAuth-with-keys regex updated for new copy

Full unit suite: 3261/3261 passing. type-check + lint clean.
Manual browser smoke + Playwright E2E spec + post-implement screenshots
follow in a subsequent commit (T013/T015/T018/T019 in tasks.md).

Refs: #28
Spec: features/auth-oauth/013-oauth-messaging-password/spec.md
Plan: features/auth-oauth/013-oauth-messaging-password/plan.md
Tasks: features/auth-oauth/013-oauth-messaging-password/tasks.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three test slots in tests/e2e/messaging/oauth-setup-modal.spec.ts:

US-3 (runs in CI across chromium-msg, firefox-msg, webkit-msg, plus
mobile profiles) β€” email user lands on /messages with cleared
IndexedDB, ReAuthModal opens in unlock mode, asserts byte-identical
pre-feature copy: "Enter Your Messaging Password" title, "Password"
label (NOT "Messaging Password"), "Unlock Messages" submit button,
no provider badge. Pins FR-013/FR-014/FR-020 invariants for the email
flow.

US-1 + US-2 (.skip()'d) β€” OAuth user setup mode and unlock-with-badge
flows. Skipped because the repo does not yet have an OAuth test
fixture (no Google/GitHub test app credentials in CI). The unit tests
in src/components/auth/ReAuthModal/ReAuthModal.test.tsx carry the
behavioral coverage; manual smoke at T018 in tasks.md exercises the
real OAuth flow end-to-end. Spec body documents the future
implementation path (Supabase admin API to flip app_metadata.provider
+ teardown).

Refs: #28
Tasks: T003, T009, T013 (US-3 portion only β€” OAuth portions deferred)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TortoiseWolfe TortoiseWolfe merged commit cedbda9 into main May 7, 2026
29 checks passed
@TortoiseWolfe TortoiseWolfe deleted the feat/oauth-messaging-password-28 branch May 7, 2026 01:33
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.

[Gap-Audit] 013 OAuth Messaging Password: implement modal-based password setup + unlock for OAuth users

2 participants