為您的 Flutter 應用程式添加多重身份驗證

如果您已升級至使用 Identity Platform 進行 Firebase 驗證,則可以將 SMS 多重驗證新增至您的 Flutter 應用程式中。

多重身份驗證 (MFA) 提高了應用程式的安全性。雖然攻擊者經常破壞密碼和社交帳戶,但攔截簡訊卻更加困難。

在你開始之前

  1. 啟用至少一個支援多重身份驗證的提供者。每個提供者都支援 MFA,電話身份驗證、匿名身份驗證和 Apple Game Center除外

  2. 確保您的應用程式正在驗證使用者電子郵件。 MFA 需要電子郵件驗證。這可以防止惡意行為者使用不屬於他們的電子郵件註冊服務,然後透過添加第二個因素來鎖定真正的所有者。

  3. Android :如果您尚未在Firebase 控制台中設定套用的 SHA-256 哈希,請執行此操作。有關查找應用程式的 SHA-256 雜湊值的信息,請參閱驗證您的客戶端

  4. iOS :在 Xcode 中,為您的專案啟用推播通知並確保您的 APNs 驗證金鑰已使用 Firebase Cloud Messaging (FCM) 配置。此外,您必須啟用遠端通知的後台模式。若要查看此步驟的深入說明,請查看Firebase iOS 手機驗證文件。

  5. Web :確保您已在Firebase 控制台的OAuth 重定向網域下新增應用程式網域。

啟用多重身份驗證

  1. 開啟 Firebase 控制台的驗證 > 登入方法頁面。

  2. 「進階」部分中,啟用簡訊多重身份驗證

    您還應該輸入用於測試應用程式的電話號碼。雖然可選,但強烈建議註冊測試電話號碼,以避免開發過程中受到限制。

  3. 如果您尚未授權套用的網域,請將其新增至 Firebase 控制台的「驗證」>「設定」頁面上的允許清單中。

選擇註冊模式

您可以選擇應用程式是否需要多重身份驗證,以及註冊用戶的方式和時間。一些常見的模式包括:

  • 將使用者的第二個因素註冊為註冊的一部分。如果您的應用程式需要對所有使用者進行多重身份驗證,請使用此方法。

  • 提供可跳過的選項以在註冊過程中註冊第二個因素。想要鼓勵但不要求多因素身份驗證的應用程式可能會更喜歡這種方法。

  • 提供從使用者帳戶或個人資料管理頁面(而非註冊畫面)新增第二個因素的功能。這最大限度地減少了註冊過程中的摩擦,同時仍為安全敏感的用戶提供多因素身份驗證。

  • 當使用者想要存取安全要求更高的功能時,需要逐步加入第二個因素。

註冊第二個因素

若要為用戶註冊新的次要因素:

  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. 發送簡訊代碼後,請用戶驗證該代碼:

    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/master/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: (_) {},
  );

恭喜!您已成功為使用者註冊了第二個身份驗證因素。

使用第二個因素讓使用者登入

若要使用兩步驟簡訊驗證登入用戶:

  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/master/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) {
  ...
} 

恭喜!您使用多重身份驗證成功登入使用者。

下一步是什麼