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

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

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

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

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

הגדרת הפרויקט ל-Android

  1. אם עדיין לא עשיתם זאת, מוסיפים את Firebase לפרויקט Android.

  2. בקובץ Gradle של המודול (ברמת האפליקציה) (בדרך כלל <project>/<app-module>/build.gradle.kts או <project>/<app-module>/build.gradle), מוסיפים את התלות בספריית Firebase Authentication ל-Android. מומלץ להשתמש Firebase Android BoM כדי לשלוט בניהול גרסאות של ספריות.

    בנוסף, כחלק מהגדרת Firebase Authentication, צריך להוסיף לאפליקציה את ה-SDK של Google Play Services.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:33.4.0"))
    
        // Add the dependency for the Firebase Authentication library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation("com.google.firebase:firebase-auth")
    // Also add the dependency for the Google Play services library and specify its version implementation("com.google.android.gms:play-services-auth:21.2.0")
    }

    כשמשתמשים ב-Firebase Android BoM, האפליקציה תמיד תשתמש בגרסאות תואמות של ספריות Firebase ל-Android.

    (חלופה)  מוסיפים יחסי תלות לספריות של Firebase בלי להשתמש ב-BoM

    אם בוחרים לא להשתמש ב-Firebase BoM, צריך לציין את כל הגרסאות של ספריות Firebase בשורת התלות שלהן.

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

    dependencies {
        // Add the dependency for the Firebase Authentication library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation("com.google.firebase:firebase-auth:23.0.0")
    // Also add the dependency for the Google Play services library and specify its version implementation("com.google.android.gms:play-services-auth:21.2.0")
    }
    מחפשים מודול ספרייה ספציפי ל-Kotlin? החל מ-אוקטובר 2023 (Firebase BoM 32.5.0), מפתחי Kotlin ומפתחי Java יוכלו להסתמך על מודול הספרייה הראשי (פרטים נוספים זמינים בשאלות הנפוצות לגבי היוזמה הזו).

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

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

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

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

    • url: קישור העומק להטמעה וכל מצב נוסף שיועברו. הדומיין של הקישור צריך להיכלל ברשימת ההיתרים של הדומיינים המורשים במסוף Firebase. כדי למצוא את הרשימה הזו, עוברים לכרטיסייה 'שיטת כניסה' (Authentication -> Sign-in method). הקישור יפנה את המשתמש לכתובת ה-URL הזו אם האפליקציה לא מותקנת במכשיר שלו ולא ניתן היה להתקין אותה.
    • androidPackageName ו-IOSBundleId: האפליקציות שבהן צריך להשתמש בכניסה לחשבון נפתח במכשיר Android או Apple. הגדרת קישורים דינמיים ב-Firebase כדי לפתוח קישורים לפעולות באימייל דרך אפליקציות לנייד.
    • handleCodeInApp: מוגדר כ-true. תמיד צריך להשלים את פעולת הכניסה באפליקציה, בניגוד לפעולות אחרות באימייל מחוץ לצ'אט (איפוס סיסמה ואימותים באימייל). הסיבה לכך היא שבסוף התהליך, המשתמש אמור להיות מחובר לחשבון ומצב האימות שלו נשמר באפליקציה.
    • dynamicLinkDomain: כשמוגדרים כמה דומיינים של קישורים דינמיים מותאמים אישית לפרויקט, לציין באיזה מהם להשתמש כאשר הקישור ייפתח באמצעותה אפליקציה שצוינה לנייד (לדוגמה, example.page.link). אחרת, הדומיין הראשון נבחר באופן אוטומטי.

    Kotlin+KTX

    val actionCodeSettings = 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
        setIOSBundleId("com.example.ios")
        setAndroidPackageName(
            "com.example.android",
            true, // installIfNotAvailable
            "12", // minimumVersion
        )
    }

    Java

    ActionCodeSettings actionCodeSettings =
            ActionCodeSettings.newBuilder()
                    // URL you want to redirect back to. The domain (www.example.com) for this
                    // URL must be whitelisted in the Firebase Console.
                    .setUrl("https://www.example.com/finishSignUp?cartId=1234")
                    // This must be true
                    .setHandleCodeInApp(true)
                    .setIOSBundleId("com.example.ios")
                    .setAndroidPackageName(
                            "com.example.android",
                            true, /* installIfNotAvailable */
                            "12"    /* minimumVersion */)
                    .build();

    למידע נוסף על ActionCodeSettings, אפשר לעיין ב מצב העברת נתונים בפעולות באימייל .

  2. מבקשים מהמשתמש את כתובת האימייל שלו.

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

    Kotlin+KTX

    Firebase.auth.sendSignInLinkToEmail(email, actionCodeSettings)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Email sent.")
            }
        }

    Java

    FirebaseAuth auth = FirebaseAuth.getInstance();
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Email sent.");
                    }
                }
            });

בעיית אבטחה

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

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

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

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

