之前在使用 Github issues 搭建博客平台的时候,研究过一番如何取得 Github 授权并调用 API 的办法。后来选择了较简单的账号密码和 Token 的方法。但是有读者反馈这样的操作依然稍显麻烦,且在第三方的页面输入账号密码总感觉不安全。后来经过研究,总算找到了 Github App 这种更为优雅的办法。
什么是 Github App
要回答这个问题,可以直接套用官方文档的说法:
GitHub Apps are first-class actors within GitHub. A GitHub App acts on its own behalf, taking actions via the API directly using its own identity, which means you don't need to maintain a bot or service account as a separate user.
简单翻译一下,就是 Github App 可以通过 Github 提供的认证信息去调用 Github API。
细心的读者会发现,Github 还提供了一个叫做“OAuth App”的东西,它的使用方式和 Github App 非常类似,最大的不同点是 OAuth App 所获取的权限都是固定且只读的,用户只能读取固定的数据而不能修改数据;而 Github App 几乎可以获取Github提供的所有功能权限,且所获取的权限可以被设定为“只读”,“可读可写”和“禁止访问”,对于权限的授权粒度会更细。
获取了对某些操作的权限之后,我们就可以使用这些权限去搭建一个独立的 App,比如一个第三方的 Github 客户端等等,这也是 Github App 的实用之处。
第三方登录的原理
前文提到,Github App 可以免去用户在第三方页面输入账号密码或者 Token 的操作而完成授权,那么它是怎么做到的呢?其实说白了,它也是一种 OAuth 登录的方式,只不过把获取 Token 的方式从“用户输入”变成“由 Github 提供”。
下面介绍这种登录方式的流程:
- A 网站跳转到 Github 的授权页面。
- Github 授权页面询问用户:“是否允许A网站获取下列权限”,用户点击“允许”,取得授权码。
- Github 授权页面重定向回 A 网站,同时在URL 上带上授权码。
- A 网站通过 URL 上的授权码往 Github 取回 Token。
- A 网站使用这个 Token 去调用 Github API。
要完成上述的流程,首先必须先注册一个 Github App。
注册 Github App
进入 Github主页,点击用户头像,找到 Setting/Developer settings/Github Apps,然后点击“New Github App”,即可进入编辑界面:
依次填入名称(此处为 SOMEONE:BLOG )、描述、主页 URL 以后,关键要在User authorization callback URL
填入获取授权后的回调地址,然后在Permissions
里面设置一些需要用到的 API 读写能力。如果你希望这个 APP 只能自己用,那么使用默认的Only on this account
,否则就选择Any account
,最后点击Create Github App
即可。
操作成功后,就可以看到这个 APP 的信息了:
其中的 Client ID 和 Client secret 就是这个应用的身份识别码,需要记下来。
Github App 注册完毕,接下来就需要第三方网站使用这个 APP 的 Client ID 去找 Github 要授权码了。
获取授权码
第三方网站要获取授权码,只需要让页面跳转到 Github 授权页即可,其中需要在 URL 中携带两个参数,分别是 Client ID 和 Redirect URL。
const CLIENT_ID = 'app 的 client id'
const REDIRECT_URL = 'app 的 redirect_url'
location.href = `https://github.com/login/oauth/authorize?` +
`client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URL}`
跳转后,Github 会询问用户是否允许这个 APP 获取某些权限:
用户确定后,会带着授权码重定向到给定的回调地址:
这时候,第三方页面(这里是 localhost:8080)已经拿到了授权码,接下来就需要凭借这个授权码以及 APP 的 Client ID 和 Client secret 去兑换 Token 了。
兑换 Token
兑换 Token 的代码如下:
router.post('/oauth', async function (ctx, next) {
const { clientID = CLIENT_ID, clientSecret = CLIENT_SECRET, code } = ctx.request.body
const { status, data } = await axios({
method: 'post',
url: 'http://github.com/login/oauth/access_token?' +
`client_id=${clientID}&` +
`client_secret=${clientSecret}&` +
`code=${code}`,
headers: {
accept: 'application/json'
}
}).catch(e => e.response)
ctx.body = { status, data }
})
由于跨域限制,所以这部分的代码必须通过服务端实现,换句话说,A 网站拿到授权码以后,需要发往这个服务端,由服务端获取 Token 后再重新返回给 A 网站。
A 网站拿到服务端返回的 Token 以后,就可以通过设置 Header 的方式在调用 Github API 的时候使用了:
'Authorization': `Bearer ${Token}`
到目前为止,基本已经 OK 了,但还有一个很大的问题,就是目前的 Token 所拿到的数据都是“只读”的,并不能对某个 Github 仓库进行任何提交或修改的操作——这是因为此 Github APP 还未被仓库所安装,这也是和 OAuth APP 最大的不同。
安装 Github APP
以我的博客平台 jrainlau.github.io 为例,如果希望用户能够通过 API 对某条 issue 发起评论等操做,我需要在这个仓库里安装我的 Github APP:
进入 Github APP 编辑页 Setting/Developer settings/Github Apps/SOMEONE:BLOG,找到左侧的 Install App,然后选择你的账户去安装:
你可以选择账户下的所有仓库或者仅某个仓库去使用这个 APP。点击授权以后,Github APP 安装完毕。此时通过授权的仓库都可以被用户通过 API 进行读写操作了。
在博客平台里,通过这个 APP 评论的用户,其外观上的体现也会标注来自 Github APP:
参考资料
- GitHub OAuth 第三方登录示例教程 ——阮一峰
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。