טריגרים לאימות ב-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: מזהה הדייר ב-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) כדי לצמצם את זמן האחזור ברשת בין ספק האימות לבין סביבת ההפעלה של הפונקציה.