javascript - 使用firebase auth时如何自动刷新idToken?
问题描述
我在我的 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()
and which 将在刷新令牌时触发并将其存储在您的状态中。
getIdToken()
但是,每当您向服务器发出 API 请求时,我看不到使用方法的任何缺点。您不必与 IdToken 观察者打交道并始终获得有效令牌。
const makeAPIRequest = async () => {
// get token before making API request
const token = await user.getIdToken()
// pass the token in request headers
}
现在,您的代码会在令牌刷新时向服务器发出请求以获取用户信息,这可能是多余的。
推荐阅读
- search - MarkLogic QBE 的问题始于功能、QBE API 中的 QBE 日期范围和搜索 API 中的搜索过滤条件
- lua - 创建填充为零的特定大小的 Lua 表的有效方法?
- c# - 这是一个排列的东西还是别的什么?
- matlab - 如何在背景图像上显示带有背景的透明图像
- typescript - 这种写作风格是什么意思?
- sql - Amazon Redshift:未定义 FROM 关键字的使用:DELETE table_name FROM
- javascript - 响应式 Flexbox 的三列布局
- ios - 如何为 uiscrollview 中的视图进行真正的独家触摸?
- docker - Dockerfile alpine image apk 找不到 Java 8
- ios - 如何将数据从 TableViewCell 传递到 ContainerView?