Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ Both the legacy member synchronization function and admin role callable grant cl

AUTH-001A now rejects unverified targets at the two existing role-grant endpoints. Do not duplicate that merged slice. It is not proof that Firebase production runs the new code, and it does not complete the remaining parent requirements below.

Adjacent account-email recovery is represented by canonical live parent [#120](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/120), not by a new AUTH-001 role-grant child. AUTH-MAIL-002A [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) merged the account-creation/request split as `46557c7` but is not published. AUTH-MAIL-002B [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153) owns the truthful My Account resend result and browser cooldown. AUTH-MAIL-002C remains proposed for password-reset/action-code recovery. None grants membership or proves provider delivery.

### Scope

- Reject member/admin/capability grants when the target Firebase user's email is unverified.
Expand Down
3 changes: 3 additions & 0 deletions GITHUB_ISSUE_SLICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de
| AUTH-003B | proposed; AUTH-003A | Apply capabilities to server functions and Firestore projections/rules. | Every capability/action pair has allow/deny tests. |
| AUTH-003C | owner_action; AUTH-003B | Enforce privileged MFA, recent-auth thresholds, revocation, and break-glass/last-admin policy. | Staging failure/success demonstrations and private owner inventory. |
| AUTH-003D | proposed; AUTH-003B/C | Migrate admin UI/routes/token refresh, prove parity, then remove legacy alias. | Route/capability/demotion tests and staged operator sign-off. |
| AUTH-MAIL-002A | source merged through [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) / PR #148 as `46557c7`; website/provider/live unproven | Separate account creation from verification-request acceptance/failure without exposing provider details. | Mocked create/accepted/unavailable tests; exact website and provider evidence stay separate. |
| AUTH-MAIL-002B | source/tests tracked in live [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153); A, #118 source; not published | Make My Account resend report request acceptance or generic unavailability, block rapid repeats, and show the same 60-second browser cooldown after either result. | No-network double-click, timer/retry, remount, UID-change, redaction, and accessibility tests; website/provider/live states remain separate. |
| AUTH-MAIL-002C | proposed; A/B | Give password-reset and Auth action-code flows generic, enumeration-resistant success/expired/used/wrong-account/offline/provider-unavailable states. | No-network state/collision/navigation/accessibility tests with identical public recovery results where required. |
| OAUTH-001A | proposed; represented within canonical live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88); SEC-001, AUTH-003B | Implement server-only, transaction/version-safe Strava token refresh so rotated refresh tokens cannot be lost. Do not publish a duplicate; claim/split only through #88. | Concurrent refresh/retry/conflict emulator tests. |
| OAUTH-001B | owner_action; represented within canonical live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88); OAUTH-001A | Minimize scopes, implement disconnect/provider revocation, record encryption decision, and add access/refresh/revoke audit. Do not publish a duplicate; claim/split only through #88. | Test-provider revoke/refresh evidence and approved threat decision. |
| OAUTH-001C | proposed; represented within canonical live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88); #99 | Create a server-safe Strava callback handoff that keeps `code`/`state` out of third parties while allowing App Check-protected `stravaExchangeCode`; retain state verification and server-only token exchange. Do not publish a duplicate; claim/split only through #88. | Encoded/case/trailing callback tests, wrong/replayed state denial, missing/invalid/valid App Check tests, and test-provider-only exchange evidence. |
Expand Down
12 changes: 8 additions & 4 deletions OPERATIONS_RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,27 +578,29 @@ Required release order:

If the website arrives before `ensureMemberProfile` or the matching Rules, the intended safe result is a temporary-unavailable message with Edit hidden. Roll back the website or safely roll forward the reviewed backend. Never release the callable while the old signup trigger remains: that trigger can overwrite a recovered profile or replace claims. Deploy the two revised Functions together, or stop and roll back the partial backend release. Do not create, delete, overwrite, or re-role a production account in Firebase Console.

### Registration email outcome — SOURCE ONLY, NOT LIVE
### Account verification email outcomes — SOURCE ONLY, NOT LIVE

