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

使用Firebase Authentication时刷新页面用户自动登出的原因排查

问题:刷新页面用户自动登出,setPersistence无效

我为个人项目开发了一套简单的登录注册功能,首次开发这类功能时遇到刷新页面用户就会登出的问题。怀疑是认证创建方式有误,但无法定位原因,尝试过使用setPersistence但没有效果。

登录页面代码

import React, { useState, useEffect } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { signInWithEmailAndPassword } from 'firebase/auth'
import { auth } from '/src/firebase.js'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faX } from '@fortawesome/free-solid-svg-icons'
import { setPersistence, browserSessionPersistence, onAuthStateChanged } from 'firebase/auth'

export default function Login({ userLogin, setUserLogin }) {

  useEffect(() => {
    if (typeof grecaptcha !== 'undefined') {
      grecaptcha.ready(() => {
        grecaptcha.render('Login-reCaptcha')
      })
    }
  },[])

  const [submitNotification,setSubmitNotification] = useState({
    show: false,
    msg: ""
  }) // notification for any responses from requests e.g form submition

  function handleInput(e) {
    setUserLogin(prevInput => {return {
      ...prevInput,
      [e.target.name]: e.target.value
    }})
  }

  const navigate = useNavigate()

  const handleLogin = async (e) => {
    e.preventDefault(e)
    if (grecaptcha.getResponse() == "") {
      setSubmitNotification(prevValue => {return{
          show: true,
          msg: "Please complete the reCaptcha!"
        }})
    } else {
      try {
        const user = await signInWithEmailAndPassword(
          auth,
          userLogin.email,
          userLogin.password
        )
        if (user) {
          handleUserAuth()
          navigate("/Getting-Started")
        }
      } catch(error) {
        console.log(error)
        if (error == "FirebaseError: Firebase: Error (auth/user-not-found).") {
          setSubmitNotification(prevValue => {return {
            show: true,
            msg: "User does not exist."
          }})
        } else if (error == "FirebaseError: Firebase: Error (auth/wrong-password).") {
          setSubmitNotification(prevValue => {return{
            show: true,
            msg: "Your email/password is incorrect"
          }})
        } else if (error == "FirebaseError: Firebase: Access to this account has been temporarily disabled due to many failed login attempts. You can immediately restore it by resetting your password or you can try again later. (auth/too-many-requests).") {
          setSubmitNotification(prevValue => {return {
            show: true,
            msg: "User blocked, please try again later"
          }})
        }
      }
    } 
  }

  const handleUserAuth = async (e) => {
    setPersistence(auth, browserSessionPersistence)
    .then(() => {
      console.log("persistence ran")
      return signInWithEmailAndPassword(auth, userLogin.email, userLogin.password)
    })
    .catch((error) => {
      console.log(error.code)
      console.log(error.messsage)
    })
  }

  return (
    <div>
      <div className='flex justify-start items-center w-full h-24 p-4 absolute top-5 left-0'>
        <img className='h-28 hidden sm:inline-block' src='/ebayanalyticscut.png'></img>
      </div>
      <div className='h-screen flex justify-center items-center text-white font-poppins'>
        <div className='sm:bg-secondary h-[550px] w-[530px] rounded-2xl overflow-hidden p-5'>
          <form className='flex flex-col items-center justify-center' onSubmit={handleLogin}>
            <h1 className='text-3xl mb-5 font-medium'>Welcome Back!</h1>
            {submitNotification.show &&
            <div className='bg-red w-[350px] h-10 rounded-md mb-4 flex flex-row items-center p-3'>
              <FontAwesomeIcon icon={faX} className='h-3 mr-4 mt-0.5 cursor-pointer' onClick={() => setSubmitNotification(prevValue => {return{...prevValue, show: false}})}/>
              <p className='text-lg'>{submitNotification.msg}</p>
            </div>}
            <div className='mb-6'>
            <label className='text-lg block'>Email Address</label>
            <input value={userLogin.email} autoComplete='off' name='email' type='email' className='h-10 w-[350px] rounded-xl text-black pl-2 font-medium'  onChange={handleInput}></input>
            </div>
            <div className='mb-9'>
            <label className='text-lg block'>Password</label>
            <input value={userLogin.password} autoComplete='off' name='password' type='password' className='h-10 w-[350px] rounded-xl text-black pl-2 font-medium' onChange={handleInput}></input>
            </div>
            <div id='Login-reCaptcha' className="g-recaptcha mb-6 -mt-3" data-sitekey={import.meta.env.VITE_RECAPTCHA_SITE_KEY} data-theme="dark"></div>
            <button className='bg-accent w-[300px] h-12 rounded-2xl text-lg mb-5'>Log In</button>
            <p>Don't have an account? <Link to='/' className='underline text-accent'>Sign Up</Link></p>
          </form>
        </div>
      </div>
    </div>
  )
}

