Phone 使用 firebase 和 flutter 进行身份验证

Phone authentication with firebase and flutter

我正在编写一个使用 phone 身份验证的 flutter 应用程序。我目前正在 iPhone 12 模拟器上运行该应用程序,当我测试该应用程序并实施 phone 编号时,我没有按计划从 firebase 收到 6 位代码。我想是因为其中一个提到了共鸣。

(我还没有苹果开发者账户,所以我给了 firebase 一个测试号码和代码,所以当我输入假号码时,我仍然会得到应用程序的代码(不是推送通知) ).

应用程序的一些点:

如果需要,我可以在真实设备上测试应用程序。
我现在不需要收到推送通知,我可以将验证码打印到控制台,这样我就能看到代码是否有效。
我想在将 phone 号码发送到此页面(验证页面)
后立即开始验证过程 现在我使用的是没有区号的完整号码。

模拟器可以用假号吗?或者我需要一个真正的?

谢谢,希望你能帮我解决问题。

这是我的代码(只有验证部分):

// imports are here

enum Status { waiting, error }

class VerificationCode extends StatefulWidget {
  const VerificationCode({Key? key, this.number}) : super(key: key);
  final number;
  @override
  _VerificationCodeState createState() => _VerificationCodeState(number);
}

class _VerificationCodeState extends State<VerificationCode> {
  late final phoneNumber;
  final _verKey = GlobalKey<FormState>();
  late String _verCode;
  late double _formHeight;
  final FirebaseAuth _auth = FirebaseAuth.instance;
  var _verificationId;
  var _status = Status.waiting;
  _VerificationCodeState(this.phoneNumber); // storing the phone number from other page

  @override
  void initState() {
    super.initState();
    _verifyPhoneNumber();
  }

  Future _verifyPhoneNumber() async {
    _auth.verifyPhoneNumber(
        phoneNumber: phoneNumber,
        verificationCompleted: (phonesAuthCredentials) async {},
        verificationFailed: (verificationFailed) async {},
        codeSent: (verificationId, reseningToken) async {
          setState(() {
            _verificationId = verificationId;
            print(_verificationId); // here I am printing the opt code so I will know what it is to use it
          });
        },
        codeAutoRetrievalTimeout: (verificationId) async {});
  }

  Future _sendCodeToFirebase({String? code}) async {
    if (_verificationId != null) {
      var credential = PhoneAuthProvider.credential(
        verificationId: _verificationId,
        smsCode: code!,
      );

      await _auth
          .signInWithCredential(credential)
          .then((value) {
            print("auth complete!");
          })
          .whenComplete(() {})
          .onError((error, stackTrace) {
            setState(() {
              _verKey.currentState!.reset();
              _status = Status.error;
            });
          });
    }
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    if (size.height <= 736) {
      _formHeight = (size.height * .05) + 6;
    } else {
      _formHeight = size.height * .048;
    }
    return Scaffold(
      body: SafeArea(
        child: GestureDetector(
          onTap: () => FocusScope.of(context).requestFocus(
            FocusNode(),
          ),
          child: SingleChildScrollView(
            child: ConstrainedBox(
              constraints: BoxConstraints(
                minHeight: size.height - 90,
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Stack(
                    alignment: Alignment.topLeft,
                    children: [
                      GestureDetector(
                        onTap: () {
                          Navigator.pop(context);
                        },
                        child: Padding(
                          padding: EdgeInsets.only(
                            top: size.height * .006,
                            left: size.width * .03,
                          ),
                          child: SvgPicture.asset(
                            "assets/arrow-back.svg",
                          ),
                        ),
                      ),
                    ],
                  ),
                  Padding(
                    padding: EdgeInsets.only(
                      top: size.height * .18,
                    ),
                    child: Center(
                      child: Text(
                        "AppName",
                        style: TextStyle(
                          fontSize: size.width * .096,
                          letterSpacing: size.width * .026,
                          fontWeight: FontWeight.w300,
                        ),
                      ),
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.only(top: size.height * .015),
                    child: Center(
                      child: Container(
                        margin: EdgeInsets.symmetric(
                          horizontal: size.width * .045,
                        ),
                        height: _formHeight,
                        child: Form(
                          key: _verKey,
                          child: TextFormField(
                            style: TextStyle(
                              fontSize: size.width * .035,
                            ),
                            decoration: InputDecoration(
                              counterText: "",
                              contentPadding:
                                  const EdgeInsets.fromLTRB(0, 10, 0, 0),
                              hintText: 'Verification Code',
                              enabledBorder: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(7.0),
                                borderSide: const BorderSide(
                                  color: Colors.black,
                                  width: 1.25,
                                ),
                              ),
                              focusedBorder: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(7.0),
                                borderSide: const BorderSide(
                                  color: Colors.black,
                                  width: 1.5,
                                ),
                              ),
                            ),
                            autofocus: false,
                            keyboardType: TextInputType.number,
                            cursorColor: Colors.black,
                            textAlign: TextAlign.center,
                            onChanged: (input) async {
                              if (input.length == 6) {
                                _verCode = input;
                                _sendCodeToFirebase(code: _verCode);
                              }
                            },
                            maxLength: 6,
                          ),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Firebase Phone iOS requires some setup steps as mentioned in the docs. The step where it mentions you need to enable push notifications is required to verify that the request is coming from your app, it's called "Silent APNs notifications" because it happens silently without the user being aware of it, but in case of testing, the reCAPTCHA verification is used, read more on this step in the official Firebase iOS docs 上的身份验证。

但是,为了在模拟器上进行测试,您确实可以使用您在 Phone 下的 Firebase 控制台中定义的假号码和短信代码身份验证 sign-in 方法。

首先,您需要setup reCAPTCHA verification for iOS Firebase SDK 的要求。

其次,add testing numbers testing Phone Auth locally

  1. 在 Firebase 控制台上,select“Phone”身份验证提供程序,然后单击“Phone 用于测试的数字”下拉菜单。
  2. 输入新的 phone 号码(例如 +44 7444 555666)和测试代码(例如 123456)。

添加测试编号注意以下事项:

  1. 在 Firebase 控制台中将号码添加到测试列表后,即使它们是真实的,您也不会收到关于它们的短信代码,您可以使用您已经在控制台中定义的代码。
  2. 如果您已经有一个使用相同 phone 号码的注册帐户,您将尝试添加到测试中,它会给出一个错误提示无法添加,请确保删除关联的帐户用号码或选择另一个号码。

Firebase接受的phone号码格式,必须有国家代码,并且遵循E.164标准,以+开头,后面跟着国家代码后跟 phone 号码。