הוספת אימות רב-גורמי לאפליקציה ב-Flutter

אם שדרגתם לאימות ב-Firebase באמצעות Identity Platform, אתם יכולים להוסיף אימות רב-שלבי באמצעות SMS לאפליקציית Flutter.

אימות רב-שלבי (MFA) משפר את האבטחה של האפליקציה. אמנם תוקפים לרוב חודרים לסיסמאות ולחשבונות ברשתות החברתיות, אבל קשה יותר ליירט הודעת טקסט.

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

  1. מפעילים לפחות ספק אחד שתומך באימות רב-שלבי. כל הספקים תומכים באימות רב-שלבי, חוץ מאימות באמצעות טלפון, אימות אנונימי ו-Apple Game Center.

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

    • iOS+‎: ב-Xcode,‏ מפעילים הודעות פוש לפרויקט ומוודאים שמפתח האימות של APNs מוגדר עם העברת הודעות בענן ב-Firebase ‏ (FCM).

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

    • Android: אם עדיין לא עשיתם זאת, מציינים את טביעת האצבע מסוג SHA-256 של האפליקציה:

      1. במסוף Firebase, עוברים אל הגדרות > הכרטיסייה כללי.

      2. גוללים למטה אל הכרטיס האפליקציות שלך, בוחרים את אפליקציית Android ומוסיפים את טביעת האצבע מסוג SHA-256 בשדה טביעות אצבע של אישור SHA.

      במאמר אימות הלקוח מוסבר איך מקבלים את טביעת האצבע של ה-SHA של האפליקציה.

    • אינטרנט: אם עדיין לא עשיתם זאת, צריך לתת הרשאה לדומיין של האפליקציה:

      1. במסוף Firebase, עוברים אל Security (אבטחה) > Authentication (אימות) > הכרטיסייה Settings (הגדרות).

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

הפעלת אימות רב-שלבי

  1. במסוף Firebase, עוברים אל Security (אבטחה) >‏ Authentication (אימות).

  2. בכרטיסייה Sign-in method, בקטע Advanced, מפעילים את האפשרות SMS Multi-factor Authentication.

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

  3. אם עדיין לא עשיתם זאת, צריך לתת הרשאה לדומיין של האפליקציה:

    1. במסוף Firebase, עוברים אל Security (אבטחה) > Authentication (אימות) > הכרטיסייה Settings (הגדרות).

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

בחירת דפוס שיוך

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

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

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

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

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

רישום של גורם שני

כדי לרשום גורם משני חדש עבור משתמש:

  1. מאמתים מחדש את המשתמש.

  2. מבקשים מהמשתמש להזין את מספר הטלפון שלו.

  3. קבלת סשן עם אימות רב-שלבי עבור המשתמש:

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. מאמתים את מספר הטלפון באמצעות סשן עם אימות רב-שלבי והתקשרות חזרה:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: multiFactorSession,
      phoneNumber: phoneNumber,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // The SMS verification code has been sent to the provided phone number.
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  5. אחרי שקוד ה-SMS נשלח, צריך לבקש מהמשתמש לאמת את הקוד:

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. משלימים את ההרשמה:

    await user.multiFactor.enroll(
      PhoneMultiFactorGenerator.getAssertion(
        credential,
      ),
    );
    

בדוגמה הבאה מוצג קוד מלא להוספת אמצעי אימות נוסף:

  final session = await user.multiFactor.getSession();
  final auth = FirebaseAuth.instance;
  await auth.verifyPhoneNumber(
    multiFactorSession: session,
    phoneNumber: phoneController.text,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code:
      // https://github.com/firebase/flutterfire/blob/main/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await user.multiFactor.enroll(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );

כל הכבוד! הוספתם בהצלחה אמצעי אימות שני למשתמש.

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

כדי להכניס משתמש באמצעות אימות דו-שלבי ב-SMS:

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

    לדוגמה, אם הגורם הראשון של המשתמש היה אימייל וסיסמה:

    try {
      await _auth.signInWithEmailAndPassword(
          email: emailController.text,
          password: passwordController.text,
      );
      // User is not enrolled with a second factor and is successfully
      // signed in.
      // ...
    } on FirebaseAuthMultiFactorException catch (e) {
      // The user is a multi-factor user. Second factor challenge is required
      final resolver = e.resolver
      // ...
    }
    
  2. אם המשתמש רשום לכמה אמצעי אימות משניים, צריך לשאול אותו באיזה מהם הוא רוצה להשתמש:

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. שליחת הודעת אימות לטלפון של המשתמש עם הרמז והסשן של אימות רב-שלבי:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. מתקשרים אל resolver.resolveSignIn() כדי להשלים את האימות המשני:

    final smsCode = await getSmsCodeFromUser(context);
    if (smsCode != null) {
      // Create a PhoneAuthCredential with the code
      final credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: smsCode,
      );
    
      try {
        await e.resolver.resolveSignIn(
          PhoneMultiFactorGenerator.getAssertion(credential)
        );
      } on FirebaseAuthException catch (e) {
        print(e.message);
      }
    }
    

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

try {
  await _auth.signInWithEmailAndPassword(
    email: emailController.text,
    password: passwordController.text,
  );
} on FirebaseAuthMultiFactorException catch (e) {
  setState(() {
    error = '${e.message}';
  });
  final firstHint = e.resolver.hints.first;
  if (firstHint is! PhoneMultiFactorInfo) {
    return;
  }
  await FirebaseAuth.instance.verifyPhoneNumber(
    multiFactorSession: e.resolver.session,
    multiFactorInfo: firstHint,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code:
      // https://github.com/firebase/flutterfire/blob/main/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await e.resolver.resolveSignIn(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );
} catch (e) {
  ...
}

כל הכבוד! התחברתם בהצלחה כמשתמש באמצעות אימות רב-שלבי.

המאמרים הבאים