添加多重身份验证(Flutter 应用)

如果您已升级到 Firebase Authentication with Identity Platform,可以向 Flutter 应用添加短信多重身份验证。

多重身份验证 (MFA) 可提高应用的安全性。虽然攻击者往往可窃取到密码和社交帐号,但截获短信却比较困难。

准备工作

  1. 请至少启用一个支持多重身份验证的提供方。每个提供方都支持 MFA,电话身份验证、匿名身份验证和 Apple Game Center 除外

  2. 确保您的应用会验证用户的电子邮件地址。MFA 要求验证电子邮件地址。这可防止图谋不轨者使用别人的电子邮件地址注册服务,然后通过添加第二重身份验证因素来阻止真正的电子邮件地址所有者注册。

  3. Android:如果您尚未在 Firebase 控制台中设置应用的 SHA-256 哈希,请执行此操作。如需了解如何查找应用的 SHA-256 哈希,请参阅对客户端进行身份验证

  4. iOS:在 Xcode 中为您的项目启用推送通知,并确保您已使用 Firebase Cloud Messaging (FCM) 配置了 APNs 身份验证密钥。如需查看此步骤的深入说明,请参阅 Firebase iOS 手机身份验证文档。

  5. Web:确保您已在 Firebase 控制台OAuth 重定向网域下添加您的应用网域。

启用多重身份验证

  1. 打开 Firebase 控制台的 Authentication > 登录方法页面。

  2. 高级部分中,启用短信多重身份验证

    您还应输入要用于测试应用的电话号码。虽然并非必需,但我们强烈建议您注册测试电话号码,以免在开发过程中受到限制。

  3. 如果您尚未向应用网域授权,请在 Firebase 控制台的 Authentication > 设置页面上将其添加到允许列表中。

选择注册模式

您可以选择应用是否要求多重身份验证,以及何时和如何注册用户。一些常见模式包括:

  • 在注册过程中注册用户的第二重身份验证。如果应用要求所有用户进行多重身份验证,请使用此方法。

  • 提供可在注册期间跳过第二重身份验证注册的选项。如果应用鼓励但不要求进行多重身份验证,可以使用此方法。

  • 提供从用户的帐号或个人资料管理页面(而不是注册屏幕)添加第二重身份验证的功能。这样可以使注册过程更顺畅,同时仍可为注重安全的用户提供多重身份验证。

  • 如果用户希望访问安全性要求更高的功能,再要求添加第二重身份验证。

注册第二重身份验证

如需为用户注册新的第二重身份验证,请执行以下操作:

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

恭喜!您已成功让使用多重身份验证的用户登录。

后续步骤