AUTH-MAIL-002A [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) separates three transitions:

1. Account creation fails: reject the operation and show one generic create-account error.
2. Account creation succeeds and Firebase accepts the verification request: preserve the signed-in account and report `accepted`; do not claim delivery.
3. Account creation succeeds and Firebase rejects the verification request: preserve the signed-in account, report `unavailable`, emit only the fixed `email_verification_failed` diagnostic, and offer My Account as an inspection path. If My Account is unavailable, stop and keep the account. Do not advertise the existing resend action as proven.

The source and tests do not change Firebase Auth, email templates, DNS, sender branding, or mailbox/provider settings. They do not send a network request in tests. Do not test this flow with a production mailbox or a real member.
AUTH-MAIL-002B [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153) adds the My Account source state machine: idle, requesting, accepted, or unavailable. Request acceptance is not delivery. A synchronous guard blocks rapid duplicate activation. Both completed outcomes start the same visible 60-second wait, then allow one retry. Remounting the page or changing the signed-in account resets this browser-only timer; Firebase can still throttle requests. The timer is not server authorization or abuse protection.

The source and tests do not change Firebase Auth, email templates, DNS, sender branding, or mailbox/provider settings. Tests use a mocked request and no network. Do not test this flow with a production mailbox or a real member.

Release and evidence order:

