Javascript 如何在令牌过期和/或刷新页面后使用firebase在Next.js中保持身份验证
我正在用firebase和next.js构建一个应用程序。我在firebase的身份验证流方面遇到了困难。一切进展顺利,直到我注意到一个bug/错误。如果我让电脑登录应用程序一段时间,并且每当我刷新页面时,这两种情况似乎都会导致我被重定向回登录页面。我可以理解为什么刷新会触发我的重定向,因为可能没有足够的时间及时检查onAuthStateChange以运行Javascript 如何在令牌过期和/或刷新页面后使用firebase在Next.js中保持身份验证,javascript,firebase,firebase-authentication,next.js,Javascript,Firebase,Firebase Authentication,Next.js,我正在用firebase和next.js构建一个应用程序。我在firebase的身份验证流方面遇到了困难。一切进展顺利,直到我注意到一个bug/错误。如果我让电脑登录应用程序一段时间,并且每当我刷新页面时,这两种情况似乎都会导致我被重定向回登录页面。我可以理解为什么刷新会触发我的重定向,因为可能没有足够的时间及时检查onAuthStateChange以运行const{user}=useAuth(),因为在初始页面加载时(刷新后)没有用户。它将进入{else},导致我在此时重定向。但有趣的是,如果
const{user}=useAuth()
,因为在初始页面加载时(刷新后)没有用户。它将进入{else}
,导致我在此时重定向。但有趣的是,如果我只是单击我的仪表板(一个受保护的页面)链接,我仍然是经过身份验证的。没有重定向。下面是我的身份验证组件的代码:
AuthComp.js:
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useAuth } from "../../context/AuthContext";
function LoadingScreen() {
return <div className="fixed top-0 right-0 h-screen w-screen z-50 flex justify-center items-center">
<div className="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-gray-900"></div>
</div>
}
export function AuthComp(props) {
const [isAuthenticated, setIsAuthenticated] = useState(false)
const router = useRouter();
const { user } = useAuth();
useEffect(() => {
let out;
if (user) {
setIsAuthenticated(true)
return () => out ? out() : null
} else {
router.push('/auth')
}
return () => out;
}, [user])
if (!user && (!isAuthenticated)) {
return <LoadingScreen />;
}
return <props.Component user={user} />
};
从“下一个/路由器”导入{useRouter};
从“react”导入{useffect,useState};
从“../../context/AuthContext”导入{useAuth}”;
函数加载屏幕(){
返回
}
导出函数AuthComp(道具){
常量[isAuthenticated,setIsAuthenticated]=useState(false)
const router=useRouter();
const{user}=useAuth();
useffect(()=>{
放出;
如果(用户){
setIsAuthenticated(真)
return()=>out?out():null
}否则{
router.push(“/auth”)
}
返回()=>out;
},[用户])
如果(!user&&(!isAuthenticated)){
返回;
}
返回
};
以下是我的auth上下文文件的代码:AuthContext.js:
import React, { useState, useEffect, createContext, useContext } from 'react'
import { fbase } from '../utils/auth/firebaseClient'
export const AuthContext = createContext()
export default function AuthProvider({ children }) {
const [user, setUser] = useState(null)
const [loadingUser, setLoadingUser] = useState(true) // Helpful, to update the UI accordingly.
useEffect(() => {
// Listen authenticated user
const unsubscriber = fbase.auth.onAuthStateChanged(async (user) => {
try {
if (user) {
setUser(user);
} else {
setUser(null)
return;
}
} catch (error) {
// Most probably a connection error. Handle appropriately.
console.log('an error occurred', error);
} finally {
setLoadingUser(false)
}
})
// Unsubscribe auth listener on unmount
return () => unsubscriber()
}, [])
return (
<AuthContext.Provider value={{ user, setUser, loadingUser }}>
{children}
</AuthContext.Provider>
)
}
// Custom hook that shorthands the context!
export const useAuth = () => useContext(AuthContext)
import React,{useState,useffect,createContext,useContext}来自“React”
从“../utils/auth/firebaseClient”导入{fbase}
export const AuthContext=createContext()
导出默认函数AuthProvider({children}){
const[user,setUser]=useState(null)
const[loadingUser,setLoadingUser]=useState(true)//有助于相应地更新UI。
useffect(()=>{
//侦听经过身份验证的用户
const unsubscriber=fbase.auth.onAuthStateChanged(异步(用户)=>{
试一试{
如果(用户){
setUser(用户);
}否则{
setUser(空)
返回;
}
}捕获(错误){
//很可能是连接错误。请正确处理。
console.log('发生错误',错误);
}最后{
setLoadingUser(错误)
}
})
//卸载时取消订阅身份验证侦听器
return()=>unsubscriber()
}, [])
返回(
{儿童}
)
}
//自定义钩子,简化上下文!
导出常量useAuth=()=>useContext(AuthContext)
可能没有足够的时间及时签入onAuthStateChange
来自onAuthStateChanged
的第一个用户结果肯定不会立即发生。您应该预计第一次回调将需要一些时间,因为首先加载用户的持久化令牌,然后进行验证
在第一次触发回调之前,您应该假设用户处于“未知”状态。在第一次回调之前,用户既不会登录也不会注销。我建议您在编写应用程序时考虑到这种三元状态。(相关,firebase.auth().currentUser
在第一次加载页面时将始终为空。)要了解更多有关此行为的信息,我建议阅读