إضافة مصادقة متعدّدة العوامل إلى تطبيق iOS

إذا تمت الترقية إلى مصادقة Firebase باستخدام النظام الأساسي للهوية، يمكنك إضافة مصادقة متعددة العوامل عبر الرسائل القصيرة SMS إلى تطبيق iOS.

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

قبل البدء

  1. فعِّل موفّر خدمة واحدًا على الأقل يتيح المصادقة المتعدّدة العوامل. يتيح كل مقدّم خدمة المصادقة المتعدّدة القنوات (MFA)، باستثناء مصادقة الهاتف، والمصادقة المجهولة، وApple Game Center.

  2. تأكَّد من أنّ تطبيقك يتحقّق من عناوين البريد الإلكتروني للمستخدمين. تتطلب MFA إثبات ملكية عنوان البريد الإلكتروني. ويؤدي ذلك إلى منع الجهات الضارّة من التسجيل في خدمة باستخدام عنوان بريد إلكتروني لا تملكه، ثم حظر المالك الحقيقي عن طريق إضافة عامل ثانٍ.

تفعيل المصادقة المتعدّدة العوامل

  1. افتح صفحة المصادقة > طريقة تسجيل الدخول في وحدة تحكّم Firebase.

  2. في القسم الإعدادات المتقدّمة، فعِّل المصادقة المتعدّدة العوامل للرسائل القصيرة.

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

  3. إذا لم تكن قد فوّضت نطاق تطبيقك من قبل، يمكنك إضافته إلى قائمة السماح في صفحة المصادقة > الإعدادات بوحدة تحكُّم Firebase.

إثبات ملكية تطبيقك

يحتاج Firebase إلى التأكّد من أنّ طلبات الرسائل القصيرة SMS واردة من تطبيقك. يمكنك إجراء ذلك بطريقتَين:

  • إشعارات أسماء نقاط الوصول الصامتة: عند تسجيل دخول مستخدم للمرة الأولى، يمكن لمنصة Firebase إرسال إشعار فوري صامت إلى جهاز المستخدم. يمكن متابعة المصادقة في حال تلقّي التطبيق الإشعار. لاحظ أنه بدءًا من iOS 8.0، لا تحتاج إلى مطالبة المستخدم بالسماح للإشعارات الفورية باستخدام هذه الطريقة.

  • اختبار reCAPTCHA: إذا لم تتمكن من إرسال إشعار صامت (على سبيل المثال، لأنّ المستخدم أوقف إعادة التحميل في الخلفية أو أنّك تختبر تطبيقك في محاكي iOS)، يمكنك استخدام reCAPTCHA. في كثير من الحالات، يتم حل اختبار CAPTCHA تلقائيًا بدون أي تفاعل من المستخدم.

استخدام الإشعارات الصامتة

لتفعيل إشعارات أسماء نقاط الوصول (APN) للاستخدام مع Firebase:

  1. في Xcode، فعِّل الإشعارات الفورية لمشروعك.

  2. حمِّل مفتاح مصادقة أسماء نقاط الوصول (APN) باستخدام "وحدة تحكُّم Firebase" (سيتم نقل التغييرات التي أجريتها تلقائيًا إلى Google Cloud Firebase). إذا لم يكن لديك مفتاح مصادقة أسماء نقاط الوصول (APN) لديك، راجِع ضبط أسماء نقاط الوصول باستخدام ميزة "المراسلة عبر السحابة الإلكترونية من Firebase" للاطّلاع على كيفية الحصول عليه.

    1. افتح وحدة تحكُّم Firebase.

    2. انتقِل إلى Project Settings (إعدادات المشروع).

    3. حدد علامة التبويب المراسلة عبر السحابة الإلكترونية.

    4. ضِمن مفتاح مصادقة أسماء نقاط الوصول (APN)، في قسم ضبط تطبيق iOS، انقر على تحميل.

    5. اختَر مفتاحك.

    6. أضِف رقم تعريف المفتاح. يمكنك العثور على رقم تعريف المفتاح ضمن الشهادات والمعرّفات والملفات الشخصية في مركز أعضاء المطوّرين من Apple.

    7. انقر على تحميل.

إذا كانت لديك شهادة أسماء نقاط الوصول (APN) من قبل، يمكنك تحميل الشهادة بدلاً من ذلك.

استخدام اختبار reCAPTCHA

لتفعيل حزمة تطوير البرامج (SDK) للعميل من استخدام reCAPTCHA:

  1. افتح إعدادات مشروعك في Xcode.

  2. انقر مرّتين على اسم المشروع في العرض التدرّجي الأيمن.

  3. اختَر تطبيقك من قسم الأهداف.

  4. انقر على علامة التبويب المعلومات.

  5. وسِّع قسم أنواع عناوين URL.

  6. انقر على الزر +.

  7. أدخِل معرِّف العميل العكسي في الحقل مخططات عناوين URL. ويمكنك العثور على هذه القيمة مدرَجة في ملف إعداد GoogleService-Info.plist باسم REVERSED_CLIENT_ID.

عند الانتهاء، يجب أن تبدو الإعدادات مشابهة لما يلي:

المخطّطات المخصَّصة

يمكنك اختياريًا تخصيص طريقة عرض تطبيقك للسمة SFSafariViewController أو UIWebView عند عرض reCAPTCHA. لإجراء ذلك، أنشِئ فئة مخصّصة تتوافق مع بروتوكول FIRAuthUIDelegate ومرِّرها إلى verifyPhoneNumber:UIDelegate:completion:.

اختيار نمط التسجيل

يمكنك اختيار ما إذا كان تطبيقك يتطلب مصادقة متعدّدة العوامل، وكيفية تسجيل المستخدمين وتوقيت تسجيلهم. وتشمل بعض الأنماط الشائعة ما يلي:

  • سجِّل العامل الثاني للمستخدم كجزء من التسجيل. استخدِم هذه الطريقة إذا كان تطبيقك يتطلّب مصادقة متعدّدة العوامل لجميع المستخدمين. ملاحظة: يجب أن يكون للحساب عنوان بريد إلكتروني تم إثبات ملكيته لتسجيل عامل ثانٍ، لذلك يجب أن تتوفر في مسار التسجيل هذا.

  • عليك توفير خيار قابل للتخطّي لتسجيل عامل ثانٍ أثناء التسجيل. التطبيقات التي ترغب في تشجيع إجراء مصادقة متعددة العوامل لها دون الحاجة إليها قد تفضل هذه الطريقة.

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

  • يمكنك طلب إضافة عامل ثانٍ بشكل تدريجي عندما يريد المستخدم الوصول إلى ميزات ذات متطلبات أمان متزايدة.

تسجيل عامل ثانٍ

لتسجيل عامل ثانوي جديد لمستخدم:

  1. أعِد مصادقة المستخدم.

  2. اطلب من المستخدم إدخال رقم هاتفه.

  3. الحصول على جلسة متعددة العوامل للمستخدم:

    Swift

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objective-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. أرسِل رسالة تحقُّق إلى هاتف المستخدم. تأكَّد من تنسيق رقم الهاتف باستخدام + في البداية وبدون علامات ترقيم أو مسافات بيضاء أخرى (على سبيل المثال: +15105551234).

    Swift

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objective-C

    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber
                                          UIDelegate:nil
                                  multiFactorSession:session
                                          completion:^(NSString * _Nullable verificationID,
                                                        NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.
    }];
    

    من أفضل الممارسات إبلاغ المستخدمين مسبقًا بأنّهم سيتلقّون رسالة SMS وأنّ الأسعار العادية تنطبق، على الرغم من أنّ هذا الإجراء ليس مطلوبًا.

    تبدأ طريقة verifyPhoneNumber() عملية التحقق من التطبيقات في الخلفية باستخدام الإشعارات الفورية الصامتة. إذا لم يكن هناك إشعار فوري صامت، فسيتم إصدار تحدي reCAPTCHA بدلاً من ذلك.

  5. بعد إرسال رمز SMS، اطلب من المستخدم التحقّق من الرمز. بعد ذلك، استخدِم ردّه لإنشاء PhoneAuthCredential:

    Swift

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. إعداد كائن تأكيد:

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. أكمِل عملية التسجيل. يمكنك اختياريًا تحديد اسم معروض للعامل الثاني. ويكون ذلك مفيدًا للمستخدمين الذين لديهم عدة عوامل ثانية، لأنّ رقم الهاتف يكون مخفيًا أثناء مسار المصادقة (على سبيل المثال، +1******1234).

    Swift

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objective-C

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    [authResult.user.multiFactor enrollWithAssertion:assertion
                                         displayName:nil
                                          completion:^(NSError * _Nullable error) {
        // ...
    }];
    

يوضح الرمز أدناه مثالاً كاملاً لتسجيل عامل ثانٍ:

Swift

let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
  // Send SMS verification code.
  PhoneAuthProvider.provider().verifyPhoneNumber(
    phoneNumber,
    uiDelegate: nil,
    multiFactorSession: session
  ) { (verificationId, error) in
    // verificationId will be needed for enrollment completion.
    // Ask user for the verification code.
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: phoneSecondFactorVerificationCode)
    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
  }
})

Objective-C

FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                              NSError * _Nullable error) {
    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumber:phoneNumber
      UIDelegate:nil
      multiFactorSession:session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.

        // Ask user for the verification code.
        // ...

        // Then:
        FIRPhoneAuthCredential *credential =
            [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
                                                        verificationCode:kPhoneSecondFactorVerificationCode];
        FIRMultiFactorAssertion *assertion =
            [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

        // Complete enrollment. This will update the underlying tokens
        // and trigger ID token change listener.
        [user.multiFactor enrollWithAssertion:assertion
                                  displayName:displayName
                                    completion:^(NSError * _Nullable error) {
            // ...
        }];
    }];
}];

تهانينا لقد سجّلت بنجاح عامل مصادقة ثانٍ لمستخدم.

تسجيل دخول المستخدمين بعامل ثانٍ

لتسجيل دخول مستخدم باستخدام ميزة إثبات الهوية عبر الرسائل القصيرة SMS:

  1. سجّل دخول المستخدم بعامله الأول، ثم ستظهر رسالة خطأ تشير إلى أن المصادقة متعددة العوامل مطلوبة. يحتوي هذا الخطأ على أداة تعيين، وتلميحات عن العوامل الثانية المسجَّلة، وجلسة أساسية تثبت أنّ المستخدم تمت مصادقته بنجاح باستخدام العامل الأول.

    على سبيل المثال، إذا كان العامل الأول للمستخدم هو البريد الإلكتروني وكلمة المرور:

    Swift

    Auth.auth().signIn(
      withEmail: email,
      password: password
    ) { (result, error) in
      let authError = error as NSError
      if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver =
          authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        // ...
      } else {
        // Handle other errors such as wrong password.
      }
    }
    

    Objective-C

    [FIRAuth.auth signInWithEmail:email
                         password:password
                       completion:^(FIRAuthDataResult * _Nullable authResult,
                                    NSError * _Nullable error) {
        if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
            // User is not enrolled with a second factor and is successfully signed in.
            // ...
        } else {
            // The user is a multi-factor user. Second factor challenge is required.
        }
    }];
    

    إذا كان العامل الأول للمستخدم هو مقدِّم خدمة موحّد، مثل OAuth، سيتم رصد الخطأ بعد طلب getCredentialWith().

  2. إذا كان المستخدم لديه العديد من العوامل الثانوية المسجلة، فاسأله عن العامل الذي يجب استخدامه. يمكنك الحصول على رقم الهاتف المقنَّع باستخدام "resolver.hints[selectedIndex].phoneNumber" والاسم المعروض من خلال "resolver.hints[selectedIndex].displayName".

    Swift

    // Ask user which second factor to use. Then:
    if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID {
      // User selected a phone second factor.
      // ...
    } else if resolver.hints[selectedIndex].factorID == TotpMultiFactorID {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    

    Objective-C

    FIRMultiFactorResolver *resolver =
        (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
    
    // Ask user which second factor to use. Then:
    FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
    if (hint.factorID == FIRPhoneMultiFactorID) {
      // User selected a phone second factor.
      // ...
    } else if (hint.factorID == FIRTOTPMultiFactorID) {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    
  3. إرسال رسالة تحقق إلى هاتف المستخدم:

    Swift

    // Send SMS verification code.
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      // verificationId will be needed for sign-in completion.
    }
    

    Objective-C

    // Send SMS verification code
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumberWithMultiFactorInfo:hint
      UIDelegate:nil
      multiFactorSession:resolver.session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        if (error != nil) {
            // Failed to verify phone number.
        }
    }];
    
  4. بعد إرسال رمز SMS، اطلب من المستخدم التحقّق من الرمز واستخدامه لإنشاء PhoneAuthCredential:

    Swift

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. إعداد كائن تأكيد باستخدام بيانات الاعتماد:

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. التعامل بشكل نهائي مع عملية تسجيل الدخول. يمكنك بعد ذلك الوصول إلى النتيجة الأصلية لتسجيل الدخول، والتي تتضمن البيانات العادية الخاصة بموفِّر الخدمة وبيانات اعتماد المصادقة:

    Swift

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(with: assertion) { (authResult, error) in
      // authResult will also contain the user, additionalUserInfo, optional
      // credential (null for email/password) associated with the first factor sign-in.
    
      // For example, if the user signed in with Google as a first factor,
      // authResult.additionalUserInfo will contain data related to Google provider that
      // the user signed in with.
    
      // user.credential contains the Google OAuth credential.
      // user.credential.accessToken contains the Google OAuth access token.
      // user.credential.idToken contains the Google OAuth ID token.
    }
    

    Objective-C

    // Complete sign-in.
    [resolver resolveSignInWithAssertion:assertion
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                            NSError * _Nullable error) {
        if (error != nil) {
            // User successfully signed in with the second factor phone number.
        }
    }];
    

