资讯详情

资讯详情

Forem 委派 API 访问实战:基于 RFC 9068 短时 JWT 的 API 认证接入指南

Forem 委派 API 访问实战基于 RFC 9068 短时 JWT 的 API 认证接入指南【免费下载链接】foremFor empowering community 项目地址: https://gitcode.com/gh_mirrors/fo/forem本文围绕 Forem 仓库的官方文档 docs/delegated_access.md 展开系统讲解如何让 Forem 的 V1 API 接受来自单一可信委派服务的短时 RFC 9068 访问令牌access-token JWT作为 API Key 之外的另一条认证通道。读者将掌握完整的部署配置项、令牌与 JWKS 契约、缓存与密钥轮换机制以及结合 app/services/delegated_access/ 源码的底层验证原理可直接据此在自建 Forem 实例上安全接入委派认证。什么是委派 API 访问Forem 的 API 默认通过api-key请求头进行认证见 app/controllers/api/v1/api_controller.rb 中的authenticate_with_api_key。委派访问Delegated Access提供了一条互补路径Forem 可以接受一个受信任委派服务签发的、短生命周期的 RFC 9068 access-token JWT。需要特别明确两个边界它只是认证方式的补充不改变账户关联account linking也不影响浏览器登录browser sign-in授权决策仍由委派服务自身负责Forem不会把控制器动作映射到scope也不会重新解释哪些 OAuth 客户端可以使用某个授权。委派服务必须在授权范围内才签发令牌。从源码结构看委派访问由三部分组成组件文件职责配置解析app/services/delegated_access/configuration.rb读取并校验环境变量构造 Verifier令牌/JWKS 校验app/services/delegated_access/verifier.rb解析 token、加载并缓存 JWKS、校验签名与声明JWKS 拉取app/services/delegated_access/jwks_client.rb从配置的 HTTPS 端点获取 JWKS 文档接入点位于 app/controllers/api/v1/api_controller.rb 的authenticate_with_delegated_access错误类型定义在 app/services/delegated_access/errors.rb。部署配置必填项开启委派访问在启用前必须一次性设置以下全部环境变量否则 configuration.rb 的required校验会直接抛出ArgumentErrorDELEGATED_ACCESS_ENABLEDtrue DELEGATED_ACCESS_ISSUERhttps://api.example.com DELEGATED_ACCESS_AUDIENCEhttps://community.example.com DELEGATED_ACCESS_IDENTITY_PROVIDERexternal-provider DELEGATED_ACCESS_OWNER_CLAIMhttps://api.example.com/claims/dev_user_id DELEGATED_ACCESS_JWKS_URIhttps://api.example.com/.well-known/jwks.json各配置项的语义与源码级约束DELEGATED_ACCESS_ENABLED取值必须是字面量true或false否则启动即报错。只有在true时API 才会解释Authorization: Bearer ...头DELEGATED_ACCESS_ISSUER / AUDIENCE令牌iss/aud声明的精确匹配值属于部署级的信任设置DELEGATED_ACCESS_IDENTITY_PROVIDER用于在Identity表中按provider字段定位用户见下文认证流程DELEGATED_ACCESS_OWNER_CLAIM指定哪个 claim 携带 Forem 用户 IDDELEGATED_ACCESS_JWKS_URIJWKS 端点地址path_required: true要求带非根路径。文档强调issuer、audience、identity provider、owner claim、JWKS URI 都是精确的部署信任设置JWKS URI 与 issuer必须是 HTTPS且绝不会从令牌声明或请求头中选取。Forem 不要求复制公钥 PEM、不校验活跃的kid是否预注册也不需要 OAuth client ID。源码中还隐含了更细的校验规则configuration.rbHTTPS 校验要求uri.is_a?(URI::HTTPS)、host 非空、无 userinfo、无 query、无 fragmentDELEGATED_ACCESS_OWNER_CLAIM必须是带路径的 HTTPS URI且不能是 RFC 9068 注册声明iss sub aud exp nbf iat jti client_id scope nonce sid以防止与标准声明冲突must be collision-resistant。可选项安全上界保守默认值DELEGATED_ACCESS_JWKS_MAX_AGE_SECONDS300 DELEGATED_ACCESS_MAX_TOKEN_LIFETIME_SECONDS60DELEGATED_ACCESS_JWKS_MAX_AGE_SECONDS每个 Puma worker进程内JWKS 密钥缓存的生存期默认 300 秒。缓存过期后每个进程只有第一个请求负责刷新并发请求会在宽限期内继续使用刚过期的条目超出宽限期后如果拉取失败则绝不再使用过期条目DELEGATED_ACCESS_MAX_TOKEN_LIFETIME_SECONDS令牌允许的最大生命周期exp - iat默认 60 秒强制令牌短时有效。这两个变量在 configuration.rb 中通过positive_integer解析必须是正整数否则抛DELEGATED_ACCESS_JWKS_MAX_AGE_SECONDS must be a positive integer类错误随后被传入Verifier的jwks_cache_lifetime与maximum_token_lifetime。初始化机制委派访问配置通过 config/initializers/delegated_access.rb 挂载在to_prepare回调中执行DelegatedAccess::Configuration.from_env.freeze存放到Rails.application.config.x.delegated_access。该配置对象是冻结的开发环境下代码重载时会重新构建。之所以放在to_prepare里是为了让 Zeitwerk 正常解析自动加载的DelegatedAccess常量而不是手写 require。未启用时的行为只要DELEGATED_ACCESS_ENABLED不是trueAPI 就会完全忽略Authorization头行为与引入此功能之前完全一致——即使客户端同时携带api-key与Authorization也不会受影响。令牌与密钥契约该契约遵循 RFC 9068尤其是其中面向资源服务器的检查显式的 access-token 类型、issuer、audience、签名与过期时间。Forem 在此基础上进一步收窄了允许的形式签名算法仅限RS256typ必须是atjwt头部必须携带非空kid必须包含受限的sub、exp、iat、nbf、jti以及配置的 owner claim会话用途声明sid与nonce会被拒绝完整的 RFC 9068 声明集含client_id、scope由委派服务负责签发但Forem 不使用这两个声明做授权。在实现上verifier.rb 的verify流程分两段不校验签名的解码先做令牌与头部的形式校验validate_token!、validate_header!确认header.keys.sort HEADER_MEMBERS即恰好是alg、kid、typ三个成员且alg RS256、typ atjwt、kid是长度不超过 128 字节的非空字符串严格校验签名解码通过JWT.decode(..., algorithms: [RS256], jwks: method(:load_jwks), iss:, aud:, required_claims: ..., verify_expiration/not_before/iat: true, leeway: 5)完成签名验证、issuer/audience 精确匹配、时间声明校验leeway为 5 秒时钟偏差CLOCK_SKEW_SECONDS。声明的二次校验validate_claims见 verifier.rb包括aud必须与配置精确相等存在nonce或sid即拒绝sub、jti必须是长度受限的非空字符串默认上限 256 字节iat、nbf、exp必须是整数且exp - iat为正且不超过maximum_token_lifetimenbf不得晚于expowner claim 必须是\A[1-9]\d{0,18}\z格式的正整数字符串且不超过有符号 64 位整数上限9_223_372_036_854_775_807解析后作为Claims.owner_id。JWKS 端点要求配置的 JWKS 端点必须返回顶层含keys数组的 JSON且满足至少一个唯一的 RSA 公钥签名密钥标记use: sig、alg: RS256模长≥ 2048 位其他公钥会被忽略任何私钥 RSA 参数d p q dp dq qi oth都会使整个响应失效。对应源码在 verifier.rbvalidated_signing_keys依次校验keys存在、数量不超过MAX_KEYS 32、每个成员是对象、不含私钥参数、kid不重复eligible_key?进一步要求 RSA 公钥、模长 ≥ 2048 且指数e为不小于 3 的奇数。拉取端 jwks_client.rb 也有硬性约束连接与读取超时各 2 秒、响应体上限 64 KB、要求 HTTP 200 且content-type为application/json或application/jwk-setjson并以application/jwk-setjson, application/json作为 Accept 头。认证流程从 Bearer 头到当前用户当委派访问启用且请求携带Authorization: Bearer token时api_controller.rb 的authenticate_with_delegated_access执行以下链路读取Rails.application.config.x.delegated_access若未启用则直接返回delegated_bearer_token解析Authorization头必须匹配Bearerscheme令牌非空且不含逗号否则抛InvalidTokenconfig.verifier.verify(token)完成全部令牌与签名校验返回Claims(subject:, owner_id:)用Identity.includes(:user).where(provider: config.identity_provider, uid: claims.subject, user_id: claims.owner_id).sole在provideruid委派服务的 subjectuser_idowner claim 解析出的用户 ID三个维度上精确定位用户若用户被标记为 spam / suspended 或未注册同样抛InvalidToken校验通过后设置user authenticated_user user后续 Pundit 授权与普通登录用户一致。在authenticate_with_api_keyapi_controller.rb中委派认证优先级高于 API Key先尝试委派令牌失败/缺失再回退到api-key请求头。注意这个回退只在没有有效委派令牌时发生一旦请求出示了 Bearer 令牌错误处理就是严格的见下节。失败、缓存与密钥轮换错误语义401 Unauthorized畸形令牌、声明或签名无效、未知kid。对应rescue_from DelegatedAccess::Errors::InvalidTokenapi_controller.rb返回{error:unauthorized,status:401}503 Service Unavailable仅当没有可用缓存条目且信任端点不可用或返回无效内容时触发rescue_from DelegatedAccess::Errors::Unavailableapi_controller.rb返回{error:delegated access unavailable,status:503}。文档明确在启用委派访问期间这两种情况都不会从已出示的 Bearer 令牌回退到 API Key 认证。缓存与防放大缓存逻辑在 verifier.rb 的load_jwks使用cache.fetchexpires_in: jwks_cache_lifetime并设置了race_condition_ttl: JWKS_REFRESH_GRACE_SECONDS10 秒实现每进程单请求刷新、其余请求继续用刚过期条目的节流策略。遥测通过ForemStatsClient.increment(delegated_access.verification, tags: [outcome:...]记录refresh、cache_hit、accepted、rejected、unavailable_jwks等结果。防放大机制未知kid不会使缓存失效也不会触发对 issuer 的请求从而防止攻击者用随机 ID 刷爆 JWKS 端点流量。密钥轮换与应急失效文档给出的轮换步骤先同时发布旧密钥与继任密钥等待至少一个配置的缓存生命周期DELEGATED_ACCESS_JWKS_MAX_AGE_SECONDS之后才开始用继任密钥签名。若怀疑密钥泄露操作员可在Rails console中立即使进程内密钥缓存失效Rails.application.config.x.delegated_access.invalidate_cache!该命令映射到 configuration.rb 的invalidate_cache!最终调用 verifier.rb 删除缓存键:delegated_access_jwks。注意必须在每个 Forem 应用进程中分别执行或直接重启全部应用进程执行前应先把被泄露的公钥从 issuer 的 JWKS 中移除缓存只包含经过校验的公钥材料且不是持久化的重启即清空。安全边界小结综合文档与源码可以归纳出以下硬性边界对应常量定义在 verifier.rb约束值来源令牌最大字节数16,38416 KBMAX_TOKEN_BYTESJWKS 最大密钥数32MAX_KEYSkid最大字节数128MAX_KID_BYTES字符串声明最大字节数256MAX_STRING_CLAIM_BYTES时钟偏差5 秒CLOCK_SKEW_SECONDS刷新宽限期10 秒JWKS_REFRESH_GRACE_SECONDSRSA 模长下限2048 位eligible_key?缓存生命周期 / 令牌最大寿命300s / 60s默认Configuration默认值这些数值共同保证令牌短命、输入有界、JWKS 文档小且只含公钥、签名强度有下限从输入面到信任面都做了收敛。委派服务需要为每个授权请求签发完整且正确的 RFC 9068 声明集并自行承担授权判断——Forem 只做认证与用户映射。测试与验证仓库在 spec/services/delegated_access/ 下提供了三个核心测试文件可作为接入时的参考基线verifier_spec.rb覆盖令牌头部校验、声明校验、生命周期校验、JWKS 解析与缓存刷新、未知kid行为等configuration_spec.rb覆盖环境变量必填、HTTPS 校验、owner claim 冲突检查、正整数边界jwks_client_spec.rb覆盖 HTTP 状态、Content-Type、响应体大小上限与超时处理。端到端层面authenticate_with_delegated_access的 401/503 映射可见于 app/controllers/api/v1/api_controller.rb相关请求级行为可在 spec/requests/api/v1/users_spec.rb 等 API 测试中进一步验证。简言之委派访问为 Forem 提供了一条短时、精确、可轮换、可应急失效的机器对机器认证通道只要委派服务严格按本文契约签发令牌、正确配置 JWKS 并遵循轮换节奏即可安全地让受信任服务以指定用户身份调用 Forem API而不必分发长期有效的 API Key。【免费下载链接】foremFor empowering community 项目地址: https://gitcode.com/gh_mirrors/fo/forem创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →