Skip to content

AUTH-001A — Reject unverified targets at existing role-grant endpoints #98

Description

@daliu

Problem

The existing admin callable and legacy membership-sync endpoint grant Firebase custom claims by matching an email address, but neither checks the authoritative Firebase Auth userRecord.emailVerified value. An attacker can pre-register a known member address and receive membership access when that address is imported.

Security invariant

Promotion to member or admin is allowed only when Firebase Auth reports userRecord.emailVerified === true. Request fields and Firestore profile mirrors never prove verification. Demotion to unverified remains allowed so operators can remove access safely.

Atomic scope

  • Add one small pure role-grant policy helper.
  • Apply it immediately after authoritative Auth lookup and before any claim or Firestore write in setMemberRole.
  • Apply it before any claim or Firestore write in each updateMemberRole batch item.
  • Preserve existing authorization, App Check, batch bounds, admin-grant exclusion, and self-demotion protection.
  • Return a bounded, non-sensitive failure for an unverified target.
  • Add positive, negative, forged-field, no-side-effect, mixed-batch, and demotion tests using synthetic identities only.

Explicitly out of scope

  • Firestore Rules or privileged server guards.
  • Email-verification profile mirrors or verification triggers.
  • Existing-claim remediation, token revocation, propagation, audit schema, MFA, or capabilities.
  • Replacement of the legacy static API-key endpoint; that remains AUTH-002.
  • Any production identity, provider, or configuration change.

Acceptance criteria

  • Unverified or missing verification state cannot receive member through either endpoint.
  • Unverified target cannot receive admin through the admin callable.
  • Verified targets retain current authorized promotion behavior.
  • A forged request emailVerified value is ignored.
  • Denial happens before setCustomUserClaims and before Firestore profile writes.
  • Demotion to unverified still succeeds for an unverified target.
  • Existing admin self-demotion protection remains intact.
  • Mixed bulk input promotes verified targets only and reports the rejected target without aborting successful items.
  • Bulk endpoint still cannot grant admin.
  • Focused and full Functions tests, lint, Rules tests, diagnostic frontend build, and diff check pass.

Dependencies and file ownership

  • Stable parent: AUTH-001 in GITHUB_ISSUES.md; coordination epic: COMMUNITY-EPIC — Unified member access and community integrations #79.
  • Hard dependencies: none. SEC-001 is recommended for the remaining parent work but is not required for this endpoint-only invariant.
  • Expected files: functions/setMemberRole.js, functions/updatemembers.js, new functions/roleGrantPolicy.js, new functions/roleGrantPolicy.test.js.
  • Do not touch the parallel callback/emulator, Rules, Stripe/payment, package, or root-documentation worktree clusters.

Claim protocol

Do not edit until this issue is assigned and a timestamped CLAIMED comment names the agent and branch. Earliest valid claim wins.

Metadata

Metadata

Assignees

Labels

area:authAuthentication and authorizationarea:firebaseFirebase services and datapriority:P0Launch blocker or urgent security risksize:SSmall focused issuetype:securitySecurity or privacy boundary

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions