GitHub App 认证流程详解(User Access Token 与 Installation Token)

用户登录态和仓库访问凭据,是两条不能互相替代的认证链路。让 App 以”用户身份”登录没问题,可一旦要自动提交 PR、读取私有仓库,就得拿到一个能代表”App 对某仓库”的凭据。一开始我把所有请求都塞给 User Access Token,结果用户撤销授权后整个自动化停摆,限流也经常把翻译任务卡死。后来把两类 Token 的边界理清楚——User Access Token 代表用户,Installation Access Token 代表 App 在某安装上的访问能力——权限边界才算真正画对。GitHub App 涉及两类 Token:User Access Token(代表用户身份)与 Installation Access Token(代表应用对仓库的访问)。理解两者生成方式、生命周期与适用场景,是把权限边界设计清楚的前提。

一、两类 Token 的对比

先给一个整体印象。把两类 Token 并排看,差别集中在身份、生成方式和失效条件上,下面从 6 个维度对比:

维度 User Access Token Installation Access Token
身份 用户 App 在某安装
生成方式 OAuth 授权 用私钥签 JWT 换
有效期 默认较短,可刷新 默认 1 小时
权限 用户授权范围 App 安装时授予
用途 读用户信息、安装列表 读仓库、提交 PR
失效条件 用户撤销授权 安装被卸载 / 过期

一句话总结:登录、读用户信息走 User Access Token,凡是”代表 App 去操作仓库”的动作都走 Installation Access Token,两者各管一段,互不掺和。这个划分我在项目里沿用至今,接口权限表也是照着它画的。

二、User Access Token

User Access Token 由 OAuth 流程产生,常见场景是”登录”。为什么用户侧要用它,而不是让 App 直接拿仓库权限?因为授权范围是用户自己勾选的,用户能明确知道”这个 App 能读我的哪些信息”,撤销时也立刻生效,这对一个处理用户私人仓库的工具很关键。我一开始想省事,让用户装好 App 就完事,结果连”他是谁”都不知道,管理后台根本没法做。补上 OAuth 登录后,身份、安装列表、仓库列表这些信息才齐全。

拿到 token 的完整链路是四步:

  1. 用户点登录,跳转 GitHub OAuth;
  2. 用户同意授权范围(如 read:user、user:email);
  3. GitHub 返回 code,App 用 code + client secret 换 access token;
  4. 拿到 access token 后,可调 GET /user 获取用户信息。

这里踩过一个坑:最开始把 code 换 token 的请求放在前端做,client secret 差点暴露,后来全部挪到服务端,前端只拿最终的登录态。User Access Token 不适合直接读仓库:它的权限继承自用户,且速率限制受用户其它行为影响。翻译任务是定时批量跑的,用户个人的限流配额根本扛不住。

三、Installation Access Token

Installation Access Token 是”App 访问某仓库”的凭据,必须服务端生成。为什么必须服务端?因为要用 App 私钥签名,私钥绝不能出现在客户端代码里,一旦泄露等于把整个 App 的仓库访问权限都交出去了。

3.1 生成步骤

生成流程看起来只有几步,但每步都有讲究:

1. 用 App 私钥签一个 JWT(10 分钟内有效)
2. 用 JWT 调 POST /app/installations/{id}/access_tokens
3. 拿到 token 和过期时间
4. token 接近过期前重新生成

JWT 有效期卡在 10 分钟内,是因为它只承担”换取 token”这一个使命,生命周期越短越安全。我最初把 JWT 的有效期设成 1 小时,换来换去没问题,但一旦 JWT 泄露,攻击者能在一个小时内随便换 token,后来收紧到 10 分钟,风险窗口小了很多。

3.2 JWT 构造

JWT 本身用 RS256 签名,关键是把时间戳和 App ID 填对:

header: { "alg": "RS256", "typ": "JWT" }
payload:
  iat:  当前时间 - 60s
  exp:  当前时间 + 600s
  iss:  App ID

私钥用 RS256 签名。生成 JWT 后用 Authorization: Bearer <jwt> 调换 token 接口。iat 往前拨 60 秒是为了容忍服务器与 GitHub 之间的时钟偏差,这一点不处理,认证会偶发报错,排查起来很费时间。