يوضح الرمز أدناه مثالاً كاملاً لتسجيل دخول مستخدم متعدد العوامل:

Swift

Auth.auth().signIn(
  withEmail: email,
  password: password
) { (result, error) in
  let authError = error as NSError?
  if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
    let resolver =
      authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver

    // Ask user which second factor to use.
    // ...

    // Then:
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo

    // Send SMS verification code
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      if error != nil {
        // Failed to verify phone number.
      }
      // Ask user for the SMS verification code.
      // ...

      // Then:
      let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationId!,
        verificationCode: verificationCodeFromUser)
      let assertion = PhoneMultiFactorGenerator.assertion(with: credential)

      // Complete sign-in.
      resolver.resolveSignIn(with: assertion) { (authResult, error) in
        if error != nil {
          // User successfully signed in with the second factor phone number.
        }
      }
    }
  }
}

Objective-C

[FIRAuth.auth signInWithEmail:email
                     password:password
                   completion:^(FIRAuthDataResult * _Nullable authResult,
                               NSError * _Nullable error) {
    if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
        // User is not enrolled with a second factor and is successfully signed in.
        // ...
    } else {
        FIRMultiFactorResolver *resolver =
            (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];

        // Ask user which second factor to use.
        // ...

        // Then:
        FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];

        // Send SMS verification code
        [FIRPhoneAuthProvider.provider
          verifyPhoneNumberWithMultiFactorInfo:hint
                                    UIDelegate:nil
                            multiFactorSession:resolver.session
                                    completion:^(NSString * _Nullable verificationID,
                                                NSError * _Nullable error) {
            if (error != nil) {
                // Failed to verify phone number.
            }

            // Ask user for the SMS verification code.
            // ...

            // Then:
            FIRPhoneAuthCredential *credential =
                [FIRPhoneAuthProvider.provider
                  credentialWithVerificationID:verificationID
                              verificationCode:kPhoneSecondFactorVerificationCode];
            FIRMultiFactorAssertion *assertion =
                [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

            // Complete sign-in.
            [resolver resolveSignInWithAssertion:assertion
                                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                                    NSError * _Nullable error) {
                if (error != nil) {
                    // User successfully signed in with the second factor phone number.
                }
            }];
        }];
    }
}];

تهانينا لقد نجحت في تسجيل دخول مستخدم باستخدام المصادقة متعددة العوامل.

الخطوات التالية