之前在使用 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 第三方登入示例教程 ——阮一峰