אימות באמצעות Firebase באמצעות קישורי אימייל

אתם יכולים להשתמש באימות ב-Firebase כדי לשלוח לאימייל של המשתמש קישור, שבו הוא יוכל ללחוץ כדי להיכנס לחשבון. במסגרת התהליך, גם כתובת האימייל של המשתמש מאומתת.

יש יתרונות רבים לכניסה באמצעות אימייל:

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

לפני שמתחילים

  1. אם עדיין לא עשיתם זאת, עליכם לפעול לפי השלבים שמפורטים במדריך תחילת העבודה.

  2. מפעילים את הכניסה באמצעות קישור לאימייל בפרויקט Firebase.

    כדי לאפשר למשתמשים להיכנס באמצעות קישור באימייל, קודם צריך להפעיל את ספק האימייל ואת שיטת הכניסה באמצעות קישור באימייל בפרויקט Firebase:

    1. פותחים את הקטע Auth במסוף Firebase.
    2. בכרטיסייה Sign in method (שיטת כניסה), מפעילים את הספק Email/Password (אימייל/סיסמה). חשוב לזכור: כדי להשתמש בכניסה באמצעות קישור לאימייל, צריך להפעיל את הכניסה באמצעות כתובת אימייל וסיסמה.
    3. באותו קטע, מפעילים את שיטת הכניסה קישור לאימייל (כניסה ללא סיסמה).
    4. לוחצים על שמירה.

כדי להתחיל את תהליך האימות, מציגים ממשק שמבקש מהמשתמש לספק את כתובת האימייל שלו, ולאחר מכן קוראים ל-sendSignInLinkToEmail() כדי לבקש מ-Firebase לשלוח את הקישור לאימות לכתובת האימייל של המשתמש.

  1. בונים את האובייקט ActionCodeSettings, שמספק ל-Firebase הוראות ליצירת קישור לאימייל. מגדירים את השדות הבאים:

    • url: קישור העומק להטמעה וכל מצב נוסף שרוצים להעביר. הדומיין של הקישור צריך להיכלל ברשימת ההיתרים של הדומיינים המורשים במסוף Firebase. אפשר למצוא את הרשימה הזו בכרטיסייה Sign-in method (שיטת כניסה) (Authentication -> Sign-in method). הקישור יפנה את המשתמש לכתובת ה-URL הזו אם האפליקציה לא מותקנת במכשיר שלו ולא ניתן היה להתקין אותה.

    • androidPackageName ו-IOSBundleId: האפליקציות שבהן נעשה שימוש כשהקישור לכניסה נפתח במכשיר Android או iOS. מידע נוסף על הגדרת קישורים דינמיים של Firebase לפתיחת קישורי פעולה באימייל דרך אפליקציות לנייד

    • handleCodeInApp: מוגדר לערך true. תמיד צריך להשלים את תהליך הכניסה באפליקציה, בניגוד לפעולות אחרות באימייל מחוץ לפורום (איפוס סיסמה ואימותים באימייל). הסיבה לכך היא שבסוף התהליך, המשתמש אמור להיות מחובר לחשבון ומצב האימות שלו נשמר באפליקציה.

    • dynamicLinkDomain: כשמגדירים כמה דומיינים של קישורים דינמיים מותאמים אישית לפרויקט, צריך לציין באיזה מהם להשתמש כשהקישור נפתח דרך אפליקציה ניידת ספציפית (לדוגמה, example.page.link). אחרת, הדומיין הראשון ייבחר באופן אוטומטי.

    var acs = ActionCodeSettings(
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be whitelisted in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true
        handleCodeInApp: true,
        iOSBundleId: 'com.example.ios',
        androidPackageName: 'com.example.android',
        // installIfNotAvailable
        androidInstallApp: true,
        // minimumVersion
        androidMinimumVersion: '12');
    
  2. מבקשים מהמשתמש את כתובת האימייל שלו.

  3. שולחים את קישור האימות לכתובת האימייל של המשתמש ושומרים את כתובת האימייל של המשתמש למקרה שהוא ישלים את הכניסה לאימייל באותו מכשיר.

    var emailAuth = 'someemail@domain.com';
    FirebaseAuth.instance.sendSignInLinkToEmail(
            email: emailAuth, actionCodeSettings: acs)
        .catchError((onError) => print('Error sending email verification $onError'))
        .then((value) => print('Successfully sent email verification'));
    });
    

