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

Firebase电话认证非测试号码出现无效验证器错误的原因与解决

Firebase手机号验证:非测试号码触发reCAPTCHA无效错误的排查与解决

问题场景

  • 测试号码(Sim1):已添加至Firebase控制台「Phone Authentication」测试号码列表,验证流程正常,输入号码后收到OTP发送提示,使用控制台预定义OTP可成功创建用户。
  • 真实号码(Sim2):未添加为测试号码,输入号码后reCAPTCHA组件正常显示,完成验证后未收到OTP,触发错误:The phone verification request contains an invalid application verifier. The reCAPTCHA token response is either invalid or expired.

代码实现

Firebase配置

import firebase from 'firebase';
import "firebase/auth";

// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "xxxxxxxxxxxxx",
  authDomain: "xxxxxxxxxx",
  projectId: "xxxxxxxxx",
  storageBucket: "xxxxxxxxxx",
  messagingSenderId: "xxxxxxxxxxxx",
  appId: "xxxxxxxxxxxxxx",
  measurementId: "xxxxxxxxxxxxx"
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);

const projectStorage = firebase.storage();
const projectFirestore = firebase.firestore();
const timestamp = firebase.firestore.FieldValue.serverTimestamp();
const auth = firebase.auth();
const provider = new firebase.auth.GoogleAuthProvider();
const increment = firebase.firestore.FieldValue.increment(1);

export { projectStorage, projectFirestore, timestamp, auth, provider, increment, firebase };

React验证组件

import React, { useState } from "react";
import firebase from "firebase"; // Import the Firebase config

const PhoneAuth = () => {
  const [phoneNumber, setPhoneNumber] = useState("");
  const [otp, setOtp] = useState("");
  const [verificationId, setVerificationId] = useState("");

  // Initialize reCAPTCHA
  const initializeRecaptcha = () => {
    window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier("recaptcha-container", {
      size: "normal",
      callback: (response) => {
        console.log("reCAPTCHA solved:", response);
      },
      "expired-callback": () => {
        console.error("reCAPTCHA expired. Please try again.");
      },
    });
    window.recaptchaVerifier.render().then((widgetId) => {
      console.log("reCAPTCHA rendered with widget ID:", widgetId);
    });
  };

  // Send OTP
  const handleSendCode = () => {
    initializeRecaptcha(); // Reinitialize reCAPTCHA every time

    const appVerifier = window.recaptchaVerifier;
    firebase
      .auth()
      .signInWithPhoneNumber(phoneNumber, appVerifier)
      .then((confirmationResult) => {
        setVerificationId(confirmationResult.verificationId);
        alert("OTP sent!");
      })
      .catch((error) => {
        console.error("Error during phone auth:", error);
        alert(error.message);
      });
  };

  // Verify OTP
  const handleVerifyCode = () => {
    const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, otp);
    firebase
      .auth()
      .signInWithCredential(credential)
      .then(() => {
        alert("Phone number verified successfully!");
      })
      .catch((error) => {
        console.error("Error verifying OTP:", error);
        alert(error.message);
      });
  };

  return (
    <div className="phone-auth">
      <h2>Phone Authentication</h2>
      <div id="recaptcha-container"></div>
      <input
        type="text"
        placeholder="Enter phone number (+1234567890)"
        value={phoneNumber}
        onChange={(e) => setPhoneNumber(e.target.value)}
      />
      <button onClick={handleSendCode}>Send OTP</button>
      <input
        type="text"
        placeholder="Enter OTP"
        value={otp}
        onChange={(e) => setOtp(e.target.value)}
      />
      <button onClick={handleVerifyCode}>Verify OTP</button>
    </div>
  );
};

export default PhoneAuth;

已尝试的排查步骤

  • 确认Firebase控制台中Phone Authentication功能已启用;
  • 已将应用域名(如开发环境的localhost)添加至授权域名列表;
  • 验证reCAPTCHA组件对测试号码可正常工作,但对非测试号码失效。

核心问题

为何非测试号码会触发该无效验证器错误?如何解决此问题?

解决方案

1. 修复reCAPTCHA重复初始化问题

当前代码每次点击「Send OTP」都会重新创建reCAPTCHA实例,覆盖旧实例后,用户完成的旧实例验证token会失效。修改方式:

  • 组件挂载时仅初始化一次reCAPTCHA;
  • 需要重置验证时调用reset()方法,而非重新创建实例。

修改后的关键代码:

// 组件挂载时初始化reCAPTCHA
React.useEffect(() => {
  initializeRecaptcha();
}, []);

const initializeRecaptcha = () => {
  // 避免重复创建实例
  if (!window.recaptchaVerifier) {
    window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier("recaptcha-container", {
      size: "normal",
      callback: (response) => {
        console.log("reCAPTCHA solved:", response);
      },
      "expired-callback": () => {
        console.error("reCAPTCHA expired. Please try again.");
        alert("验证码已过期,请重新验证");
        window.recaptchaVerifier.reset();
      },
    });
    window.recaptchaVerifier.render().then((widgetId) => {
      console.log("reCAPTCHA rendered with widget ID:", widgetId);
    });
  }
};

// Send OTP
const handleSendCode = () => {
  // 重置reCAPTCHA,确保每次请求使用新的验证会话
  window.recaptchaVerifier.reset();

  // 手机号格式校验
  if (!phoneNumber.startsWith('+') || phoneNumber.length < 10) {
    alert("请输入正确的手机号格式,需包含国家代码(如+8613xxxxxxxxx)");
    return;
  }

  const appVerifier = window.recaptchaVerifier;
  firebase
    .auth()
    .signInWithPhoneNumber(phoneNumber, appVerifier)
    .then((confirmationResult) => {
      setVerificationId(confirmationResult.verificationId);
      alert("OTP sent!");
    })
    .catch((error) => {
      console.error("Error during phone auth:", error);
      alert(error.message);
    });
};

2. 确保手机号格式合规

Firebase要求手机号必须包含正确的国家代码(如+86、+1),且无空格、特殊字符。添加前置校验可避免因格式错误导致的隐性验证失败。

3. 检查reCAPTCHA配置一致性

  • 确认Firebase控制台「Phone Authentication」的reCAPTCHA类型设置正确(开发环境可选v2,生产环境推荐invisible类型);
  • 若使用自定义域名,确保域名已同时添加到Firebase授权域名列表和Google reCAPTCHA控制台的允许域名列表。

内容的提问来源于stack exchange,提问作者Minenhle Solson Cele

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:52:32