Subscriber Identity & Auth Integration
This document details how the Listserv application integrates subscriber records with Firebase Auth UIDs, manages double opt-in verification, and enforces security through the accountIncomplete custom token claim.
1. Firebase Auth UID Coupling (SubscriberId == Auth UID)
Every listserv subscriber is directly backed by a Firebase Auth user account. Instead of using arbitrary subscriber strings (e.g. sub-12345), the subscriber document ID in Firestore is the nominal brand uid matching the Firebase Auth UID:
app-listserv/data/subscriber/{uid}Architectural Benefits:
- Zero Identity Duplication: Email addresses and authentication credentials reside in Firebase Auth Core.
- Self-Service Dashboard: Allows subscribers to sign into the platform at any point to update delivery preferences, toggle digest mode, or view personal subscription status.
- Ecosystem Portability: Integrates seamlessly with other Accessible Data Framework applications (
app-survey,app-discussion,app-customer).
2. Web Double Opt-In State Machine
For registrations initiated through the public web form, subscription follows a double opt-in state machine:
Web Registration
│
▼
┌───────────┐ Email Verification Link Click
│ pending │ ───────────────────────────────────────▶ ┌───────────┐
└───────────┘ (Creates Firebase User + completionToken) │ confirmed │
└─────┬─────┘
│
▼
┌───────────┐
│ active │
└───────────┘pendingState: Temporary record created with email, chosen languages, and a secure verificationtoken. No Firebase Auth account exists yet.verify()Action: Validates token, creates the Firebase Auth user account (createUser({ email })), assigns custom token claims, setsSubscriberId = Auth UID, and upgrades status toconfirmed/active.
3. Email Command Path & Immediate Linking
Subscribers registering via email commands (e.g., sending SUBSCRIBE to channel-1@mg.a11ydata.com) bypass the web verification state machine:
- The inbound handler executes
getUserByEmail(email). - If Existing User Found: Reuses the existing Auth UID (Account Linking).
- If New User: Automatically creates a Firebase Auth user account (
createUser({ email })) and assigns the custom claimaccountIncomplete: true. - The subscriber is immediately set to
status: 'active'.
4. Security Gating via accountIncomplete Custom Claim
When an account is created without a password (such as via double opt-in or email commands), Firebase Auth attaches a custom token claim:
{
"accountIncomplete": true
}Firestore Security Rule Enforcement:
Firestore security rules in app-listserv/firestore.rules inspect this custom claim to gate client write access:
allow write: if (
isAuthenticated() && (
isSuperAdmin() ||
isUserTeamEditor(getListservTeamId()) ||
(
isUser(subscriberId) &&
request.auth.token.accountIncomplete != true
)
)
);Security Guarantee
Subscribers whose accounts lack a password cannot write to or mutate their Firestore records. They can only modify their settings after completing the password setup flow.
5. Account Completion Flow
Subscribers with accountIncomplete: true receive an account completion link:
- Token Generation: A unique
completionTokenis written to the subscriber's document. - Notification Link: An email is sent containing the URL:
https://listserv.a11ydata.com/account/complete?token=<completionToken> - Password Creation: The user opens the page, sets a secure password, and submits the form.
- Claim Clearing: The
completeAccountcloud endpoint verifies the token, callsauth.updateUser(uid, { password }), removes theaccountIncompleteclaim, and clears thecompletionTokenfrom Firestore.