App.jsx代码

import React, { useState } from 'react'
import './index.css'
import { Routes, Route } from 'react-router-dom'
import Signup from './pages/Signup'
import Login from './pages/Login'
import Start from './pages/Start'
import { setPersistence, browserSessionPersistence, signInWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth'
import { auth } from '/src/firebase.js'

export default function App() {

  const [userSignup, setUserSignup] = useState({
    email: "",
    password: "",
    confirmpassword: ""
  })
  
  const [userLogin,setUserLogin] = useState({
    email: "",
    password: ""
  })

  return (
    <div>
      <Routes>
        <Route path='/' element={<Signup 
        userSignup={userSignup} 
        setUserSignup={setUserSignup}/>}/>
        <Route path='/Login' 
        element={<Login 
        userLogin={userLogin} 
        setUserLogin={setUserLogin}/>}/>
        <Route path='/Getting-Started' 
        element={<Start 
        userLogin={userLogin}/>} />
      </Routes>
    </div>
  )
}

问题分析与修复方案

1. 重复登录导致持久化设置失效

你在handleLogin中已经调用了signInWithEmailAndPassword完成登录,随后又调用handleUserAuth再次执行登录操作,这会覆盖之前的登录状态,且setPersistence必须在登录前设置才能生效。

修改handleLogin,将持久化设置和登录合并为一次操作:

const handleLogin = async (e) => {
  e.preventDefault(e)
  if (grecaptcha.getResponse() == "") {
    setSubmitNotification(prevValue => ({
      show: true,
      msg: "Please complete the reCaptcha!"
    }))
  } else {
    try {
      // 先设置持久化,再执行登录
      await setPersistence(auth, browserSessionPersistence)
      const user = await signInWithEmailAndPassword(
        auth,
        userLogin.email,
        userLogin.password
      )
      if (user) {
        navigate("/Getting-Started")
      }
    } catch(error) {
      console.log(error)
      // 优化错误判断逻辑,直接用error.code更可靠
      if (error.code === "auth/user-not-found") {
        setSubmitNotification({ show: true, msg: "User does not exist." })
      } else if (error.code === "auth/wrong-password") {
        setSubmitNotification({ show: true, msg: "Your email/password is incorrect" })
      } else if (error.code === "auth/too-many-requests") {
        setSubmitNotification({ show: true, msg: "User blocked, please try again later" })
      }
    }
  } 
}

同时删除多余的handleUserAuth函数。

2. 未监听用户认证状态变化

当前App组件依赖userLogin这个表单状态来传递用户信息,但页面刷新后useState的初始值会重置为空对象,导致页面认为用户未登录。需要通过Firebase的onAuthStateChanged实时监听用户认证状态,同步到全局状态。

修改App.jsx:

import React, { useState, useEffect } from 'react'
import './index.css'
import { Routes, Route, Navigate } from 'react-router-dom'
import Signup from './pages/Signup'
import Login from './pages/Login'
import Start from './pages/Start'
import { onAuthStateChanged } from 'firebase/auth'
import { auth } from '/src/firebase.js'

export default function App() {

  const [userSignup, setUserSignup] = useState({
    email: "",
    password: "",
    confirmpassword: ""
  })
  
  const [userLogin,setUserLogin] = useState({
    email: "",
    password: ""
  })
  // 添加全局用户状态,存储Firebase返回的用户对象
  const [currentUser, setCurrentUser] = useState(null)

  useEffect(() => {
    // 监听用户认证状态变化
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user)
      // 如果用户存在,同步邮箱到userLogin状态(可选)
      if (user) {
        setUserLogin(prev => ({ ...prev, email: user.email }))
      }
    })
    // 组件卸载时取消监听
    return unsubscribe
  }, [])

  // 可选:添加路由保护,未登录时跳转到登录页
  const ProtectedRoute = ({ children }) => {
    if (!currentUser) {
      return <Navigate to="/Login" />
    }
    return children
  }

  return (
    <div>
      <Routes>
        <Route path='/' element={<Signup 
        userSignup={userSignup} 
        setUserSignup={setUserSignup}/>}/>
        <Route path='/Login' 
        element={<Login 
        userLogin={userLogin} 
        setUserLogin={setUserLogin}/>}/>
        <Route path='/Getting-Started' 
        element={
          <ProtectedRoute>
            <Start user={currentUser} />
          </ProtectedRoute>
        } />
      </Routes>
    </div>
  )
}

同时修改Start组件,接收user props而不是userLogin,因为currentUser包含完整的认证用户信息。

3. 注意持久化类型

browserSessionPersistence表示会话级持久化,关闭浏览器标签页后登录状态会消失。如果需要持久化到浏览器缓存(关闭浏览器后仍保留),可以改用browserLocalPersistence。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:10:56