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

React项目中SignInWithRedirect功能异常问题求助

问题描述
  • 使用signInWithRedirect进行谷歌登录时,页面会跳转到谷歌,但仅显示顶部蓝色进度条就立刻返回网站,无法看到谷歌的账号登录/选择界面
  • 之前尝试signInWithPopup时出现跨域错误
  • 成功登录一次后,无法再次触发登录流程或切换其他账号;登出后使用同一邮箱登录也失效
相关代码

React主组件代码

import './app.css'
import { Route, Routes } from 'react-router-dom'
import { Link, useMatch, useResolvedPath } from "react-router-dom"
import Home from './pages/home.jsx'
import PPapers from './pages/pastp.jsx'
import Logo from './images/re.svg'
import 'bootstrap/dist/css/bootstrap.min.css';
import { auth, googleProvider } from './config/firebase-config';
import { createUserWithEmailAndPassword, signInWithRedirect, signOut, signInWithEmailAndPassword, getRedirectResult } from 'firebase/auth'; 
import { useEffect, useState } from 'react';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import GoogleLogo from './images/google.png'
import { onAuthStateChanged } from 'firebase/auth';

function NavBar(){
  const [cLogged, setLoggedIn] = useState(false);
  const [modalShow, setModalShow] = useState(false);

  // 优化认证状态监听:只订阅一次
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setLoggedIn(!!user);
      if (user) {
        console.log(user.email);
      }
    });

    // 组件卸载时取消订阅
    return unsubscribe;
  }, []);

  const logout = async () => {
    try {
      await signOut(auth);
    } catch (err) {
      console.error(err);
    }
  };

  return (
    <> 
      <div className = "navBar">
      <div id='LHS'>
        <img src= {Logo} alt="" />
          <label className="hamburger-menu">
          <input type="checkbox" />
          </label>
          <aside className="sidebar">
            <nav>
                <div><CustomLink className = "links" to="/">Home</CustomLink></div>
                <div><CustomLink className = "links" to="/pastp">Past Papers</CustomLink></div>
            </nav>
          </aside>
      </div>
        <div id="RHS">
          {cLogged ? <button className='accountBtn' onClick={logout}>Logout</button>: <Button variant="primary" className = 'accountBtn'onClick={() => setModalShow(true)}>Log-In</Button>}
          <SignInPopup
            show={modalShow}
            onHide={() => setModalShow(false)}
          /> 
          <Link id='pBtn'>
            Get Premium
          </Link>
        </div>
      </div>
    </>
  )
}

function SignInPopup(props) {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [isLogin, setLogin] = useState(true);
  const [invalid, setInvalid] = useState(false);

  // 处理谷歌跳转后的认证结果
  useEffect(() => {
    const handleRedirectResult = async () => {
      try {
        const result = await getRedirectResult(auth);
        if (result) {
          // 认证成功,关闭弹窗
          props.onHide();
        }
      } catch (err) {
        console.error(err);
        setInvalid(true);
      }
    };

    handleRedirectResult();
  }, [props]);

  const signIn = async () => {
    try {
      if (isLogin) {
        // 登录用signInWithEmailAndPassword
        await signInWithEmailAndPassword(auth, email, password);
      } else {
        // 注册用createUserWithEmailAndPassword
        await createUserWithEmailAndPassword(auth, email, password);
      }
      props.onHide();
    } catch (err) {
      console.error(err);
      setInvalid(true);
    }
  };

  const signInWithGoogle = async () => {
    try {
      // 设置prompt强制显示账号选择界面
      googleProvider.setCustomParameters({ prompt: 'select_account' });
      await signInWithRedirect(auth, googleProvider);
    } catch (err) {
      console.error(err);
      setInvalid(true);
    }
  };

  function isInvalid() {
    return (
      email === "" ||
      password === "" ||
      password.length < 6 ||
      !email.includes('@') ||
      !email.includes('.')
    );
  }

  return (
    <Modal
      {...props}
      size="md"
      aria-labelledby="contained-modal-title-vcenter"
      centered
      data-bs-theme="dark"
      className='modal'
    >
      <Modal.Header closeButton>
        <Modal.Title id="contained-modal-title-vcenter">
          <h2>RevisionEase</h2>
        </Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <h4>{isLogin ? 'Sign-in' : 'Sign-Up'}</h4>
        <p className='signIn' onClick={() => setLogin(!isLogin)}>
          {isLogin ? 'Sign-Up?' : 'Sign-in?'}
        </p>
        <div className='su'>
          <input placeholder="Enter Email" onChange={e => setEmail(e.target.value)} />
          <input placeholder="Enter Password" type='password' onChange={e => setPassword(e.target.value)} />
          <button className='signIn' onClick={signIn}>{isLogin ? 'Sign-in' : 'Sign-up'}</button>
          <p>Or sign-in with:</p>
          <button className="googlesu" onClick={signInWithGoogle}>
            <img src={GoogleLogo} alt="" />
          </button>
          <p>{(invalid && isInvalid()) ? 'Please enter your details correctly' : ''}</p>
        </div>
      </Modal.Body>
      <Modal.Footer>
        <label>Log-in and Sign-up to gain access to more past papers and more!</label>
      </Modal.Footer>
    </Modal>
  );
}

function App() {
  return (
    <>
      <NavBar />
      <div className="maincontainer">
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/pastp" element={<PPapers />} />
        </Routes>
      </div>
    </>
  )
}

function CustomLink({ to, children, ...props }) {
  const resolvedPath = useResolvedPath(to)
  const isActive = useMatch({ path: resolvedPath.pathname, end: true })

  return (
    <li className={isActive ? "active" : ""}>
      <Link to={to} {...props}>
        {children}
      </Link>
    </li>
  )
}

export default App;

Firebase配置代码

import { getAuth, GoogleAuthProvider } from "firebase/auth";
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getStorage } from 'firebase/storage';

const firebaseConfig = {
  apiKey: "---",
  authDomain: "---",
  projectId: "---",
  storageBucket: "---",
  messagingSenderId: "---",
  appId: "---",
  measurementId: "---"
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const googleProvider = new GoogleAuthProvider();
export const db = getFirestore(app);
export const storage = getStorage(app);
关键修复点
  1. 处理signInWithRedirect的回调:在SignInPopup组件中添加getRedirectResult调用,负责接收谷歌跳转后的认证结果,否则Firebase无法完成认证流程,会直接返回网站。
  2. 强制账号选择:在signInWithGoogle中设置googleProvider.setCustomParameters({ prompt: 'select_account' }),避免谷歌自动登录已保存的账号,解决无法切换账号的问题。
  3. 修复登录/注册逻辑:原代码中不管登录还是注册都用createUserWithEmailAndPassword,登录应该调用signInWithEmailAndPassword。
  4. 优化认证状态监听:NavBar中的onAuthStateChanged只订阅一次,避免重复监听导致的状态异常,同时组件卸载时取消订阅。
  5. 跨域问题(Popup模式):登录Firebase控制台,进入认证->设置->授权域名,添加你的本地开发域名(比如localhost:3000),解决signInWithPopup的跨域错误。

内容的提问来源于stack exchange,提问作者Mohammed Aayan Pathan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:34:54