Node.js + Firebase:如何在服务端验证用户是否已认证
问题根源
firebase.auth().currentUser是客户端Firebase SDK专属属性,仅在浏览器/移动端这类有用户会话上下文的环境中有效。服务端Node.js的Firebase Auth实例与客户端会话完全隔离,直接调用这个属性必然返回null。
正确处理方案
1. 客户端需要传递的内容
客户端完成认证后,调用user.getIdToken()获取当前用户的ID令牌,然后在请求服务端时,将令牌放在Authorization请求头中,格式为Bearer <你的ID令牌>。
客户端示例代码:
// 前端获取令牌并发起请求 const user = firebase.auth().currentUser; if (user) { const idToken = await user.getIdToken(); await fetch('/your-fastify-api', { method: 'POST', headers: { 'Authorization': `Bearer ${idToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ data: 'example' }) }); }
2. 服务端验证令牌并获取用户信息
服务端需要使用Firebase Admin SDK的auth().verifyIdToken()方法验证令牌合法性,验证通过后即可解码出用户的UID、邮箱等核心信息,也可以进一步调用getUser(uid)获取完整用户记录。
Fastify路由示例
fastify.get('/protected-resource', async (request, reply) => { // 提取请求头中的令牌 const authHeader = request.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return reply.status(401).send({ error: '请提供有效认证令牌' }); } const idToken = authHeader.split(' ')[1]; try { // 验证并解码令牌 const decodedToken = await firebase.auth().verifyIdToken(idToken); // 可选:根据UID获取完整用户信息 const user = await firebase.auth().getUser(decodedToken.uid); return { status: 'success', userInfo: { uid: user.uid, email: user.email, displayName: user.displayName } }; } catch (err) { return reply.status(401).send({ error: '令牌无效或已过期' }); } });
3. 通用优化:Fastify全局认证钩子
为了避免重复编写验证逻辑,可以通过Fastify的preHandler钩子实现全局或局部的身份校验,将用户信息挂载到request对象中供后续路由使用:
// 定义认证钩子 const authHook = async (request, reply) => { const authHeader = request.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { reply.status(401); throw new Error('未授权访问'); } const idToken = authHeader.split(' ')[1]; try { const decodedToken = await firebase.auth().verifyIdToken(idToken); // 将用户信息挂载到request request.authUser = decodedToken; } catch (err) { reply.status(401); throw new Error('认证失败'); } }; // 给单个路由添加钩子 fastify.get('/private-route', { preHandler: authHook }, async (request) => { return { message: `欢迎回来,${request.authUser.email}` }; }); // 给整个插件/路由组添加钩子 fastify.register((instance, opts, done) => { instance.addHook('preHandler', authHook); instance.get('/group-route-1', async (request) => { /* ... */ }); instance.post('/group-route-2', async (request) => { /* ... */ }); done(); });
内容的提问来源于stack exchange,提问作者Robert Brax
相关产品推荐
相关产品推荐

