news 2026/9/19 1:56:09

使用 Firebase Auth Webhook 为 Hasura GraphQL Engine 实现自定义认证:Node.js 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Firebase Auth Webhook 为 Hasura GraphQL Engine 实现自定义认证:Node.js 实战指南

使用 Firebase Auth Webhook 为 Hasura GraphQL Engine 实现自定义认证:Node.js 实战指南

【免费下载链接】graphql-engineBlazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.项目地址: https://gitcode.com/gh_mirrors/gr/graphql-engine

本指南以仓库中的 nodejs-firebase 认证 Webhook 样板 为主体,讲解如何用 Node.js 编写一个转发到 Firebase Auth 并返回会话变量的认证 Webhook,并把它接入 Hasura GraphQL Engine 的 Webhook 认证模式。读完本文,你将掌握该样板的源码结构、三种部署方式(Heroku / Now / Glitch)、FIREBASE_CONFIG环境变量的配置,以及 Webhook 请求/响应协议(HASURA_GRAPHQL_AUTH_HOOKX-Hasura-*会话变量)的完整细节,可以直接落地一套基于 Firebase ID Token 的 Hasura 认证方案。

背景:为什么需要 Auth Webhook

Hasura GraphQL Engine 本身不做身份认证,而是把"谁在请求、拥有什么角色"的判断交给外部认证服务。除了 JWT 模式外,最灵活的方式就是Webhook 认证模式:把 GraphQL Engine 配置为在收到请求时调用一个自定义 HTTP 端点,该端点读取客户端请求头、验证用户身份,并返回一组以X-Hasura-*开头的会话变量(session variables),Hasura 再依据这些变量执行角色与权限(Permission)规则。

Firebase 生态中,客户端通过 Firebase SDK 登录后拿到id_token,服务端可用 Firebase Admin SDK 校验其有效性。于是,一个典型的组合是:Node.js + Express 的 Webhook 服务负责校验id_token,Hasura 负责基于校验结果执行细粒度权限控制

本样板的完整文件位于仓库的 community/boilerplates/auth-webhooks/nodejs-firebase 目录,同目录下还提供了 nodejs-express、lambda-cognito、firebase-cloud-functions 等其他语言的样板,可供对照。

项目结构:源码级剖析

该样板是一个标准的 Express 应用,目录结构如下:

community/boilerplates/auth-webhooks/nodejs-firebase/ ├── Procfile # Heroku 启动声明:web: node server.js ├── package.json # 依赖声明(express、firebase-admin) ├── server.js # Express 入口,挂载 /firebase 路由 ├── firebase/ │ ├── config.js # 读取 FIREBASE_CONFIG 环境变量 │ └── firebaseHandler.js # 核心认证逻辑 └── assets/ └── deploy-glitch.png # Glitch 一键部署按钮图片

入口 server.js

server.js 非常精简:

// init project var express = require('express'); var app = express(); var port = process.env.PORT || 3000; app.get('/', (req, res) => { res.send('Webhooks are running'); }); // Firebase handler var firebaseRouter = require('./firebase/firebaseHandler'); app.use('/firebase', firebaseRouter); // listen for requests :) var listener = app.listen(port, function () { console.log('Your app is listening on port ' + port); });

要点:

  • 端口来自process.env.PORT,默认3000(云平台通常会自动注入PORT);
  • 根路径/返回Webhooks are running,用于健康检查;
  • 认证逻辑全部挂在/firebase前缀下,最终对外暴露的 Webhook 地址为http://<host>:<port>/firebase/webhook

核心认证逻辑 firebaseHandler.js

firebase/firebaseHandler.js 是整个样板的灵魂,其处理流程可以拆成四步:

第一步:初始化 Firebase Admin SDK。服务启动时读取FIREBASE_CONFIG环境变量(见 firebase/config.js),并以其内容初始化 Admin SDK:

var admin = require('firebase-admin'); var serviceAccount = require('./config.js'); var error = null; if (serviceAccount) { try { admin.initializeApp({ credential: admin.credential.cert(JSON.parse(serviceAccount)) }); } catch (e) { error = e; } }

