我在我的Next.js应用程序中使用firebase进行身份验证,我也有一个服务于REST API的快速服务器,它有一个中间件,使用firebase-admin
来验证从我的应用程序发送的idToken
,通过身份验证的路由
目前
由firebase生成的idToken持续一个小时,如果客户端仍然在我的应用程序和点击任何需要idToken
的路由,如果idToken
过期,那么服务器只是抛出一个未经身份验证的错误,这是很好的工作,但这不是想要的,我知道我的用户在那里,只是idToken
过期
如何刷新用户的idToken
,如果它已经过期,而不必在浏览器中进行完全刷新以获得新的idToken
一些代码
AuthContext.tsx
/* eslint-disable no-unused-vars */
import { useRouter } from 'next/router'
import nookies from 'nookies'
import { createContext, useContext, useEffect, useState } from 'react'
import { axios } from '../config/axios'
import firebase from '../config/firebase'
import { AuthUser } from '../types'
import { BaseUser } from '../types/user'
import { getProvider } from '../utils/oAuthProviders'
type AuthContextType = {
user: AuthUser | null
login: (email: string, password: string) => Promise<any>
signup: (email: string, password: string) => Promise<any>
logout: () => Promise<any>
oAuthLogin: (provider: string) => Promise<any>
}
const AuthContext = createContext<AuthContextType>({} as AuthContextType)
export const useAuth = () => useContext(AuthContext)
const fromPaths = ['/login', '/signup']
const formatUser = (user: BaseUser, idToken: string): AuthUser => {
return {
...user,
idToken,
}
}
export const AuthContextProvider = ({ children }: { children: React.ReactNode }) => {
const [user, setUser] = useState<AuthUser | null>(null)
const [loading, setLoading] = useState(true)
const router = useRouter()
console.log(user)
useEffect(() => {
const unsub = firebase.auth().onIdTokenChanged((user) => {
if (user) {
user
.getIdToken()
.then(async (idToken) => {
try {
const userResp = await axios.get('/user/me', {
headers: {
Authorization: `Bearer ${idToken}`,
},
})
nookies.set(undefined, 'idk', idToken, { path: '/' })
const {
data: { userFullDetials },
} = userResp
setUser(formatUser(userFullDetials, idToken))
setLoading(false)
if (fromPaths.includes(router.pathname)) {
router.push('/home')
}
} catch (err) {
console.log(err)
setUser(null)
setLoading(false)
}
})
.catch((err) => {
console.log(err.message)
setUser(null)
setLoading(false)
})
} else {
setLoading(false)
setUser(null)
}
})
return () => unsub()
}, [router])
const login = (email: string, password: string) => {
return firebase.auth().signInWithEmailAndPassword(email, password)
}
const signup = (email: string, password: string) => {
return firebase.auth().createUserWithEmailAndPassword(email, password)
}
const oAuthLogin = (provider: string) => {
return firebase.auth().signInWithPopup(getProvider(provider))
}
const logout = async () => {
setUser(null)
await firebase.auth().signOut()
}
const returnObj = {
user,
login,
signup,
logout,
oAuthLogin,
}
return (
<AuthContext.Provider value={returnObj}>
{loading ? (
<div className="flex items-center justify-center w-full h-screen bg-gray-100">
<h1 className="text-indigo-600 text-8xl">S2Media</h1>
</div>
) : (
children
)}
</AuthContext.Provider>
)
}
// auth.ts
// Auth Middleware in express
import { NextFunction, Request, Response } from 'express'
import fbadmin from 'firebase-admin'
import { DecodedIdToken } from '../types/index'
export default async (req: Request, res: Response, next: NextFunction) => {
const authorization = req.header('Authorization')
if (!authorization || !authorization.startsWith('Bearer')) {
return res.status(401).json({
status: 401,
message: 'authorization denied',
})
}
const idToken = authorization.split(' ')[1]
if (!idToken) {
return res.status(401).json({
status: 401,
message: 'authorization denied',
})
}
try {
const decodedToken = await fbadmin.auth().verifyIdToken(idToken)
req.user = decodedToken as DecodedIdToken
return next()
} catch (err) {
console.log(err.message)
return res.status(401).json({
status: 401,
message: 'authorization denied',
})
}
}
Firebase SDK会为你做这些。无论何时调用user.getIdToken()
,它都会返回一个有效的令牌。如果现有令牌过期,它将刷新并返回一个新令牌。您可以使用onIdTokenChanged()
,它将在令牌刷新时触发并将其存储在您的状态中。
然而,当你向服务器发出API请求时,我没有看到使用getIdToken()
方法的任何缺点。你不需要处理IdToken观察者,总是获得有效的令牌。
const makeAPIRequest = async () => {
// get token before making API request
const token = await user.getIdToken()
// pass the token in request headers
}
现在你的代码在令牌刷新时向服务器发出请求以获取用户信息,这可能是冗余的。