المصادقة مع Firebase باستخدام "رابط البريد الإلكتروني" في Android

يمكنك استخدام مصادقة Firebase لتسجيل الدخول إلى المستخدمين عن طريق إرسال رسالة إلكترونية إليهم تحتوي على رابط، يمكنهم النقر عليه لتسجيل الدخول. وفي هذه العملية، يتم أيضًا إثبات ملكية عنوان البريد الإلكتروني للمستخدم.

هناك العديد من المزايا لتسجيل الدخول عبر البريد الإلكتروني:

  • تسجيل الدخول والاشتراك بسهولة
  • انخفاض مخاطر إعادة استخدام كلمة المرور على جميع التطبيقات، ما قد يقوّض أمان كلمات المرور المحددة بشكل جيد.
  • هي إمكانية مصادقة المستخدم مع التحقّق أيضًا من أنّ المستخدم هو المالك الشرعي لعنوان بريد إلكتروني.
  • يحتاج المستخدم فقط إلى حساب بريد إلكتروني يمكن الوصول إليه لتسجيل الدخول. ليست هناك حاجة إلى ملكية رقم هاتف أو حساب على وسائل التواصل الاجتماعي.
  • ويمكن للمستخدم تسجيل الدخول بأمان بدون الحاجة إلى تقديم (أو تذكُّر) كلمة مرور، ما قد يكون مزعجًا على الأجهزة الجوّالة.
  • يمكن ترقية مستخدم حالي سبق له تسجيل الدخول باستخدام معرّف بريد إلكتروني (كلمة مرور أو موحّد) لتسجيل الدخول باستخدام البريد الإلكتروني فقط. على سبيل المثال، سيظل بإمكان المستخدم الذي نسي كلمة المرور تسجيل الدخول بدون الحاجة إلى إعادة ضبطها.

قبل البدء

إعداد مشروع Android

  1. أضِف Firebase إلى مشروع Android الخاص بك، إذا لم يسبق لك إجراء ذلك.

  2. في ملف Gradle للوحدة (على مستوى التطبيق) (عادةً <project>/<app-module>/build.gradle.kts أو <project>/<app-module>/build.gradle)، أضِف التبعية لمكتبة مصادقة Firebase لنظام التشغيل Android. ننصحك باستخدام برنامج Firebase Android BoM للتحكّم في إصدارات المكتبة.

    وأيضًا، كجزء من إعداد مصادقة Firebase، يجب أيضًا إضافة حزمة SDK لخدمات Google Play إلى تطبيقك.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
    
        // 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")
    }

    باستخدام إطار عمل Android BoM، سيستخدم تطبيقك دائمًا إصدارات متوافقة من مكتبات Android في Firebase.

    (بديل) أضِف تبعيات مكتبة Firebase بدون استخدام قائمة BoM.

    إذا اختَرت عدم استخدام قائمة مشروعات Firebase، يجب تحديد كل إصدار من إصدارات مكتبة 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. في علامة التبويب طريقة تسجيل الدخول، فعِّل مزوِّد خدمة البريد الإلكتروني/كلمة المرور. لاحظ أنه يجب تمكين تسجيل الدخول عبر البريد الإلكتروني/كلمة المرور لاستخدام ميزة تسجيل الدخول عبر رابط البريد الإلكتروني.
  3. في القسم نفسه، فعِّل طريقة تسجيل الدخول رابط البريد الإلكتروني (تسجيل الدخول بدون كلمة مرور).
  4. انقر على حفظ.

لبدء مسار المصادقة، يمكنك تقديم واجهة للمستخدم تطلب فيها من المستخدم تقديم عنوان بريده الإلكتروني ثم الاتصال بـ sendSignInLinkToEmail لطلب من Firebase إرسال رابط المصادقة إلى عنوان البريد الإلكتروني للمستخدم.

  1. أنشئ عنصر ActionCodeSettings الذي يزوّد Firebase بتعليمات عن كيفية إنشاء رابط البريد الإلكتروني. اضبط الحقول التالية:

    • url: الرابط لموضع معيّن الذي يجب تضمينه وأي حالة إضافية يتم تمريرها. يجب إضافة نطاق الرابط إلى القائمة البيضاء في قائمة وحدة تحكُّم Firebase للنطاقات المسموح بها، والتي يمكن العثور عليها من خلال الانتقال إلى علامة التبويب "طريقة تسجيل الدخول" (المصادقة -> طريقة تسجيل الدخول). سيعيد الرابط توجيه المستخدم إلى عنوان URL هذا إذا لم يكن التطبيق مثبّتًا على جهازه ولم يكن التطبيق قابلاً للتثبيت.
    • androidPackageName وIOSBundleId: التطبيقات المستخدَمة عند فتح رابط تسجيل الدخول على جهاز Android أو Apple مزيد من المعلومات حول كيفية ضبط روابط Firebase الديناميكية لفتح روابط الإجراءات عبر البريد الإلكتروني عبر التطبيقات المتوافقة مع الأجهزة الجوّالة.
    • handleCodeInApp: تم الضبط على "صحيح" يجب أن تكتمل عملية تسجيل الدخول دائمًا في التطبيق على عكس إجراءات البريد الإلكتروني الأخرى خارج النطاق (إعادة تعيين كلمة المرور وعمليات التحقق من البريد الإلكتروني). ويرجع هذا إلى أنّه في نهاية العملية، يُتوقّع من المستخدم تسجيل الدخول واستمرار حالة المصادقة داخل التطبيق.
    • 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 تقديم عنوان البريد الإلكتروني للمستخدم عند إكمال عملية تسجيل الدخول. لنجاح عملية تسجيل الدخول، يجب أن يتطابق عنوان البريد الإلكتروني هذا مع العنوان الذي تم إرسال رابط تسجيل الدخول إليه في الأصل.

يمكنك تبسيط هذا المسار للمستخدمين الذين يفتحون رابط تسجيل الدخول على الجهاز نفسه الذي يطلبون فيه الرابط من خلال تخزين عناوين بريدهم الإلكتروني محليًا، على سبيل المثال، باستخدام SharedPreferences - عند إرسال الرسالة الإلكترونية لتسجيل الدخول. بعد ذلك، استخدم هذا العنوان لإكمال التدفق. لا تمرّر عنوان البريد الإلكتروني للمستخدم في معلَمات عنوان URL لإعادة التوجيه وأعِد استخدامه، لأنّ ذلك قد يؤدي إلى تفعيل إدخال الجلسات.

بعد اكتمال عملية تسجيل الدخول، ستتم إزالة أي آلية تسجيل دخول سابقة لم يتم التحقّق منها من المستخدم وسيتم إلغاء أي جلسات حالية. على سبيل المثال، إذا أنشأ شخص ما في السابق حسابًا لم يتم إثبات ملكيته باستخدام عنوان البريد الإلكتروني وكلمة المرور نفسيهما، ستتم إزالة كلمة مرور المستخدم لمنع الشخص الذي طالب بالملكية وأنشأ هذا الحساب الذي لم يتم إثبات ملكيته من تسجيل الدخول مرة أخرى باستخدام البريد الإلكتروني وكلمة المرور اللذين لم يتم إثبات ملكيتهما.

احرص أيضًا على استخدام عنوان URL يستخدم HTTPS في مرحلة الإنتاج لتجنّب اعتراض الخوادم الوسيطة على الرابط.

إكمال تسجيل الدخول في تطبيق Android

تستخدم مصادقة Firebase روابط Firebase الديناميكية لإرسال رابط البريد الإلكتروني إلى جهاز جوّال. لإكمال عملية تسجيل الدخول عبر التطبيق للأجهزة الجوّالة، يجب إعداد التطبيق لرصد رابط التطبيق الوارد، وتحليل الرابط الأساسي لصفحة معيّنة ثم إكمال عملية تسجيل الدخول.

تستخدم مصادقة Firebase روابط Firebase الديناميكية عند إرسال رابط يُفتح في تطبيق للأجهزة الجوّالة. ومن أجل استخدام هذه الميزة، يجب إعداد "الروابط الديناميكية" في "وحدة تحكّم Firebase".

  1. تفعيل روابط Firebase الديناميكية:

    1. في وحدة تحكُّم Firebase، افتح قسم الروابط الديناميكية.
    2. إذا لم تكن قد قبلت بنود الروابط الديناميكية حتى الآن وأنشأت نطاق "الروابط الديناميكية"، عليك تنفيذ ذلك الآن.

      إذا سبق لك إنشاء نطاق "الروابط الديناميكية"، يُرجى تدوينه. يبدو نطاق الروابط الديناميكية عادةً على النحو التالي:

      example.page.link

      ستحتاج إلى هذه القيمة عند ضبط تطبيق Apple أو Android لاعتراض الرابط الوارد.

  2. ضبط تطبيقات Android:

    1. من أجل معالجة هذه الروابط من تطبيق Android، يجب تحديد اسم حزمة Android في إعدادات مشروع وحدة تحكم Firebase. بالإضافة إلى ذلك، يجب تقديم شهادة SHA-1 وSHA-256 لشهادة التطبيق.
    2. والآن بعد إضافة نطاق الرابط الديناميكي والتأكد من تهيئة تطبيق Android بشكل صحيح، ستتم إعادة توجيه الرابط الديناميكي إلى تطبيقك، بدءًا من نشاط المشغل.
    3. إذا كنت تريد أن يُعيد الرابط الديناميكي التوجيه إلى نشاط معيّن، ستحتاج إلى إعداد فلتر أهداف في ملف AndroidManifest.xml. يمكن إجراء ذلك إما عن طريق تحديد نطاق الرابط الديناميكي أو معالج إجراء البريد الإلكتروني في فلتر الأهداف. تتم تلقائيًا استضافة معالج إجراءات البريد الإلكتروني على نطاق مثل المثال التالي:
      PROJECT_ID.firebaseapp.com/
    4. تحذيرات:
      1. ولا تحدِّد عنوان URL الذي تضبطه في actionsCodeSettings ضمن فلتر الأهداف.
      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 في الوقت الفعلي وقواعد الأمان في Cloud Storage، يمكنك الحصول على رقم تعريف المستخدم الفريد للمستخدم الذي سجّل الدخول من خلال المتغيّر auth، واستخدامه للتحكّم في البيانات التي يمكن للمستخدم الوصول إليها.

يمكنك السماح للمستخدمين بتسجيل الدخول إلى تطبيقك باستخدام مزوّدي مصادقة متعددين من خلال ربط بيانات اعتماد موفّر المصادقة بحساب مستخدم حالي.

لتسجيل خروج المستخدم، يمكنك الاتصال على signOut:

Kotlin+KTX

Firebase.auth.signOut()

Java

FirebaseAuth.getInstance().signOut();