Skip to content

AUTH-MAIL-002C1 — Make password-reset requests honest and repeat-safe #155

Closed

Description

@daliu

Officer impact: Members get one safe password-reset result that does not reveal whether an account exists and does not promise that email was sent or delivered. A visible wait prevents accidental repeat requests. Officers never ask for an address, password, code, link, screenshot, or mailbox access in a public issue or AI tool.

Officer documentation: Update docs/officers/EMERGENCY_AND_RECOVERY.md and the minimum exact membership/account guide. Keep the revised behavior NOT AVAILABLE YET until the exact website revision is published and verified.

Deployment evidence: None at issue creation. Closure must separate source/tests, merge, website publication, runmprc.com verification, Firebase/provider configuration, and production behavior. Tests are synthetic and make no network or email request.

Parent and dependencies

Focused AUTH-MAIL-002C1 child of #120. #145 completed registration truth. #153 completed verification resend truth. #99, #103, and #111 provide isolated tests, callback safety, and redacted diagnostics. #119 owns sender/provider configuration. #105/#133/#136 own protected publication. This child does not implement incoming Firebase action-code handling; that remains a separate C2 slice.

Problem

The current Forgot password path always says a link is on its way after the provider call finishes, even when that call failed and was intentionally swallowed. That protects against account enumeration but makes an unproven delivery promise. It also has no immediate in-flight guard, no visible cooldown, and no tested retry or cleanup state.

Atomic outcome

Replace the reset flag with one small state machine:

  • Idle: Forgot password? is available after a locally entered address.
  • Requesting: one disabled large action; rapid repeat activation makes one provider call.
  • Finished: the same fixed public result after provider success or failure. It says email delivery cannot be confirmed and gives safe Inbox/Spam/wait guidance without claiming an account or message exists.
  • Cooldown: both provider outcomes show the same 60-second countdown, then allow one retry.

The cooldown is a browser usability guard, not durable abuse protection. Refresh/remount may reset it; Firebase throttling can still apply.

Invariants

  • Success, missing-account, throttled, offline, malformed-provider, and provider-failure outcomes use the same public result after a request is attempted.
  • Never say sent, delivered, on its way, account exists, account missing, provider accepted, or provider failed.
  • Missing local input may ask the member to enter an address; it does not call Firebase.
  • A synchronous in-flight guard prevents rapid duplicate calls before React state updates.
  • Success and failure use the same visible cooldown and retry transition.
  • No caught provider object, address, UID, code, token, link, or query/fragment enters UI, logs, analytics, screenshots, or test output.
  • Reset does not sign in, create an account, change membership/roles/dues, navigate, or edit Firestore.
  • Tests use synthetic mocks only and make no network or production-mail request.

Exact source boundary

  • src/pages/login/LoginForm.jsx
  • src/pages/login/LoginForm.test.jsx
  • src/pages/login/LoginForm.css only for scoped large/focus-visible reset controls
  • docs/officers/EMERGENCY_AND_RECOVERY.md
  • docs/officers/EVENTS_SHOP_MEMBERS.md only if needed for one cross-reference
  • minimum exact status hunks in SECURITY.md, OPERATIONS_RUNBOOK.md, GITHUB_ISSUES.md, and GITHUB_ISSUE_SLICES.md

Post a scope amendment before editing Identity runtime, Firebase configuration, workflows, packages, Functions, Rules, provider settings, action-code routes, or any other file.

Acceptance criteria

  • Provider success and failure render byte-for-byte-equivalent public result content and accessibility state.
  • Result copy is enumeration-resistant and makes no send/delivery/account-existence promise.
  • Missing local input makes no provider call and gives one clear next action.
  • A synchronous guard proves rapid repeat activation makes exactly one provider call.
  • Both outcomes start the same visible 60-second cooldown and recover to one retry action.
  • Timer cleanup/remount tests prove no stuck state or post-unmount update.
  • Sign-in/registration state, intended return route, and input remain intact; reset makes no unrelated service call.
  • Screen-reader announcement, keyboard activation, focus visibility, disabled state, and at least a 44px action target are tested.
  • Focused and full frontend, SPA, lint/build, diff/link/sensitive-pattern checks pass.
  • Officer guidance states purpose, approver, prerequisites, steps, expected result, stop conditions, proof, undo, and escalation.

