取得 LINE 登入的三大步驟
上篇的「前置作業」都設定完後,本篇要進入程式碼的部份了,程式碼主要是調用 API 來取得 token 跟資料。
前端調用 API 會有跨網域的問題,因此要由後端來處理,這邊用的是 Google Apps Script(以下簡稱 GAS)。LINE 的說明文件是推薦 Heroku,另外這邊也推薦用 Google Cloud Platform。總之,就是寫 Node.js 的。
GAS 基本使用可看這篇:
Google Apps Script 基本使用:跨網域 AJAX、接 Firebase
GCP 的使用可看這篇:
用 Google Cloud Platform(GCP)建 Node.js 網站
有了執行 code 的工具,接下來就是要寫 code 了。
整理一下請使用者用 LINE 登入,並取得公開資料的三個步驟如下圖:
- 產出一個讓使用者點擊的 LINE 網址
- 拿 LINE 網址回傳的授權碼,調用 API 取得 token
- 拿 token 調用 API 取得使用者的公開資料
以下就開始對這三個步驟做點筆記。
1 產出一個讓使用者點擊的 LINE 網址
- 1-1 用
https://access.line.me/oauth2/v2.1/authorize
加上參數成為一個連結 - 1-2 使用者進到連結,LINE 會判斷是否登入過開啟不同頁面
- 1-3 使用者從 LINE 的頁面登入後,LINE 會轉址到
redirect_uri
填的網址,並帶上code
、state
這兩個參數
這步卡了最久,因為一直以為是要調用 API,但試著 GET、POST,回來的都是完整的 HTML 內容,後來是 Google 到了 這篇,才發現,啊,根本就不是 API,而是一個單純的連結,只要讓使用者點擊連結到 LINE 的頁面,是否登入 LINE 會判斷。
既然只是一個單純的連結,那們們就生出連結就行,連結的架構如下:
這邊為了閱讀上好看,所以每個參數用成一行,實際使用時請整個連結是一行,官方給的範例是這樣:
https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=1234567890&redirect_uri=https%3A%2F%2Fexample.com%2Fauth&state=12345abcde&scope=openid%20profile&nonce=09876xyz
以下筆記各參數要填的東西。
response_type
這個是使用者登入後,請LINE回傳的東西,固定填「code」(授權碼)。
client_id
就是上篇 建立 Channel 後,後台顯示的 Channel ID。
redirect_uri
就是上篇在 Channel 後台,LINE Login 頁籤 裡填入的 Callback URL。
如果這邊寫的值,跟 Callback URL 不一樣,即便使用者登入了也會報錯,LINE 不會回傳資料。
state
這個是可以填我們自己想要的驗證碼,使用者在登入後,LINE 會回傳 code、state 這兩個參數,state 的值就是我們在這邊寫入的,可以判斷當 state 回來的值跟我們寫入的是一樣時,才執行某個 function,防止被大量登入變成攻擊。
scope
想要取得的公開資料,可以填三個值:profile、openid、email。
可以一次三個全取,用 %20
連接,像這樣:
...&scope=openid%20profile%20email
連結建立完後,就可以讓使用者點擊連結連到 LINE,LINE 會判斷使用者是否連動過,桌機版的話會給出不同頁面,手機上的話如果連動過就會直接回傳 code 跟 state 了,不會要求再次登入。
拿 Let’s Write 訂閱電子報來載圖,桌機版如下:
如果曾經授權過,之後就只會要求登入,不會再要授權。
使用者登入 LINE 並且有授權連動,LINE 就會把頁面轉址到 redirect_uri 的那個網址,本篇是用 GAS 的,redirect_uri 填的是 GAS 發佈後產的網址,因此這一步最後會收到的網址如下:
https://script.google.com/macros/s/xxxxxxxxxxxxxxxxxxxxxxxxx/exec?code=xdl9dGKAT242TWQq0koa&state=abc
可以看到確認帶了二個參數:code、state。
state 這邊亂填,就填了 abc,所以這邊回來的也就寫 abc。
code 就是授權碼,下一步就是要拿 code 的值去調用 API 要 token。
code 一次只能保留 10 分鐘,10 分鐘過後這組 code 就沒用了。
這段介紹的參數都是文件中規定必填的參數,完整的參數可以看 官方文件。
2 拿 LINE 網址回傳的授權碼,調用 API 取得 token
- 2-1 拿 code 去調用
https://api.line.me/oauth2/v2.1/token
- 2-2 API 回傳的資料會有一個
id_token
,這個就是下一步取使用者資料要用的
上一步我們拿到了 code(授權碼),這一步就是拿這個 code 去調用取 token 的 API。
這一段也卡了一陣子,因為官方文件中是用 crul 去做示範的,而 August 在寫的時候是用 GAS,同樣是 POST,但寫法不同。
官方文件的 Demo 換用 GAS 是寫成這樣
調用 API 時有幾個必要的參數,這邊寫一下:
grant_type
固定填 authorization_code
。
code
這是上一步取得的 code,因為值會附在回來的網址中,所以在 GAS 就用 e.parameter.code
來取得。
redirect_uri
跟第一步一樣是填要轉址的網址,一樣要跟 Channel 的 Callback URL 相同。
這邊用 GAS,所以一樣是填發佈後的網址。
client_id
跟第一步一樣,填 Channel ID。
client_secret
在上篇中有提到,建完 Channel 後,可以從後台看到 Channel ID、Channel secret。client_secret 就是填 Channel secret。
POST API 後,回傳的值有六個:access_token、expires_in、id_token、refresh_token、scope、token_type。
我們只需要其中的 id_token,拿 id_token 就可以進到下一步取資料了。
如果想了解其他的值代表什麼意思,可以看 官方說明文件。
3 拿 token 調用 API 取得使用者的公開資料
- 3-1 拿 id_token 調用 Social API
- 3-2 回來的值就會包含 name、picture、email
這一步就是拿剛剛得到的 id_token,去調用 Social API,就可以取得使用者的名稱、大頭照、email。主要是看第一步建立網址時,scope 裡寫了什麼,如果沒寫 email,那就不會有 email。
官方說明文件的範例一樣是用 curl:
curl -v -X POST 'https://api.line.me/oauth2/v2.1/verify' \
-d 'id_token=eyJraWQiOiIxNmUwNGQ0ZTU2NzgzYTc5MmRjYjQ2ODRkOD...'
August 一樣改寫成用 GAS 來調用 API:
調用 Social API 只有兩個參數是必填:
id_token
就是上一步取得的 id_token。
client_id
就是 Channel ID。
API 回傳的值會像這樣:
{ "iss": "https://access.line.me", "sub": "U1234567890abcdef1234567890abcdef", "aud": "1234567890", "exp": 1504169092, "iat": 1504263657, "nonce": "0987654asdf", "amr": [ "pwd", "linesso", "lineqr" ], "name": "Taro Line", "picture": "https://sample_line.me/aBcdefg123456", "email": "[email protected]" }
最下面三個的 name、picture、email,就是我們最後要取得的資料。
sub 是指每一位使用者的獨立 ID。
如果想了解每一個值的意思,可看 官方說明文件。
三大步驟的完整程式碼
扣掉第一部是單純的連結,August 附上實作可取得使用者公開資料的程式碼。
程式碼的最後是把資料傳到 Firebase 上做儲存,因此有 在 GAS 引入 Firebase 的功能。
最後實際的使用結果,可以直接掃 QR code,掃完後也順便訂閱了 Let’s Write 的電子報了。但…目前還沒有寫一個電子報的版出來,所以前期還是會以 LINE 的推播為主,也歡迎加入~
請掃描(手機就直接點)這 QR code 訂閱電子報並加入 Let’s Write 的 LINE 官方帳號:
2020.04.02 更新:
最近 August 研究了 Auth0 的註冊會員 方式,裡面也可以直接用 LINE 登入,因此本站由今天開始改採用了 Auth0 來註冊、登入會員。以下的 QRcode 連結改為 Auth0 的登入。
參考資源
Integrating LINE Login with your web app
Really strange error post with google script