ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

使用 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_HOOK、X-Hasura-*会话变量的完整细节可以直接落地一套基于 Firebase ID Token 的 Hasura 认证方案。背景为什么需要 Auth WebhookHasura GraphQL Engine 本身不做身份认证而是把谁在请求、拥有什么角色的判断交给外部认证服务。除了 JWT 模式外最灵活的方式就是Webhook 认证模式把 GraphQL Engine 配置为在收到请求时调用一个自定义 HTTP 端点该端点读取客户端请求头、验证用户身份并返回一组以X-Hasura-*开头的会话变量session variablesHasura 再依据这些变量执行角色与权限Permission规则。Firebase 生态中客户端通过 Firebase SDK 登录后拿到id_token服务端可用 Firebase Admin SDK 校验其有效性。于是一个典型的组合是Node.js Express 的 Webhook 服务负责校验id_tokenHasura 负责基于校验结果执行细粒度权限控制。本样板的完整文件位于仓库的 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.jsserver.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.jsfirebase/firebaseHandler.js 是整个样板的灵魂其处理流程可以拆成四步第一步初始化 Firebase Admin SDK。服务启动时读取FIREBASE_CONFIG环境变量见 firebase/config.js并以其内容初始化 Admin SDKvar 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中抽出 tokenconst 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.2与firebase-admin ^14.2.0Node 版本要求24.x.xProcfile 只有一行web: node server.js供 Heroku 等平台识别启动命令。本地验证只需npm install npm start # 另开终端 curl -H Authorization: Bearer id_token http://localhost:3000/firebase/webhook部署到云端三种方式原文档给出了三种快速部署路径均以把FIREBASE_CONFIG注入环境变量为核心。方式一Heroku推荐将仓库中该样板目录复制到独立目录并初始化 gitcp -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-----\nyour-private-key\n-----END PRIVATE KEY-----\n, client_email: firebase-adminsdk-t4siktestapp-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注入环境变量时需保留原样。方式二NowZeit使用 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-----\nyour-private-key\n-----END PRIVATE KEY-----\n, client_email: firebase-adminsdk-t4siktestapp-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-----\nyour-private-key\n-----END PRIVATE KEY-----\n, client_email: firebase-adminsdk-t4siktestapp-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/webhookHASURA_GRAPHQL_AUTH_HOOK_MODE--auth-hook-mode请求方式GET默认或POST以 Docker 为例在docker run或docker-compose中追加-e HASURA_GRAPHQL_AUTH_HOOKhttps://your-webhook/firebase/webhook \ -e HASURA_GRAPHQL_AUTH_HOOK_MODEGET需要注意若请求头中包含X-Hasura-Admin-Secret管理员密钥则跳过 Webhook 校验、直接授予管理员权限必须保证 Hasura 容器能通过网络访问到 Webhook 地址否则认证会失败Webhook 模式生效的前提是先为 GraphQL 端点启用管理员密钥即HASURA_GRAPHQL_ADMIN_SECRET否则端点默认可匿名访问认证形同虚设。Webhook 请求规范配置为GET时Hasura 会把客户端请求的绝大部分请求头原样转发给 Webhook唯一例外是Content-Length、Content-Type、User-Agent、Host、Origin、Accept、Cache-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-IdFirebaseuidX-Hasura-Role: user与上述规范完全一致。若要使用匿名public/unauthorized角色则返回200且X-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头转发给 WebhookWebhook 校验通过并回传X-Hasura-User-Id、X-Hasura-RoleHasura 即依据该用户角色执行表级/行级权限例如{user_id: {_eq: X-Hasura-User-Id}}这类行选择规则。WebSocket 连接续期实时查询对于实时查询live queries与流式订阅认证后的 WebSocket 连接默认没有超时。若需要定期重新认证Webhook 可在200响应中额外返回以下任一字段Cache-Control相对过期时间秒如Cache-Control: max-age600Expires绝对过期时间格式为%a, %d %b %Y %T GMT如Expires: Mon, 30 Mar 2020 13:25:18 GMT。到达过期时间后Hasura 会重新请求 Webhook 并建立新的 WebSocket 连接。生产环境注意事项结合源码与官方 Webhook 规范落地到生产时有几点建议Webhook 必须走 HTTPSid_token属于敏感凭证明文传输有被截获风险明确失败策略当前样板对校验失败返回匿名角色若业务要求无效 token 直接拒绝可改为返回401 Unauthorized注意 Hasura 只接受200与401保护服务账号密钥FIREBASE_CONFIG包含private_key切勿提交进代码仓库或暴露在客户端最小化会话变量只返回权限规则真正需要的X-Hasura-*变量避免把多余的用户信息透传给 GraphQL 引擎结合权限规则使用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),仅供参考
返回列表