0%

Day21 - LINE Login 實作

前言

現在有規模的平台提供商基本上都會提供 Single Sign On (SSO)的登入機制,藉由他們的平台讓一般小網站可以讓使用者免密碼登入,雖然有很多種 Oauth2 的套件,不過我還是喜歡自己打 🤣,下面就開始嚕!

實作

views/建立一個line_login_auth.html,填入以下內容,主要是實作一個按鈕並透過 javascript 來互叫 api 並導向到 LINE 頁面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>LINE Login</title>
</head>
<body>
<button class="auth" onclick="auth()">LINE LOGIN</button>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script>
function auth() {
$.ajax({
url: 'https://YOUR_SERVERLESS_DOMAIN.amazonaws.com/dev/line/auth',
method: 'POST',
success: function(data) {
window.location.replace(data.result);
},
});
}
</script>
</html>

接著我們在.env加入 LINE Login 的環境變數並輸入對應的參數

1
2
3
LINE_LOGIN_CLIENT_ID=
LINE_LOGIN_SECRET=
LINE_LOGIN_URI=https://YOUR_SERVERLESS_DOMAIN.amazonaws.com/dev/line/auth

最後就是我們最重要的部分了,實作 GET a& POST 的 api,這邊我先從 POST 講起,這邊主要是把環境變數組在網址中回傳給前端,這邊需要注意的是state他是在回傳回來的 JWT 需要解碼時所使用的,這邊我簡單打個字串,然而實際上會隨機產個變數,確保解碼的安全。

GET 則是負責處理從 LINE 回來的參數們,這邊我只用回傳回來的stateid_token解開,拿到裡面的使用者的參數,後續可以參照應用把資訊存下來,放入 session 並告知前端已登入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from flask import request
from flask_restful import Resource
import requests
import webbrowser
import os
import jwt
import json


class LineLoginController(Resource):
def get(self):
r = requests.post(
"https://api.line.me/oauth2/v2.1/token",
data={
"grant_type": "authorization_code",
"code": request.args.get('CODE'),
"redirect_uri": os.environ.get('LINE_LOGIN_URI'),
"client_id": os.environ.get('LINE_LOGIN_CLIENT_ID'),
"client_secret": os.environ.get('LINE_LOGIN_SECRET'),
}, headers={"Content-Type": "application/x-www-form-urlencoded"})
payload = json.loads(r.text)
print(payload)
token = payload.get("id_token")
if token is None:
return {'result': payload['error_description']}, 400

state = request.args.get('state')
token = token.encode()
dt = jwt.decode(token, state, None, algorithms=['HS256'])
if dt:
return {'result': dt}, 200

def post(self):
r_uri = os.environ.get("LINE_LOGIN_URI")
client = os.environ.get("LINE_LOGIN_CLIENT_ID")
state = "nostate" # it will be random value
uri = f"https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id={client}&redirect_uri={r_uri}&scope=profile%20openid%20email&state={state}"
return {'result': uri}

JWT 解開的參數如下,sub就是 LINE 的唯一值 ID,一般拿到這個就可以知道是來自 LINE 的使用者了。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"result": {
"iss": "https://access.line.me",
"sub": "Ud6ab866a67xxxxxxxxb12b0baffb8ac",
"aud": "1622939248",
"exp": 1569925548,
"iat": 1569921948,
"amr": ["linesso"],
"name": "NiJia Lin",
"picture": "https://profile.line-scdn.net/0hKvTocMuLFFlpFj9HpKdrDlVTGjxxxxxxxxx1YHACMOPRtEHmkWJVIKUyUJNkQWGT0X",
"email": "loxxxxx09@gmail.com"
}
}

最後別忘了要在api.py加入路由才會被導出去ㄛ

1
2
from controller.line_login_controller import LineLoginController
api.add_resource(LineLoginController, '/line/auth')

結論

在實作的時候不知道為什麼一直不能讓 python 去幫我把用戶導去 LINE 認證的頁面,我記得之前寫 Ruby 可以啊(?),最後只好讓前端去幫我把使用者導出去,這邊就帶大家做到這嚕!