You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Auth用户创建与Firestore数据插入的原子性执行?

How to Ensure Atomicity Between Firebase Auth User Creation and Firestore Data Insertion

Great question—this is a common pain point since Firebase Auth and Firestore are separate services, so Firestore’s transactions/batch operations only work within Firestore itself. There’s no native cross-service atomic transaction, but we can implement a compensating transaction pattern using Cloud Functions to get close to the "all-or-nothing" behavior you need. Here’s how to do it:

Core Approach

The idea is to wrap both operations in a server-side function (Cloud Functions) where we:

  1. First create the Auth user.
  2. If the Auth creation succeeds, attempt to write the user’s additional data to Firestore.
  3. If the Firestore write fails, immediately delete the newly created Auth user to roll back the operation.
  4. Return a clear success/failure status to the client.

Step-by-Step Implementation

1. Deploy a Cloud Function to Handle the Combined Operation

Here’s a Node.js example of a callable cloud function that manages both operations and handles rollbacks:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.createUserWithProfile = functions.https.onCall(async (data, context) => {
  // Validate input first (always a good practice!)
  if (!data.email || !data.password || !data.name || !data.businessType) {
    throw new functions.https.HttpsError("invalid-argument", "Missing required fields");
  }

  let createdUserId;

  try {
    // Step 1: Create the Auth user
    const userRecord = await admin.auth().createUser({
      email: data.email,
      password: data.password,
      displayName: data.name // Optional: Sync name to Auth displayName too
    });
    createdUserId = userRecord.uid;

    // Step 2: Write user profile to Firestore
    await admin.firestore()
      .collection("users")
      .doc(createdUserId)
      .set({
        name: data.name,
        businessType: data.businessType,
        email: data.email,
        createdAt: admin.firestore.FieldValue.serverTimestamp()
      });

    return { success: true, uid: createdUserId };
  } catch (error) {
    // Step 3: Rollback Auth user if Firestore write failed
    if (createdUserId) {
      try {
        await admin.auth().deleteUser(createdUserId);
        functions.logger.info(`Rolled back Auth user: ${createdUserId}`);
      } catch (rollbackError) {
        functions.logger.error(`Failed to rollback Auth user: ${createdUserId}`, rollbackError);
        // Log this critical error for manual cleanup later
      }
    }

    // Translate error types for client clarity
    if (error.code === "auth/email-already-exists") {
      throw new functions.https.HttpsError("already-exists", "Email is already in use");
    }
    throw new functions.https.HttpsError("internal", "Failed to create user profile", error.message);
  }
});

2. Call the Cloud Function from Your Client

Instead of calling Auth and Firestore directly from the client, use the callable function to trigger the combined operation. Here’s a Web client example:

// Initialize Firebase (if not already done)
import { initializeApp } from "firebase/app";
import { getFunctions, httpsCallable } from "firebase/functions";

const app = initializeApp(yourFirebaseConfig);
const functions = getFunctions(app);
const createUserWithProfile = httpsCallable(functions, "createUserWithProfile");

// Usage
async function registerUser() {
  try {
    const result = await createUserWithProfile({
      email: "user@example.com",
      password: "SecurePass123!",
      name: "Jane Smith",
      businessType: "Professional Services"
    });
    console.log("User created successfully:", result.data.uid);
    // Redirect to dashboard or update UI
  } catch (error) {
    console.error("Registration failed:", error.message);
    // Show error message to user (e.g., "Email already exists")
  }
}

Key Considerations for Edge Cases

  • Rollback Failures: In rare cases, deleting the Auth user might fail (e.g., network outage mid-operation). For these scenarios, set up a periodic cleanup job (another Cloud Function triggered by Cloud Scheduler) that checks for Auth users without corresponding Firestore documents and deletes them.
  • Idempotency: If the client retries the request (e.g., due to a network glitch), avoid creating duplicate users. Add a check in the cloud function to see if an Auth user with the provided email already exists before proceeding.
  • Security: Restrict direct client access to Auth user creation and Firestore writes. Use Firebase Security Rules to ensure only your cloud function can write to the users collection, and disable client-side Auth sign-up if needed.

This approach gives you the closest possible behavior to an atomic transaction for these cross-service operations, ensuring consistency in almost all cases.

内容的提问来源于stack exchange,提问作者Snake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:13:51