注意:config.js只是把process.env.FIREBASE_CONFIG原样导出,所以部署时必须把 Firebase 服务账号 JSON 的完整内容作为该环境变量的值。若未配置,后续请求会返回500 Firebase not configured;若配置了但无法解析(如 JSON 格式错误),会返回500 Invalid firebase configuration

第二步:读取并解析 Authorization 头。Webhook 路由为GET /firebase/webhook。无Authorization头时直接返回匿名角色:

var authHeaders = request.get('Authorization'); if (!authHeaders) { response.json({'x-hasura-role': 'anonymous'}); return; }

第三步:提取 Bearer Token。通过正则从Authorization: Bearer <id_token>中抽出 token:

const extractToken = (bearerToken) => { const regex = /^(Bearer) (.*)$/g; const match = regex.exec(bearerToken); if (match && match[2]) { return match[2]; } return null; }

第四步:校验并返回会话变量。调用admin.auth().verifyIdToken(idToken)校验 token 真实性,成功后返回X-Hasura-User-Id(取 Firebase 用户的uid)和角色user;校验失败则同样回退到匿名角色:

admin.auth().verifyIdToken(idToken) .then((decodedToken) => { var hasuraVariables = { 'X-Hasura-User-Id': decodedToken.uid, 'X-Hasura-Role': 'user' }; response.json(hasuraVariables); }) .catch((e) => { console.log(e); response.json({'x-hasura-role': 'anonymous'}); });

从源码可以推断该样板的安全策略:Firebase 校验失败的请求不会收到 401,而是被降级为匿名角色。因此若要严格拒绝未授权请求,可自行改为返回 401(见下文"响应规范")。

依赖与进程管理

package.json 声明依赖express ^4.22.2firebase-admin ^14.2.0,Node 版本要求24.x.x;Procfile 只有一行web: node server.js,供 Heroku 等平台识别启动命令。本地验证只需:

npm install npm start # 另开终端 curl -H "Authorization: Bearer <id_token>" http://localhost:3000/firebase/webhook

部署到云端:三种方式

原文档给出了三种快速部署路径,均以"把FIREBASE_CONFIG注入环境变量"为核心。

方式一:Heroku(推荐)

将仓库中该样板目录复制到独立目录并初始化 git:

cp -r <仓库路径>/community/boilerplates/auth-webhooks/nodejs-firebase <some-dir> cd <some-dir> git init && git add . && git commit -m "init auth webhook"

创建 Heroku 应用并推送部署:

heroku apps:create git push heroku master