1. Review the exact source and synthetic mocked tests.
2. Merge the exact reviewed commit.
3. Publish that frontend revision only through the approved website release path.
4. Verify the hosting record and the exact `runmprc.com` revision separately.
5. Before calling the linked recovery usable, verify the exact [#118](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/118) Rules, Functions, and profile page plus #120's truthful resend result/cooldown child. An unavailable My Account page is a stop result.
5. Before calling the linked recovery usable, verify the exact [#118](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/118) Rules, Functions, and profile page plus [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153)'s truthful resend result/cooldown. An unavailable My Account page is a stop result.
6. Use an isolated staging Firebase project and approved safe email sink before any end-to-end test.
7. Record request acceptance separately from message delivery, Spam placement, and sender configuration.
8. Keep provider/DNS/template work under [#119](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/119).
9. Keep profile-independent resend/cooldown/session recovery and password/action-code recovery under parent [#120](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/120). The current Resend success message is not delivery proof.
9. Keep password/action-code recovery in AUTH-MAIL-002C under parent [#120](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/120). #153 changes only the browser resend result and usability timer; it does not prove provider delivery or impose a durable rate limit.

Rollback is a reviewed frontend revert or safe roll-forward. Do not delete or recreate an Auth account because the verification request failed.

Expand All @@ -624,4 +626,6 @@ For OBS-001A3 [#142](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/142),

For AUTH-MAIL-002A [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145), **source/tests** means mocked account-create and email-request outcomes plus fixed-output canary checks; **merge** means the resulting `main` SHA; **website publication** and **`runmprc.com` revision verification** are separate; **Firebase/provider configuration** is unchanged; and **live delivery** remains unverified. An `accepted` result proves only that Firebase accepted the request. It does not prove Inbox delivery, sender reputation, or Spam placement.

For AUTH-MAIL-002B [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153), **source/tests** means mocked resend outcomes, double-click/ref/timer/UID cleanup, fixed-output canaries, and accessible status/action checks; **merge** is a separate `main` SHA; **website publication** and **`runmprc.com` revision verification** are separate; **Firebase/provider configuration** is unchanged; and **delivery** remains unverified. The 60-second display resets with browser component/session changes and is not provider throttling proof.

Provider configuration, production secrets, Netlify publication, Firebase deployment, and live behavior always require separate dated evidence. Local tests and a green GitHub summary do not prove them.
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ These entries are implementation evidence, not a production risk-acceptance deci
| Part of RISK-035 | The Jest environment is repaired, the stale frontend smoke test is replaced, #124 adds the complete frontend suite as a named blocking hosted step, and #126 adds the standalone SPA callback suite as its own named blocking step. | Keep exact hosted evidence on #124/#126; make lint non-mutating/fail-closed, prove required branch checks, and add the broader TEST-001 suite. |
| RISK-026, RISK-036 | #135 merged through PR #138 as `9eafab1217aff7058c42240aaba72d7b93f8ed24`, replacing automatic frontend-first/fail-open GitHub deployment with a tested manual exact-current-commit gate. Post-merge staging and synthetic production probes failed closed before authentication or mutation, and published neither Firebase nor Pages. | #133 must configure protected environments and least-privilege OIDC/WIF; #136 must prove staged/target deployment and clearing/readback of the existing Pages `runmprc.com` claim; a protected WEB-001 child must establish Netlify publication and rollback. No Firebase, Pages, live-host, or provider-setting change is proven by source/static tests alone. |
| RISK-039 | The #118 source slice uses an empty authenticated callable request, bounded Firebase Auth identity fields, one transactional create-only helper shared with signup, constant responses, generic failures, and a UI that hides Edit until setup and the Rules-protected read succeed. Signup and recovery never change custom claims; browser profile creation remains denied. | Source review/merge is not deployment. Under #105, deploy the exact #100 Rules plus both `createMemberOnSignUp` and `ensureMemberProfile` before the website, prove App Check policy, use a synthetic staged account, verify rollback, then record website, Function, Rules, and live behavior separately. Never repair a real profile manually. |
| RISK-040 | AUTH-MAIL-002A [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) source returns `accepted` or `unavailable` after account creation, keeps the signed-in credential on request failure, and shows a focused generic status with one My Account inspection/stop path. Synthetic tests use mocked Firebase calls and canary values only. | Merge and publish the exact frontend revision, verify `runmprc.com` separately, and keep provider delivery/Spam/DNS/template evidence under #119. A source result or accepted request does not prove delivery. Do not call My Account recovery usable until #118 is live and #120's resend result/cooldown child is complete; password/action-code recovery also remains open under #120. |
| RISK-040 | AUTH-MAIL-002A [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) merged as `46557c7`: account creation returns `accepted` or `unavailable` without exposing provider details, but that website revision is not published. AUTH-MAIL-002B [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153) removes My Account's false “sent” claim, adds a synchronous repeat guard, and applies the same 60-second browser cooldown after request acceptance or generic failure. Synthetic tests use mocked Firebase calls and canary values only. | Publish and verify each exact frontend revision separately. #118 profile source is merged but live behavior is unproven. Keep delivery/Spam/DNS/template evidence under #119. An accepted request does not prove delivery, and a browser cooldown can reset on refresh/account change; it is not server abuse protection. Password/action-code recovery remains AUTH-MAIL-002C under #120. |

## 5. Threat model

Expand Down
27 changes: 18 additions & 9 deletions docs/officers/EMERGENCY_AND_RECOVERY.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
| Wrong public text, image, or link | Ask the platform owner for one reviewed revert pull request. |
| Site unavailable or old after a merge | Ask the platform owner to check Netlify and GitHub Pages separately; do not change DNS. |
| Login or member access is wrong | Stop role requests and contact the identity/platform owners. |
| Account exists but verification email request failed | Ask the member to stop resending. Record only the time, public `/login` URL, and plain status. Do not ask for the email address, password, code, action link, mailbox access, or screenshot. |
| Account exists but a verification email request is unavailable | Ask the member to stop resending. Record only the time, route path `/login` or `/account`, and plain status. Omit all query/fragment text. Do not ask for the email address, password, code, action link, mailbox access, or screenshot. |
| Profile Save says permission is missing | Ask the member to stop, sign out, and wait. Open a new redacted incident through [Request a change](./REQUEST_A_CHANGE.md). Do not edit the database, delete the login, recreate the account, or grant a role. |
| Private member information is visible | Contact platform and privacy owners immediately; they choose containment through an approved service procedure. Do not change permissions yourself. |
| Unexpected payment, refund, order, or signup | Contact treasurer and platform owners. The new source guard is not deployed and its officer control is NOT AVAILABLE YET. Do not test with real money. |
Expand Down Expand Up @@ -75,30 +75,39 @@ In words: stop, record only safe public facts, contact both owners, and wait for

**Escalation:** treasurer plus platform owner; add the security owner for an unauthorized command and the privacy owner if member information appeared.

### Account created but verification request failed
### Verification email request unavailable

**Status:** the truthful two-result screen in [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) is **SOURCE ONLY, NOT LIVE** until its exact website revision is published and verified.
**Status:** the truthful create-account result in [#145](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/145) and My Account resend/countdown in [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153) are **SOURCE ONLY, NOT LIVE** until the exact website revision is published and verified. #118 profile source is merged, but its backend and live behavior are also unproven.

**Purpose:** preserve the new account and prevent repeated email requests or unsafe account repair.

**Approver:** membership lead plus identity/platform owner.

**Prerequisites:** the time, the public `/login` address, and the exact plain status with all private details omitted. My Account recovery is not proven until [#118](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/118) records the exact Rules, Functions, and profile page, and [#120](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/120) records a truthful resend result plus cooldown. Without both proofs, use only these stop-and-escalate steps.
**Prerequisites:** the time, the public route path `/login` or `/account`, and the exact plain status with all private details omitted. Do not copy anything after `?` or `#` in the browser address. My Account recovery is not proven until [#118](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/118) has exact Rules/Functions/profile-page deployment evidence and [#153](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/153) has exact website evidence. Without both proofs, use only the stop-and-escalate steps below.

1. Ask the member to stop clicking Create account or Resend.
1. Ask the member to stop clicking Create account or the email-request action.
2. Do not ask for their email address, password, code, action link, mailbox access, or screenshot.
3. Record only `account created; verification request did not finish`, the time, and the public `/login` URL.
3. Record only the exact plain `accepted` or `unavailable` status, the time, and the route path `/login` or `/account`. Omit all query text after `?` and all fragment text after `#`.
4. Ask the member to keep the account. Do not delete or recreate it.
5. Open a redacted incident through [Request a change](./REQUEST_A_CHANGE.md).
6. Contact the identity/platform owner.
7. Add the communications owner if accepted messages land in Spam or never arrive.
8. Wait for separate website and provider evidence before saying the problem is fixed.

**Expected result:** no private data is copied, no duplicate account is created, and the owner can separate a website request failure from a provider-delivery problem.
After the exact #118 and #153 live proofs exist:

**Stop conditions:** repeated resend attempts, real mailbox tests, direct Firebase Console changes, account deletion/recreation, role changes, or a request for private details.
1. Ask the member to choose **Request another verification email** once.
2. Ask them to wait through the full visible 60-second countdown.
3. If accepted, ask them to check Inbox and Spam once. Accepted does not mean delivered.
4. If unavailable, wait through the countdown and try once more.
5. If the second request is unavailable, stop and escalate.
6. Do not refresh to bypass the browser countdown. It can reset, but Firebase can still throttle requests.

**Success proof:** the exact website revision displays the correct generic status in an approved synthetic check. Sender branding, delivery, and Spam placement need separate private evidence under [#119](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/119).
**Expected result:** no private data is copied, no duplicate account is created, rapid repeats are blocked, and the owner can separate a website request failure from a provider-delivery problem.

**Stop conditions:** more than one retry after the countdown, refresh used to bypass it, real mailbox tests, direct Firebase Console changes, account deletion/recreation, role changes, or a request for private details.

**Success proof:** the exact website revision displays accepted/unavailable outcomes, the visible countdown, and one later retry in an approved synthetic check. Sender branding, delivery, and Spam placement need separate private evidence under [#119](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/119).

**Undo:** ask for one reviewed frontend revert or safe roll-forward. Never undo this problem by deleting the login account.

Expand Down
Loading
Loading