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

NextJS 14使用Firebase短信认证时this.auth.settings未定义问题求助

Firebase OTP认证在NextJS 14中出现this.auth.settings is undefined错误的解决方法

我在NextJS 14中尝试用Firebase短信OTP实现用户认证,点击Continue按钮调用onSignup函数时触发了this.auth.settings is undefined错误,相关代码和错误截图如下,请求协助解决。

相关代码

app/login/page.js

'use client'
import { useState, useRef } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation'
import { BiArrowBack } from 'react-icons/bi';
import PhoneInput from 'react-phone-input-2'
import 'react-phone-input-2/lib/style.css'
import styles from './Login.module.css';
import OTPInput from 'otp-input-react';
import { CgSpinner } from "react-icons/cg";
import { auth } from "../firebase";
import toast, { Toaster } from 'react-hot-toast';
import { RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
import { BsFillShieldLockFill, BsTelephoneFill } from "react-icons/bs";

export default function Login() {
    const router = useRouter()
    const [mobileNumber, setMobileNumber] = useState('');
    const [verificationId, setVerificationId] = useState('');
    const [otp, setOtp] = useState("");
    const [loading, setLoading] = useState(false);
    const [showOTP, setShowOTP] = useState(false);

    const recaptchaVerifier = useRef(null);
      const telephone = "+" + mobileNumber;

  function onCaptchVerify() {
    if (!window.recaptchaVerifier) {
      window.recaptchaVerifier = new RecaptchaVerifier(
        "recaptcha-container",
        {
          size: "invisible",
          callback: (response) => {
            onSignup();
          },
          "expired-callback": () => {},
        },
        auth
      );
    }
  }

  function onSignup() {
    setLoading(true);
    onCaptchVerify();
    const appVerifier = window.recaptchaVerifier;

    signInWithPhoneNumber(auth, telephone, appVerifier)
      .then((confirmationResult) => {
        window.confirmationResult = confirmationResult;
        setLoading(false);
        setShowOTP(true);
        toast.success("OTP sended successfully!");
      })
      .catch((error) => {
        console.log(error);
        setLoading(false);
      });
  }

  function onOTPVerify() {
    setLoading(true);
    window.confirmationResult
      .confirm(otp)
      .then(async (res) => {
        console.log(res);
        setUser(res.user);
        setLoading(false);
      })
      .catch((err) => {
        console.log(err);
        setLoading(false);
      });
  }

  return (
    <div className={styles.loginBody}>
      <div className={styles.loginHeader}>
        <div className={styles.arrow}>
          <Link href={'/'}><BiArrowBack /></Link>
        </div>
        <div className={styles.loginText}>
          <p>Login/Register</p>
        </div>
        <div></div>
      </div>
      {showOTP ? (
      <>
        <div className="bg-white text-purple-500 w-fit mx-auto p-4 rounded-full">
                  <BsFillShieldLockFill size={30} />
                </div>
                <div className="bg-f5f5f5 flex-grow flex flex-col items-center justify-center">
        <Toaster position="top-right" reverseOrder={false} toastOptions={{ duration: 2000 }} />

        <div className="bg-yellow-100 mx-6 my-12 p-6 rounded-md shadow-lg w-full max-w-md">
          <h2 className="text-black text-2xl font-semibold mb-6">Enter OTP</h2>

          <p className="text-black mb-6">We have sent an OTP to your mobile number.</p>
                <OTPInput
                  value={otp}
                  onChange={setOtp}
                  OTPLength={6}
                  otpType="number"
                  disabled={false}
                  autoFocus
                  inputClassName="w-full text-purple-500 border-red-500 focus:ring-red-500 focus:border-red-500 rounded-md mx-auto"
                ></OTPInput>
                <button
                  onClick={onOTPVerify}
                  className="bg-purple-400 w-full mt-4 flex gap-1 items-center justify-center py-2.5 text-white rounded"
                >
                  {loading && (
                    <CgSpinner size={20} className="mt-1 animate-spin" />
                  )}
                  <span>Verify OTP</span>
                </button>
                </div>
                </div>
              </> 
      ) : (
      
      <>
        <div className={styles.loginSubBody}>
        <div className={styles.mobileNumber}>
        <PhoneInput
            country={'in'}
            value={mobileNumber}
            onChange={setMobileNumber}
          />
        </div>
        <div className={styles.continue}>
          <button onClick={onSignup}>Continue</button>
        </div>
      </div>
      </>)}

    </div>
  );
}

firebase.js(已隐藏敏感信息)

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID",
  measurementId: "YOUR_MEASUREMENT_ID",
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

export const auth = getAuth(app);

错误截图

错误截图


问题分析与修复方案

这个错误主要由三个原因导致:缺少Recaptcha挂载容器、Recaptcha初始化时机异步问题、未声明setUser状态。以下是具体修复步骤:

1. 添加Recaptcha容器元素

RecaptchaVerifier需要挂载到指定ID的DOM元素上,即使是隐形模式也必须存在。在登录页面的非OTP显示区域添加容器:

{/* 在showOTP为false的JSX块中添加 */}
<div className={styles.loginSubBody}>
  <div id="recaptcha-container"></div> {/* 新增这行 */}
  <div className={styles.mobileNumber}>
    <PhoneInput
        country={'in'}
        value={mobileNumber}
        onChange={setMobileNumber}
      />
  </div>
  <div className={styles.continue}>
    <button onClick={onSignup}>Continue</button>
  </div>
</div>

2. 修复Recaptcha初始化与异步逻辑

调整onCaptchVerify为异步函数,确保初始化完成后再调用signInWithPhoneNumber,同时补全setUser的状态声明:

// 新增用户状态声明
const [user, setUser] = useState(null);

// 修改onCaptchVerify为异步返回Promise
function onCaptchVerify() {
  return new Promise((resolve) => {
    if (!window.recaptchaVerifier) {
      window.recaptchaVerifier = new RecaptchaVerifier(
        "recaptcha-container",
        {
          size: "invisible",
          callback: (response) => resolve(response),
          "expired-callback": () => {},
        },
        auth
      );
    }
    resolve(window.recaptchaVerifier);
  });
}

// 修改onSignup为async/await处理异步
async function onSignup() {
  setLoading(true);
  try {
    const appVerifier = await onCaptchVerify();
    const confirmationResult = await signInWithPhoneNumber(auth, telephone, appVerifier);
    window.confirmationResult = confirmationResult;
    setLoading(false);
    setShowOTP(true);
    toast.success("OTP发送成功!");
  } catch (error) {
    console.log(error);
    setLoading(false);
    toast.error("发送OTP失败,请重试");
  }
}

3. 额外检查项

  • 确认Firebase控制台已启用电话认证方式,并添加了正确的应用域名
  • 保持Firebase SDK版本为最新,避免版本兼容问题
  • 测试时使用真实手机号码,模拟器无法接收OTP

内容的提问来源于stack exchange,提问作者Sparsh Gahlot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:12:03