Excluded

  • DNS, Firebase templates, custom sender, SMTP/mail provider, production delivery tests (AUTH-MAIL-001 — Brand and authenticate Firebase Authentication email #119).
  • Firebase action-code landing states such as expired/used/already-complete/wrong account (future AUTH-MAIL-002C2).
  • Google sign-in/linking, membership/payment state, Firebase Rules/Functions, workflows, or production data.

Claim

CLAIMED by /root at creation on branch codex/issue-155-password-reset-truth from exact main 23bca8c. One atomic source/test outcome; external release remains separate.

Activity

  1. self-assigned this
    on Jul 13, 2026
  2. added
    bugSomething isn't working
    priority:P1High-priority follow-up
    area:authAuthentication and authorization
    area:firebaseFirebase services and data
    area:webWeb application and hosting
    size:SSmall focused issue
    status:in-progressClaimed and actively being implemented
    type:testingTest infrastructure and quality gates
    on Jul 13, 2026
  3. daliu commented on Jul 13, 2026

    @daliu
    ContributorAuthor

    CLAIMED by /root at 2026-07-13T13:12:19Z; branch codex/issue-155-password-reset-truth; exact base 23bca8c. Active ownership is limited to the nine paths listed in the issue. No Identity runtime, Firebase, Functions, Rules, workflows, packages, or provider settings are included.

  4. daliu commented on Jul 13, 2026

    @daliu
    ContributorAuthor

    EXTRACTION FINISHED by /root at 2026-07-13T13:30:43Z. Exact staged SHA-256: 2a1acc2e9ee0e6c476558697d74853829047c1653ccb238e35d6620cfdb15972 on unchanged base 23bca8c. Exactly nine owned paths; no unstaged delta. Node 20 proof: focused LoginForm 28/28, full frontend 9 suites/130 tests, SPA 10/10, strict changed-file lint, optimized build, diff/link/sensitive-pattern checks all pass. Three independent exact-hash reviews APPROVED after fixes for the scoped hidden announcement and expired/clock-shift timer recovery. No network, Firebase, mailbox, member data, provider setting, or deployment mutation occurred.

  5. daliu commented on Jul 13, 2026

    @daliu
    ContributorAuthor

    COMPLETED / CLAIM RELEASED — 2026-07-13T13:39:26Z

    • Source: PR AUTH-MAIL-002C1: make password-reset requests honest #156 squash-merged to main as 594306fb06d9a6a2a44b5675b305f462f11b877d.
    • Exact-head proof: CI run 29254075796 passed Frontend lint/build (9 suites / 130 tests, SPA 10/10), Functions, and Rules; Netlify preview succeeded.
    • Post-merge proof: exact-main CI run 29254264701 passed all three hosted jobs.
    • Review: three independent reviews approved exact diff hash 2a1acc2e9ee0e6c476558697d74853829047c1653ccb238e35d6620cfdb15972.
    • Protected deployment attempt: run 29254280177 passed exact-main/exact-CI preflight, then failed closed before checkout/build because the required public App Check site key is absent. Website build/artifact, Firebase deployment, and GitHub Pages publication were skipped.
    • Production truth: source and tests are merged. This revision was NOT published by the protected workflow; runmprc.com, Firebase/provider configuration, real mail delivery, and live reset behavior remain unverified. Officer docs therefore remain NOT AVAILABLE YET.
    • Privacy: synthetic tests only; no real account, email, mailbox, action URL, token, or production request was used.

    All issue acceptance boxes are checked for the atomic source outcome. The status:in-progress label and file claim are released. #120 remains open for C2 action-code states and provider/live evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:authAuthentication and authorizationarea:firebaseFirebase services and dataarea:webWeb application and hostingbugSomething isn't workingpriority:P1High-priority follow-upsize:SSmall focused issuetype:reliabilityReliability and recoverytype:testingTest infrastructure and quality gates

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions