You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Firebase手机号认证:如何配置reCaptcha(无法用SafetyNet)

React Native Firebase手机号认证:切换reCaptcha替代SafetyNet

问题说明

使用Firebase手机号认证时触发错误(错误截图:Firebase手机号认证错误),因所在地区不在SafetyNet支持范围内,需改用reCaptcha完成认证。

实现步骤

1. Firebase控制台配置

  • 进入Firebase控制台,打开目标项目的Authentication -> 登录方法,启用手机号认证
  • 在项目设置 -> 常规标签下,确认已添加你的React Native应用(Android/iOS)

2. 保留原Firebase初始化代码

原配置无需改动,继续使用:

import firebase from '@react-native-firebase/app';
import auth from '@react-native-firebase/auth';

const firebaseConfig = {
  apiKey: 'xxxxxxxxxxxx',
  authDomain: 'xxxxxxxxxxxxx',
  projectId: 'xxxxxxxxxxxxx',
  storageBucket: 'xxxxxxxxxxxxxx',
  messagingSenderId: 'xxxxxxxxxxxxx',
  appId: 'xxxxxxxxxxxxxxx',
  measurementId: 'xxxxxxxxxxxxxx',
};

if (!firebase.apps.length) {
  firebase.initializeApp(firebaseConfig);
}

export default () => {
  return { firebase, auth };
};

3. 重写SignUPScreen组件(添加reCaptcha验证)

引入RecaptchaVerifier并完成验证逻辑:

import { useState, useEffect } from 'react';
import { View, TextInput, Button, Alert, StyleSheet } from 'react-native';
import { RecaptchaVerifier } from '@react-native-firebase/auth';
import firebaseSetup from './firebaseSetup';

const SignUPScreen = ({ navigation }) => {
  const { auth } = firebaseSetup();
  const [confirm, setConfirm] = useState(null);
  const [phoneNumber, setPhoneNumber] = useState('');
  const [verifier, setVerifier] = useState(null);

  useEffect(() => {
    const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container', {
      size: 'invisible', // 隐藏式自动验证,无需用户手动操作
      callback: () => {},
      'expired-callback': () => Alert.alert('验证过期,请重新操作')
    }, auth());
    setVerifier(recaptchaVerifier);
  }, [auth]);

  const signIn = async () => {
    if (!verifier) {
      Alert.alert('验证器未就绪,请稍后重试');
      return;
    }
    try {
      const confirmation = await auth().signInWithPhoneNumber(phoneNumber, verifier);
      setConfirm(confirmation);
      navigation.navigate('OTP', { confirm: confirmation });
    } catch (error) {
      Alert.alert('认证失败', error.message);
    }
  };

  return (
    <View style={styles.container}>
      {/* 必须保留此空View作为reCaptcha的挂载容器 */}
      <View id="recaptcha-container" />
      <TextInput
        style={styles.textInput}
        placeholder="输入手机号(带国家码,如+8613xxxxxxxxx)"
        keyboardType="phone-pad"
        onChangeText={setPhoneNumber}
        value={phoneNumber}
      />
      <Button title="获取验证码" onPress={signIn} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingHorizontal: 20,
  },
  textInput: {
    borderBottomWidth: 1,
    marginBottom: 20,
    paddingVertical: 10,
  },
});

export default SignUPScreen;

4. OTP组件优化(可选)

保留原逻辑,优化错误提示和输入限制:

import { useState } from 'react';
import { Alert, Button, StyleSheet, View } from 'react-native';
import { TextInput } from 'react-native-paper';

const OTP = ({ route }) => {
  const { confirm } = route.params;
  const [code, setCode] = useState('');

  const confirmCode = async () => {
    try {
      await confirm.confirm(code);
      Alert.alert('认证成功', '用户登录成功');
    } catch (error) {
      Alert.alert('验证码错误', '请输入正确的6位验证码');
    }
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.textInput}
        placeholder="输入6位验证码"
        value={code}
        onChangeText={setCode}
        keyboardType="number-pad"
        maxLength={6}
      />
      <Button title="确认验证码" onPress={confirmCode} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingHorizontal: 20,
  },
  textInput: {
    marginBottom: 20,
  },
});

export default OTP;

关键注意事项

  • 手机号必须包含国家码前缀(如+86),否则会触发认证失败
  • size: 'invisible'为隐藏式验证,系统自动完成,无需用户交互
  • 必须保留id="recaptcha-container"的空View,否则reCaptcha无法正常初始化

内容的提问来源于stack exchange,提问作者Asem Qoraishi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 07:15:34