השלמת הכניסה באפליקציה ל-Android

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

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

  1. מפעילים את הקישורים הדינמיים ב-Firebase:

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

      אם כבר יצרתם דומיין Dynamic Links, שימו לב אליו. Dynamic Links בדרך כלל נראה כמו בדוגמה הבאה:

      example.page.link

      תצטרכו את הערך הזה כשתקבעו שהאפליקציה שלכם ל-Apple או ל-Android תוכל ליירט את הקישור הנכנס.

  2. הגדרת אפליקציות ל-Android:

    1. כדי לטפל בקישורים האלה מאפליקציית Android, צריך לציין את שם החבילה ב-Android בהגדרות הפרויקט במסוף Firebase. בנוסף, צריך לספק את SHA-1 ו-SHA-256 של אישור האפליקציה.
    2. עכשיו, אחרי שהוספתם דומיין של קישור דינמי ווידאתם שה אפליקציית Android מוגדרת נכון, הקישור הדינמי יפנה אל באפליקציה שלך, החל מפעילות של מרכז האפליקציות.
    3. אם רוצים שהקישור הדינמי יפנה לפעילות ספציפית, צריך יהיה צורך להגדיר מסנן Intent בקובץ AndroidManifest.xml חדש. כדי לעשות זאת, מציינים את הדומיין של הקישור הדינמי או את הטיפול בפעולות אימייל במסנן ה-Intent. כברירת מחדל, כתובת האימייל ה-handler של הפעולות מתארח בדומיין כמו בדוגמה הבאה:
      PROJECT_ID.firebaseapp.com/
    4. הערות:
      1. אל תציינו את כתובת ה-URL שהגדרתם ב-actionCodeSettings במסנן הכוונה.
      2. במהלך יצירת הדומיין של הקישור הדינמי, ייתכן שיצרתם גם קישור לכתובת URL קצרה. כתובת ה-URL הקצרה הזו לא תועבר. אל תגדירו את מסנן הכוונה כך שיזהה אותה באמצעות המאפיין android:pathPrefix. המשמעות היא הוא לא מסוגל להבחין בקישורים דינמיים שונים בחלקים שונים של את האפליקציה שלך. עם זאת, אתם יכולים לבדוק את הפרמטר mode של השאילתה בקישור כדי לראות איזו פעולה מנסה לבצע, או להשתמש בשיטות SDK כמו isSignInWithEmailLink כדי לבדוק אם קישור שהאפליקציה קיבלה עושה את מה שרציתם.
    5. למידע נוסף על קבלת קישורים דינמיים, אפשר לעיין במאמר הוראות לקבלת קישורים דינמיים ל-Android.

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

Kotlin+KTX

val auth = Firebase.auth
val intent = intent
val emailLink = intent.data.toString()

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    val email = "someemail@domain.com"

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Successfully signed in with email link!")
                val result = task.result
                // You can access the new user via result.getUser()
                // Additional user info profile *not* available via:
                // result.getAdditionalUserInfo().getProfile() == null
                // You can check if the user is new or existing:
                // result.getAdditionalUserInfo().isNewUser()
            } else {
                Log.e(TAG, "Error signing in with email link", task.exception)
            }
        }
}

Java

FirebaseAuth auth = FirebaseAuth.getInstance();
Intent intent = getIntent();
String emailLink = intent.getData().toString();

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    String email = "someemail@domain.com";

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Successfully signed in with email link!");
                        AuthResult result = task.getResult();
                        // You can access the new user via result.getUser()
                        // Additional user info profile *not* available via:
                        // result.getAdditionalUserInfo().getProfile() == null
                        // You can check if the user is new or existing:
                        // result.getAdditionalUserInfo().isNewUser()
                    } else {
                        Log.e(TAG, "Error signing in with email link", task.getException());
                    }
                }
            });
}

כדי לקרוא מידע נוסף על טיפול בכניסה באמצעות קישור לאימייל ב-Apple במדריך לפלטפורמות של Apple.

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

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

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

Kotlin+KTX

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Link the credential to the current user.
Firebase.auth.currentUser!!.linkWithCredential(credential)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d(TAG, "Successfully linked emailLink credential!")
            val result = task.result
            // You can access the new user via result.getUser()
            // Additional user info profile *not* available via:
            // result.getAdditionalUserInfo().getProfile() == null
            // You can check if the user is new or existing:
            // result.getAdditionalUserInfo().isNewUser()
        } else {
            Log.e(TAG, "Error linking emailLink credential", task.exception)
        }
    }

Java

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Link the credential to the current user.
auth.getCurrentUser().linkWithCredential(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Successfully linked emailLink credential!");
                    AuthResult result = task.getResult();
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error linking emailLink credential", task.getException());
                }
            }
        });

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

Kotlin+KTX

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Re-authenticate the user with this credential.
Firebase.auth.currentUser!!.reauthenticateAndRetrieveData(credential)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            // User is now successfully reauthenticated
        } else {
            Log.e(TAG, "Error reauthenticating", task.exception)
        }
    }

Java

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Re-authenticate the user with this credential.
auth.getCurrentUser().reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // User is now successfully reauthenticated
                } else {
                    Log.e(TAG, "Error reauthenticating", task.getException());
                }
            }
        });

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

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

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

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

השלבים הבאים

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

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

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

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

כדי לנתק משתמש מהחשבון, יש להתקשר אל signOut

Kotlin+KTX

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();