3.3 缓存

由于 1 小时过期,服务端要缓存。每次翻译任务要处理几十个仓库,每个仓库都现场换 token,不仅慢,还容易撞上速率限制,所以缓存是必选项:

  • Redis key: gh_install_token:{installation_id};
  • TTL: 50 分钟(留 10 分钟余量);
  • 命中即返回,未命中则重新生成。

把 TTL 定在 50 分钟而不是 60,是我踩过坑后调出来的:卡在整点过期,缓存和真实过期时间贴得太近,一旦 Redis 与 GitHub 时钟有偏差,就会偶发 401。留 10 分钟余量后,这类问题再没出现过。

四、本项目的使用划分

光理解两类 Token 还不够,落地时得有一张明确的”场景到 Token”对照表,避免新同事把 token 用串:

场景 用哪个 Token
读取用户信息 User Access Token
列出用户已安装的 App User Access Token
读取仓库元数据 Installation Access Token
提交分支/PR Installation Access Token
接收 push 事件 Webhook(无需 Token)

这张表定下来之后,权限审计变得很轻松:凡是仓库写入动作,代码里只允许出现 Installation Token;凡是身份相关,只允许 User Access Token。团队做代码 review 时直接按表对号入座,越界的基本一眼能看出来。

五、Token 的安全存储

Token 的安全存储是上线前必须过的一道关,我按”最小暴露”原则逐条落实:

  • User Access Token 存服务端 session,HttpOnly cookie 携带 session id;
  • App 私钥存放在环境变量或密钥管理服务(KMS/SSM);
  • Installation Token 缓存在 Redis,不入数据库;
  • 任何 token 不写日志、不暴露前端。

一条一条说原因:User Access Token 不落前端,浏览器拿不到明文;私钥进 KMS 后可以随时 rotate,配合定期轮换,泄露窗口可控;Installation Token 放 Redis 是因为它本来就是短期凭据,写数据库反而留了陈旧的敏感数据。日志里打 token 是我早期犯过的错,后来加了脱敏检查,凡是命中 gh_ 开头的字段一律打码。

六、Webhook 与 Token 的配合

App 装载后,GitHub 会向配置的 URL 推送事件(如 push、pull_request)。Webhook 与 Token 是两套独立体系:Webhook 只负责”告诉我发生了什么”,真正执行任务还得靠 Installation Token。处理流程:

  1. 校验 X-Hub-Signature-256 与 webhook secret;
  2. 解析事件,得到 installation.id、repository.full_name;
  3. 调缓存或重新生成 Installation Token;
  4. 执行业务逻辑(增量翻译、PR 状态同步等)。

这四步里最容易忽略的是第一步签名校验。不校验,任何人都能伪造 push 事件触发任务,等于给别人留了个免费的调用入口。我一开始为了省事跳过校验,被扫描工具提示后才补上,之后所有 webhook 入口都强制校验签名。

七、典型错误与处理

上线后线上报错集中在几个固定类型,我把它们整理成排查表,运维同学照着处理就行:

错误 原因 处理
401 Bad credentials Token 过期或撤销 重新生成 Installation Token
403 Resource not accessible 安装权限不足 引导用户重新安装并勾选仓库
404 Not Found 安装被卸载 标记安装失效,提示重装
429 Too Many Requests 限流 退避 + 复用请求

几个错误里,403 容易被误判为代码 bug,其实多半是用户安装 App 时没勾选目标仓库。处理方式是从错误响应里解析出仓库名,直接提示用户去安装页面补勾选,省去来回排查。

常见问题(FAQ)

Q1:User Access Token 能不能读仓库?

可以,但权限继承自用户本人,不推荐用于应用自动化。

Q2:Installation Token 为什么要缓存?

因为生成需要私钥签名,且调用有速率限制。

Q3:私钥泄露怎么办?

立刻在 GitHub 开发者设置里”Rotate”私钥并重新部署。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
小码农的头像小码农认证作者

相关推荐

返回顶部