טריגרים לאימות ב-Firebase

אפשר להפעיל פונקציות בתגובה ליצירה ולמחיקה של חשבונות משתמשים ב-Firebase Authentication. לדוגמה, אתם יכולים לשלוח אימייל פתיחה למשתמש שזה עתה יצר חשבון באפליקציה שלכם. הדוגמאות בדף הזה מבוססות על דוגמה שעושה בדיוק את זה – שולחת אימיילים של פתיחה ופרידה עם יצירת החשבון ומחיקתו.

לדוגמאות נוספות של תרחישים לדוגמה, ראו מה אפשר לעשות עם Cloud Functions?.

הפעלת פונקציה כשנוצר משתמש

אפשר ליצור פונקציה שמופעלת כשנוצר משתמש ב-Authentication באמצעות הגורם המטפל באירועים onUserCreated מחבילת המשנה firebase-functions/v2/identity:

const { onUserCreated } = require("firebase-functions/identity");
const { defineSecret } = require("firebase-functions/params");
const { logger } = require("firebase-functions");
const { sendWelcomeEmail } = require("./utils/myEmailService");

const emailApiKey = defineSecret("EMAIL_API_KEY");

exports.newUserWelcome = onUserCreated(
  { secrets: [emailApiKey] },
  async (event) => {
    const { uid, email, displayName } = event.data;

    if (!email) {
      logger.log(`User ${uid} does not have an email address.`);
      return;
    }

    await sendWelcomeEmail(email, displayName);
  },
);

חשבונות Authentication יפעילו אירועים של יצירת משתמשים ב-Cloud Functions במקרים הבאים:

  • המשתמש יוצר חשבון אימייל וסיסמה.
  • משתמש נכנס בפעם הראשונה באמצעות ספק זהויות מאוחד.
  • המפתח יוצר חשבון באמצעות SDK לאדמינים.
  • משתמש נכנס לסשן אימות אנונימי חדש בפעם הראשונה.

אירוע של Cloud Functions לא מופעל כשמשתמש נכנס לחשבון בפעם הראשונה באמצעות טוקן בהתאמה אישית.

הגדרת אפשרויות הטריגר וריבוי דיירים

כדי להגדיר את הפונקציה, אפשר להעביר אובייקט של אפשרויות (AuthOptions) בתור הפרמטר הראשון אל onUserCreated:

/**
 * Sends a welcome email scoped to a specific tenant in Identity Platform.
 */
exports.sendWelcomeEmailToTenant = onUserCreated(
  {
    secrets: [emailApiKey],
    // Only trigger when a user is a member of this tenant
    tenantId: "my-tenant-id",
  },
  async (event) => {
    const { uid, email, displayName } = event.data;
    // Customize the email for this tenant
    await sendWelcomeEmail(email, displayName, event.tenantId);
  },
);
  /**
 * Sends a welcome email only to users not associated with any tenant.
 */
exports.sendWelcomeEmailNoTenant = onUserCreated(
  {
    secrets: [emailApiKey],
    // Only trigger when a user is NOT a member of a tenant
    tenantId: IS_NOT_TENANT,
  },
  async (event) => {
    const { email, displayName } = event.data;

    // Send a generic welcome email
    await sendWelcomeEmail(email, displayName);
  },
);

אם הפרויקט משתמש בריבוי דיירים ב-Identity Platform, אתם יכולים להגדיר את היקף הטריגר:

  • פרויקט ברירת המחדל (ללא דייר): מגדירים את tenantId לערך IS_NOT_TENANT כדי להאזין רק למשתמשים שנוצרו בפרויקט ברירת המחדל.
  • דייר ספציפי: מספקים את מזהה המחרוזת של הדייר (לדוגמה, { tenantId: "tenant-id-1" }) כדי להאזין רק למשתמשים שנוצרו בדייר הזה.
  • כל הדיירים והמשתמשים: אם משמיטים את tenantId, הפונקציה מופעלת באירועים של יצירת משתמשים בכל הדיירים ובמשתמשי ברירת המחדל של הפרויקט.

בנוסף ל-tenantId, אתם יכולים לציין אפשרויות הגדרה רגילות של דור שני, כולל region, ‏ concurrency, ‏ cpu, ‏ memory,‏ timeoutSeconds, ‏ minInstances, ‏ maxInstances ו-secrets.

גישה למאפייני משתמשים

מנתוני המשתמש שמוחזרים לפונקציה, אתם יכולים לגשת לרשימת מאפייני המשתמש שזמינים באובייקט UserRecord של המשתמש החדש שנוצר באמצעות event.data. לדוגמה, אפשר לקבל את כתובת האימייל ואת השם המוצג של המשתמש, כמו שמוצג כאן:

const { uid, email, displayName } = event.data;

טריגרים של אימות בדור השני מקבלים אובייקט מסוג AuthEvent. בנוסף ל-event.data, אתם יכולים לגשת למטא-נתונים של אירועים כמו:

  • ‫event.id: מזהה ייחודי של האירוע.
  • ‫event.type: סוג האירוע (google.firebase.auth.user.v2.created).
  • ‫event.time: חותמת זמן בפורמט ISO 8601 שמייצגת את מועד האירוע.
  • ‫event.project: מזהה הפרויקט ב-Google Cloud.
  • ‫event.tenantId: מזהה הדייר (tenant) ב-Identity Platform שמשויך למשתמש, אם רלוונטי.

הפעלת פונקציה כשמשתמש נמחק

בדיוק כמו שאפשר להפעיל פונקציה כשנוצר משתמש, אפשר להגיב לאירועים של מחיקת משתמשים. משתמשים בגורם המטפל באירועים onUserDeleted מ-firebase-functions/v2/identity, כמו שמוצג כאן:

const { onUserDeleted } = require("firebase-functions/identity");
const { defineSecret } = require("firebase-functions/params");
const { logger } = require("firebase-functions");
const { sendGoodbyeEmail } = require("./utils/myEmailService");

const emailApiKey = defineSecret("EMAIL_API_KEY");

exports.deletedUserFarewell = onUserDeleted(
  { secrets: [emailApiKey] },
  async (event) => {
    const { uid, email, displayName } = event.data;
    if (!email) {
      logger.log(`User ${uid} does not have an email address.`);
      return;
    }

    await sendGoodbyeEmail(email, displayName);
  },
);

כמו ב-onUserCreated, אפשר להגדיר את onUserDeleted עם אפשרויות כמו { tenantId: IS_NOT_TENANT } כדי להגביל את הטריגרים למשתמשים בפרויקט ברירת המחדל.

הפעלת פונקציות חסימה

אם שדרגתם ל-Firebase Authentication with Identity Platform, אתם יכולים להרחיב את Firebase Authentication באמצעות פונקציות חסימה.

פונקציות חסימה מאפשרות להריץ קוד בהתאמה אישית באופן סינכרוני, שמשנה את התוצאה של רישום משתמש או כניסה לאפליקציה. בניגוד לטריגרים ברקע, שפועלים באופן אסינכרוני אחרי שאירוע מסתיים, פונקציות חסימה מאפשרות למנוע ממשתמש לבצע אימות אם הוא לא עומד בקריטריונים מסוימים, או לעדכן את המידע וההצהרות של המשתמש לפני שהם מוחזרים לאפליקציית הלקוח.

שיטות מומלצות לטריגרים מהדור השני

כשמטמיעים טריגרים של אימות מהדור השני, חשוב לזכור את השיטות המומלצות הבאות:

  • התחשבות בבקשות מקבילות: מכונות של Cloud Functions (דור שני) מעבדות בקשות מקבילות (כברירת מחדל, 80 בקשות מקבילות כש-CPU ≥ 1). חשוב לוודא שהפונקציה לא מסתמכת על מצב גלובלי שניתן לשינוי בין הפעלות בו-זמניות.
  • תכנון לאידמפוטנטיות: העברת אירועים בדור השני מתבצעת לפחות פעם אחת באמצעות Eventarc. חשוב לוודא שהפונקציות הן אידמפוטנטיות. לדוגמה, לפני ביצוע של פעולות משניות, צריך לוודא שאימייל פתיחה עדיין לא נשלח או שרשומה במסד הנתונים עדיין לא אותחלה.
  • היקף הפונקציות של ריבוי דיירים: אם האפליקציה משתמשת בריבוי דיירים של Identity Platform, צריך לוודא אם הפונקציות צריכות לטפל באירועים בכל הדיירים או רק בדיירים ספציפיים. כדי למנוע ממשתמשים בדייר להפעיל פונקציות שמיועדות רק לפרויקט הראשי, משתמשים ב-tenantId: IS_NOT_TENANT.
  • ניהול אזורים והקצאת משאבים: מציינים את מיקום הפונקציה (region) כדי לצמצם את זמן האחזור ברשת בין ספק האימות לבין סביבת ההפעלה של הפונקציה.