15

image

之前在使用 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提供的所有功能权限,且所获取的权限可以被设定为“只读”,“可读可写”和“禁止访问”,对于权限的授权粒度会更细。

image

获取了对某些操作的权限之后,我们就可以使用这些权限去搭建一个独立的 App,比如一个第三方的 Github 客户端等等,这也是 Github App 的实用之处。

第三方登录的原理

前文提到,Github App 可以免去用户在第三方页面输入账号密码或者 Token 的操作而完成授权,那么它是怎么做到的呢?其实说白了,它也是一种 OAuth 登录的方式,只不过把获取 Token 的方式从“用户输入”变成“由 Github 提供”。

下面介绍这种登录方式的流程:

  1. A 网站跳转到 Github 的授权页面。
  2. Github 授权页面询问用户:“是否允许A网站获取下列权限”,用户点击“允许”,取得授权码。
  3. Github 授权页面重定向回 A 网站,同时在URL 上带上授权码。
  4. A 网站通过 URL 上的授权码往 Github 取回 Token。
  5. A 网站使用这个 Token 去调用 Github API。

要完成上述的流程,首先必须先注册一个 Github App。

注册 Github App

进入 Github主页,点击用户头像,找到 Setting/Developer settings/Github Apps,然后点击“New Github App”,即可进入编辑界面:

image

依次填入名称(此处为 SOMEONE:BLOG )、描述、主页 URL 以后,关键要在User authorization callback URL填入获取授权后的回调地址,然后在Permissions里面设置一些需要用到的 API 读写能力。如果你希望这个 APP 只能自己用,那么使用默认的Only on this account,否则就选择Any account,最后点击Create Github App即可。

操作成功后,就可以看到这个 APP 的信息了:

image

其中的 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 获取某些权限:
image

用户确定后,会带着授权码重定向到给定的回调地址:
image

这时候,第三方页面(这里是 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}`

image

image

到目前为止,基本已经 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,然后选择你的账户去安装:

image

你可以选择账户下的所有仓库或者仅某个仓库去使用这个 APP。点击授权以后,Github APP 安装完毕。此时通过授权的仓库都可以被用户通过 API 进行读写操作了。

在博客平台里,通过这个 APP 评论的用户,其外观上的体现也会标注来自 Github APP:

image

参考资料


jrainlau
12.9k 声望11.7k 粉丝

Hiphop dancer,