你第一次构建 GitHub 集成时,会花一个小时琢磨到底要 GitHub App 还是 OAuth App。文档把它们当作等价选项。其实不然。单是 webhook 这一点,多数时候就足以替你做出选择。
极简摘要
- GitHub App:安装在账户或仓库上,拥有细粒度权限,接收其被安装资源的 webhook,用短期令牌以自身身份认证。
- OAuth App:由用户授权,以用户的权限代表用户行事,只有像任何第三方工具那样自行设置时才接收 webhook,用长期令牌以用户身份认证。
如果你构建的东西在没有活跃用户的情况下运行——CI 机器人、代码评审自动化、任何定时任务——你需要 GitHub App。如果你构建的工具代表已登录用户执行操作(比如代码搜索 UI),OAuth App 才是正确的形态。
它们如何接收 webhook
GitHub Apps
创建应用时你配置一个 webhook URL。当用户把应用安装到账户或仓库上时,GitHub 开始把该范围的事件发送到你的 URL。事件包括 installation(安装/卸载生命周期)、installation_repositories(新增或移除的仓库),以及你的应用订阅的事件类型(push、pull_request 等)。
每个事件都带一个 X-GitHub-Hook-Installation-Target-Type 头(“integration”)和一个 installation ID,你可以用它为该具体安装铸造令牌。签名是 X-Hub-Signature-256 里的 HMAC SHA-256——与仓库 webhook 同样的形态。
OAuth Apps
OAuth App 没有内置的 webhook 订阅。要接收 webhook,应用的授权用户必须创建指向应用 URL 的仓库或组织 webhook。这意味着应用的 webhook 触及范围取决于其用户在哪里设置了订阅,而不是应用本身被“安装”在哪里。
有些团队构建的 OAuth App 会在授权后通过 GitHub API 自动创建 webhook。这能行,但你现在要在应用自身逻辑之外管理按用户的 webhook 生命周期。
认证才是真正的分歧
GitHub App 使用 JWT 签名请求来为每个安装铸造短期安装令牌(1 小时)。JWT 用你创建应用时生成的私钥签名。你的代码:
// 1. 用应用私钥签一个 JWT(10 分钟过期)
const jwt = createAppJwt(APP_ID, PRIVATE_KEY);
// 2. 用 JWT 换取安装令牌(有效 1 小时)
const token = await fetchInstallationToken(jwt, INSTALLATION_ID);
// 3. 用该令牌调用 API
const res = await fetch('https://api.github.com/repos/x/y/issues', {
headers: { Authorization: `token ${token}` },
});
安装令牌限定于该安装并自动过期——所以泄露的影响半径有限。
OAuth App 用通过标准 OAuth 流程获得的用户访问令牌认证。这些令牌默认长期有效并以用户身份行事——泄露一个就意味着攻击者能做该用户能做的一切。GitHub 文档在新集成上倾向于推荐 GitHub App,部分原因就在此。
权限:受限 vs 宽泛
GitHub App 让你请求细粒度权限:读 issues、写 checks、读 pull requests,其他一概不能访问。每项权限相互独立。用户看到确切清单并可拒绝。
OAuth App 用较旧的基于 scope 的系统:repo、read:user 等。scope 更粗。repo scope 授予对用户可见的所有仓库的读写权限——没有“对特定仓库只读”的版本。
对于只需读代码、写 check runs 的代码评审机器人,带两项具体权限的 GitHub App 远比请求完整 repo 访问的 OAuth App 侵入性小得多。
两者的本地测试
webhook 签名机制完全相同——配置见 GitHub webhook 本地测试。区别在于你如何注册 URL。
- GitHub App:在应用的设置页设置 webhook URL。把应用安装到一个测试仓库。触发事件。完成。
- OAuth App:应用本身没有 webhook。添加一个仓库 webhook(在仓库设置页手动添加,或通过 API 以编程方式),指向你的隧道 URL。
对于 OAuth App,你还需要测试 OAuth 回调流程本身——见如何在本地测试 OAuth 回调。
在两者之间迁移很痛苦
如果你从 OAuth App 起步,后来意识到需要 GitHub App,你无法迁移。用户必须重新授权新应用,你必须重新签发任何已存储的令牌,而你通过 OAuth App 设置的任何仓库 webhook 在删除之前会持续触发。一开始花十分钟挑对类型。
何时选哪个
选 GitHub App,当:
- 你的集成按自己的时间表或响应事件运行,没有已登录用户。
- 你想要对特定仓库或组织的受限权限。
- 你想要绑定到安装范围的 webhook,而非按仓库配置。
- 你构建的东西最终会上架 GitHub Marketplace。
选 OAuth App,当:
- 你的工具是一个 UI,用户登录后在自己的 GitHub 账户里做事。
- 你需要完全以用户身份行事,包括其访问模式。
- 你不需要 webhook,或将按仓库手动设置。
关于底层签名验证细节,见签名验证指南。加入 PortPreview 等候名单,获得能处理 GitHub webhook 时序与重放捕获的隧道。