部署完成后,进入Manage App > Settings,为应用设置如下环境变量:

  • FIREBASE_CONFIG:把 Firebase 服务账号 JSON 的完整内容作为该字段的值。示例:
{ "type": "service_account", "project_id": "testapp-2222", "private_key_id": "f02aca08952f702de43ed577b428f405efe2d377", "private_key": "-----BEGIN PRIVATE KEY-----\n<your-private-key>\n-----END PRIVATE KEY-----\n", "client_email": "firebase-adminsdk-t4sik@testapp-24a60.iam.gserviceaccount.com", "client_id": "113608616484852272199", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-t4sik%40testapp-22222.iam.gserviceaccount.com" }

该 JSON 可在 Firebase 控制台Project settings > Service accounts > Generate new private key中下载;private_key内含换行转义符\n,注入环境变量时需保留原样。

方式二:Now(Zeit)

使用 Now 平台部署时,直接在now命令中以-e参数注入环境变量:

npm install -g now now -e \ FIREBASE_CONFIG='{ "type": "service_account", "project_id": "testapp-2222", "private_key_id": "f02aca08952f702de43ed577b428f405efe2d377", "private_key": "-----BEGIN PRIVATE KEY-----\n<your-private-key>\n-----END PRIVATE KEY-----\n", "client_email": "firebase-adminsdk-t4sik@testapp-24a60.iam.gserviceaccount.com", "client_id": "113608616484852272199", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-t4sik%40testapp-22222.iam.gserviceaccount.com" }'

方式三:Glitch

仓库提供了 Glitch 一键导入所需的部署按钮图片 assets/deploy-glitch.png(原 README 中的按钮会跳转到 Glitch 的 GitHub 导入页面)。进入 Glitch 编辑器后,在.env文件中添加同样的环境变量:

FIREBASE_CONFIG='{ "type": "service_account", "project_id": "testapp-2222", "private_key_id": "f02aca08952f702de43ed577b428f405efe2d377", "private_key": "-----BEGIN PRIVATE KEY-----\n<your-private-key>\n-----END PRIVATE KEY-----\n", "client_email": "firebase-adminsdk-t4sik@testapp-24a60.iam.gserviceaccount.com", "client_id": "113608616484852272199", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-t4sik%40testapp-22222.iam.gserviceaccount.com" }'

接入 Hasura GraphQL Engine

配置 Webhook 认证模式

部署好 Webhook 后,把它的 URL 配置到运行 GraphQL Engine 的容器环境变量中。Hasura 的 Webhook 认证模式由以下两个环境变量(或等价的启动参数)控制,官方规范详见 docs/docs/auth/authentication/webhook.mdx:

环境变量对应 Flag说明
HASURA_GRAPHQL_AUTH_HOOK--auth-hookWebhook 端点地址,例如https://<your-webhook>/firebase/webhook
HASURA_GRAPHQL_AUTH_HOOK_MODE--auth-hook-mode请求方式,GET(默认)或POST

以 Docker 为例,在docker run(或docker-compose)中追加:

-e HASURA_GRAPHQL_AUTH_HOOK=https://<your-webhook>/firebase/webhook \ -e HASURA_GRAPHQL_AUTH_HOOK_MODE=GET

需要注意:

  • 若请求头中包含X-Hasura-Admin-Secret(管理员密钥),则跳过 Webhook 校验、直接授予管理员权限;
  • 必须保证 Hasura 容器能通过网络访问到 Webhook 地址,否则认证会失败;
  • Webhook 模式生效的前提是先为 GraphQL 端点启用管理员密钥(即HASURA_GRAPHQL_ADMIN_SECRET),否则端点默认可匿名访问,认证形同虚设。

Webhook 请求规范

配置为GET时,Hasura 会把客户端请求的绝大部分请求头原样转发给 Webhook(唯一例外是Content-LengthContent-TypeUser-AgentHostOriginAcceptCache-Control等约 14 个传输层/浏览器层头不会转发)。因此本样板能够通过Authorization头拿到客户端的 Firebaseid_token

配置为POST时,Hasura 会在转发全部客户端请求头之外,把请求体封装为 JSON 一并发送,形如:

{ "headers": { "header-key1": "header-value1" }, "request": { "variables": { "a": 1 }, "operationName": "UserQuery", "query": "query UserQuery($a: Int) { users(where: { id: { _eq: $a } }) { id } }" } }

Webhook 响应规范(会话变量协议)

Hasura 只接受两种状态码,其余一律按500 Internal Server Error处理:

响应含义
200 OK认证通过(或授权为匿名角色),响应体携带X-Hasura-*会话变量
401 Unauthorized拒绝该 GraphQL 请求

200响应体至少要包含X-Hasura-Role以告知 Hasura 使用哪个角色;其余自定义变量(如X-Hasura-User-Id)都会进入权限规则上下文。所有值都必须是字符串,Hasura 收到后会自动转换类型。标准示例:

HTTP/1.1 200 OK Content-Type: application/json { "X-Hasura-User-Id": "25", "X-Hasura-Role": "user", "X-Hasura-Is-Owner": "true", "X-Hasura-Custom": "custom value" }

本样板成功时返回的是X-Hasura-User-Id(Firebaseuid)+X-Hasura-Role: user,与上述规范完全一致。

若要使用匿名(public/unauthorized)角色,则返回200X-Hasura-Role设为匿名角色名:

HTTP/1.1 200 OK Content-Type: application/json { "X-Hasura-Role": "anonymous", }

本样板在"无 Authorization 头"和"token 校验失败"两种场景下正是返回这个结果。

客户端如何请求

客户端使用 Firebase SDK 登录获得id_token后,向 GraphQL Engine 发起请求时携带如下请求头:

{ "Authorization": "Bearer <id_token>" }

GraphQL Engine 收到后会把Authorization头转发给 Webhook,Webhook 校验通过并回传X-Hasura-User-IdX-Hasura-Role,Hasura 即依据该用户角色执行表级/行级权限(例如{"user_id": {"_eq": "X-Hasura-User-Id"}}这类行选择规则)。

WebSocket 连接续期(实时查询)

对于实时查询(live queries)与流式订阅,认证后的 WebSocket 连接默认没有超时。若需要定期重新认证,Webhook 可在200响应中额外返回以下任一字段:

  • Cache-Control:相对过期时间(秒),如"Cache-Control": "max-age=600"
  • Expires:绝对过期时间,格式为"%a, %d %b %Y %T GMT",如"Expires": "Mon, 30 Mar 2020 13:25:18 GMT"

到达过期时间后,Hasura 会重新请求 Webhook 并建立新的 WebSocket 连接。

生产环境注意事项

结合源码与官方 Webhook 规范,落地到生产时有几点建议:

  1. Webhook 必须走 HTTPSid_token属于敏感凭证,明文传输有被截获风险;
  2. 明确失败策略:当前样板对校验失败返回匿名角色,若业务要求"无效 token 直接拒绝",可改为返回401 Unauthorized(注意 Hasura 只接受200401);
  3. 保护服务账号密钥FIREBASE_CONFIG包含private_key,切勿提交进代码仓库或暴露在客户端;
  4. 最小化会话变量:只返回权限规则真正需要的X-Hasura-*变量,避免把多余的用户信息透传给 GraphQL 引擎;
  5. 结合权限规则使用:Webhook 只负责"认证"(你是谁),数据隔离仍由 Hasura 的 Permission 规则负责,二者需配套设计。

延伸阅读

  • 样板总览与贡献规范:community/boilerplates/auth-webhooks/README.md
  • 官方 Webhook 认证协议完整规范:docs/docs/auth/authentication/webhook.mdx
  • 同目录其他语言样板:nodejs-express、lambda-cognito、firebase-cloud-functions
  • 架构说明文档:architecture/live-queries.md(了解 WebSocket 认证续期机制的应用场景)

【免费下载链接】graphql-engineBlazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.项目地址: https://gitcode.com/gh_mirrors/gr/graphql-engine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 1:54:41

施工测量的结构几何控制中枢:从打桩放线到精度校验系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 1:54:36

UE5 GameInstance子系统实战指南:跨关卡全局状态管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 1:53:02

Win10磁盘100%排查:任务管理器到SFC/DISM实战

任务管理器里磁盘一栏长期顶在 100%&#xff0c;鼠标点一下要等三秒&#xff0c;这种滋味我在好几台 windows10 机器上都遇到过。网上搜“磁盘100%解决方法”&#xff0c;答案从关服务到换硬盘五花八门&#xff0c;但真正到了现场&#xff0c;同一招在这台机器上管用&#xff0…

作者头像 李华
网站建设 2026/9/19 1:51:44

第030篇 腾讯跨端通信开发面经:面试官问数据同步与一致性想听什么?

面试公司:腾讯 微信鸿蒙版 岗位方向:鸿蒙跨端通信开发工程师 | 技术域:网络与数据 难度:★★★★☆ | 核心考点:数据同步与一致性 标签: HarmonyOS 网络与数据 鸿蒙面经 数据同步与一致性 面试真题 摘要:面试腾讯的跨端通信开发岗位时被问到「数据同步与一致性…

作者头像 李华
网站建设 2026/9/19 1:50:42

智慧教室故障排查指南:快速定位问题的方法与应急方案

开学季 | 智慧教室“不掉链”&#xff01;这份故障排查指南请收好每年开学头两周&#xff0c;基本是所有智慧教室运维人最忙的时候。不是这边触控屏没有反应&#xff0c;就是那边教师电脑死活不出画面&#xff0c;再要么就是无线麦没声音。我做了这么多年智慧教室的运维支撑&am…

作者头像 李华