Next.js通过getServerSideProps获取Firebase当前登录用户报错如何解决
问题原因和修复方案
1. 修复auth/unsupported-persistence-type报错
这个报错的根源是firebaseClient.js会在Next.js的服务端渲染阶段也被执行,Node.js服务端环境没有浏览器的会话存储能力,调用setPersistence就会触发不支持的错误。你需要加客户端环境判断,只有在浏览器环境下才设置持久化:
// utils/firebaseClient.js import firebase from 'firebase'; const firebaseConfig = { // 已隐去配置信息 } if(!firebase.apps.length) { firebase.initializeApp(firebaseConfig); // 仅在客户端环境设置持久化 if (typeof window !== 'undefined') { firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION); } } else { firebase.app(); } const auth = firebase.auth(); const googleProvider = new firebase.auth.GoogleAuthProvider(); export { firebase, googleProvider, auth };
2. 补充ID Token写入Cookie的逻辑
Firebase默认的身份状态只存在浏览器的IndexedDB/LocalStorage中,服务端无法直接读取,你需要在用户登录成功、Token刷新时主动将ID Token写入Cookie,这样getServerSideProps才能拿到对应凭证:
// pages/index.js import { auth, googleProvider } from '../utils/firebaseClient'; import nookies from 'nookies'; export default function Authentication() { auth.onAuthStateChanged((user) => { if(user) window.location.replace('/dashboard'); }); // 监听ID Token刷新,自动更新Cookie保证有效性 auth.onIdTokenChanged(async (user) => { if (user) { const token = await user.getIdToken(); nookies.set(null, 'token', token, { maxAge: 30 * 24 * 60 * 60, path: '/', secure: process.env.NODE_ENV === 'production' }) } else { nookies.destroy(null, 'token'); } }) const googleButton = () => { auth.signInWithPopup(googleProvider).catch((err) => { console.log(err); }); } return ( <> <button onClick={googleButton}>使用谷歌账号登录</button> </> ) }
3. 完善getServerSideProps逻辑
你当前的代码验证完用户身份后没有把用户信息返回给页面组件,补充对应逻辑即可:
// pages/dashboard.js import { firebaseAdmin } from '../utils/firebaseAdmin'; import nookies from 'nookies'; export default function Dashboard({ user }) { if (!user) { return ( <> <h1>加载中</h1> </> ) } return ( <> <h1>你好 {user.email}</h1> </> ); } export async function getServerSideProps(ctx) { try { const cookies = nookies.get(ctx); if (!cookies.token) throw new Error('无登录凭证'); const user = await firebaseAdmin.auth().verifyIdToken(cookies.token); // 将过滤后的用户信息传给页面组件 return { props: { user: { uid: user.uid, email: user.email, displayName: user.name } } } } catch (err) { console.error(err); return { redirect: { destination: '/', permanent: false } } } }
可选优化点
- 如果担心客户端存储Cookie的安全问题,可以把ID Token发送到自定义API接口,由服务端写入httpOnly、secure属性的Cookie,降低XSS风险
- 可以封装一个全局的Auth监听高阶组件,统一处理Token刷新和Cookie更新逻辑,避免每个页面重复编写代码
内容的提问来源于stack exchange,提问作者yogurtsyum
相关产品推荐
相关产品推荐