חששות לגבי אבטחה

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

כדי לייעל את התהליך הזה למשתמשים שפותחים את הקישור לכניסה באותו מכשיר שבו הם מבקשים את הקישור, אפשר לשמור את כתובת האימייל שלהם באופן מקומי – למשל באמצעות SharedPreferences – כששולחים את האימייל לכניסה. לאחר מכן, תוכלו להשתמש בכתובת הזו כדי להשלים את התהליך. אל תעבירו את כתובת האימייל של המשתמש בפרמטרים של כתובת ה-URL להפניה אוטומטית, ואל תשתמשו בה שוב, כי הפעולות האלה עלולות לאפשר הזרקות של סשנים.

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

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

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

  1. במדריך מוסבר איך מגדירים את האפליקציה לקבלת קישורים דינמיים ב-Flutter.

  2. ב-handler של הקישורים, בודקים אם הקישור מיועד לאימות הקישור לאימייל, ואם כן, משלימים את תהליך הכניסה.

    // Confirm the link is a sign-in with email link.
    if (FirebaseAuth.instance.isSignInWithEmailLink(emailLink)) {
      try {
        // The client SDK will parse the code from the link for you.
        final userCredential = await FirebaseAuth.instance
            .signInWithEmailLink(email: emailAuth, emailLink: emailLink);
    
        // You can access the new user via userCredential.user.
        final emailAddress = userCredential.user?.email;
    
        print('Successfully signed in with email link!');
      } catch (error) {
        print('Error signing in with email link.');
      }
    }
    

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

ההבדל יהיה במחצית השנייה של הפעולה:

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.linkWithCredential(authCredential);
} catch (error) {
    print("Error linking emailLink credential.");
}

אפשר גם להשתמש בכך כדי לבצע אימות מחדש של משתמש עם קישור לאימייל לפני שמריצים פעולה עם מידע רגיש.

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.reauthenticateWithCredential(authCredential);
} catch (error) {
    print("Error reauthenticating credential.");
}

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

אם יצרתם את הפרויקט ב-15 בספטמבר 2023 או לאחר מכן, ההגנה מפני ספירת כתובות אימייל מופעלת כברירת מחדל. התכונה הזו משפרת את האבטחה של חשבונות המשתמשים בפרויקט, אבל היא משביתה את השיטה fetchSignInMethodsForEmail(), שבעבר המלצנו להשתמש בה כדי להטמיע תהליכים שמתחילים במזהה.

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

למידע נוסף, עיינו במסמכי התיעוד בנושא הגנה מפני ספירת כתובות אימייל.

השלבים הבאים

אחרי שמשתמש יוצר חשבון חדש, החשבון הזה נשמר כחלק מהפרויקט ב-Firebase, וניתן להשתמש בו כדי לזהות משתמש בכל האפליקציות בפרויקט, ללא קשר לשיטת הכניסה שבה המשתמש השתמש.

באפליקציות שלכם, אתם יכולים לקבל את פרטי הפרופיל הבסיסיים של המשתמש מהאובייקט User. ניהול משתמשים

במסגרת כללי האבטחה של Firebase Realtime Database ושל Cloud Storage, אפשר לקבל את מזהה המשתמש הייחודי של המשתמש שנכנס לחשבון מהמשתנה auth, ולהשתמש בו כדי לקבוע לאילו נתונים למשתמש תהיה גישה.

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

כדי להוציא משתמש, קוראים לפונקציה signOut():

await FirebaseAuth.instance.signOut();