# 整合 LINE Login 與 web app

本頁將說明如何支援 OpenID Connect (opens new window) 協議,並且透過 ID Token 取得用戶資訊的LINE Login整合到 Web app 中。 如果您沒有可以整合 LINE Login 的應用程式,可以使用範例的應用程式。請參閱「開始使用 LINE Login」。

注意

# 登入的流程

透過 LINE Login 登入網站的流程 (web login) 是以 OAuth 2.0 授權碼核發流程 (authorization code grant flow) (opens new window)OpenID Connect (opens new window) 協議為基礎。開發者的應用必須能發出 server-side request,並接收來自 LINE Platform 的資料。以下為 web login 流程總覽:

Web login flow

# 建立 Channel

建立「LINE Login Channel」,並且設定為 Web app。

# 設定 Callback URL

用戶執行身份驗證和授權的操作之後,Web app 將使用 Callback URL,接收授權碼和state

請在 LINE Developers Console 設定的 LINE Login 頁籤中,設定 channel 的 Callback URL。

在 1 個 channel 中,可以指定多個 Callback URL。

重新導向設定

# 申請 Email 存取權限

使用 LINE Login v2.1 時,可以取得使用 LINE Login 登入的用戶電子郵件地址。

若需要透過 Web app 取得用戶的電子郵件地址時,請事先從 LINE Developers Console 申請取得電子郵件地址的權限。

  1. Basic settings 表單中的 OpenID Connect 欄位點選 Apply

    申請取得電子郵件地址的權限

  2. 同意使用條款並上傳畫面截圖,說明開發者正在收集使用者的 email address 及其用途。

如申請獲得接受,Email address permission 欄位將顯示 Applied

# 要求授權

為進行使用者認證,並為 app 要求權限,請透過所需的 query parameter,如下面例子所示將使用者重新導向授權 URL。

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=profile%20openid&nonce=09876xyz

可以分配給授權 URL 的查詢屬性如下。

屬性 類型 必須 說明
response_type String 必須 code
client_id String 必須 LINE Login Channel 的 Channel ID。您可以在LINE Developers console中查看。
redirect_uri String 必須 LINE Developers Console 中註冊的 Callback URL。
state String 必須 用於防止跨站請求偽造 (opens new window)的唯一性屬性。 請在 Web app 請求中,針對每個登入階段隨機生成。注意,不能使用 URL 編碼過後的文字。
scope String 必須 向用戶請求授予權限。詳細內容請參閱「Scopes」。
nonce String 選填 用於防止Replay attack (opens new window)的文字。此值會在 Response 的 ID Token中。
prompt String 選填 consent。即使已授予用戶所有請求的權限,仍然強制顯示同意畫面。
max_age Number 選填 用戶認證後,允許的最長經過時間(以秒為單位)。相當於在OpenID Connect Core 1.0 (opens new window)的「Authentication Request」區段中,所定義的max_age屬性。
ui_locales String 選填 在 LINE Login 中,所顯示畫面的顯示語言和文字類型。按照優先度從高到低的順序,在用空格分隔的列表中,設定在RFC 5646(BCP 47) (opens new window)中定義的語言標籤。相當於在OpenID Connect Core 1.0 (opens new window)的「Authentication Request」區段中,所定義的ui_locales屬性。
bot_prompt String 選填 用戶登入時,會顯示將 LINE 官方帳號加入好友的選項。指定normalaggressive。詳細內容請參閱「在登入時將 LINE 官方帳號加為好友 (bot link)」。
initial_amr_display String 選填 若設定為 lineqr 將會顯示 QR code 登入 來取代預設的 email 登入
switch_amr Boolean 選填 預設值為 true。若設定為 false,則會隱藏用於更改登錄方式的按鈕,例如 "透過 Email 登入" 或 " QR Code 登入" 的選項。
disable_ios_auto_login Boolean 選填 預設為 false,若設定為 true,將會停止在 iOS 上的自動登入機制
提示
  • 在 Web app 中,新增 LINE Login 按鈕時,請按照「LINE Login 按鈕 設計準則」的規定。
  • 您也可以不顯示 LINE Login 按鈕,直接連結到授權 URL。
  • 用戶的驗證資訊,不會通知 Web app。
關於 LIFF 瀏覽器中的授權請求

在 LIFF 瀏覽器中,透過 LINE Login,發行授權請求時,不能保證其動作。另外,使用外部瀏覽器,開啟 LIFF 應用程式時,請使用 liff.login() 代替 LINE Login 的授權請求。

# Scopes

開發者可將 Scope parameter 指定為下列 scope,必須指定至少一個 scope,亦可用 URL 編碼空白字元 (%20) 區隔,指定多個 scope:

Scope 用戶資訊 ID Token
(包括用戶 ID)
ID Token 中的
顯示名稱
ID Token 中的
個人資料圖片 URL
ID Token 中的
電子郵件地址
profile - - - -
profile%20openid -
profile%20openid%20email ✓(※)
openid - - - -
openid%20email - - - ✓(※)

註記:要指定 email,並要求用戶取得 Email 的權限時,請先申請取得 Email 的權限

上面未列出的 Scopes 請求

# 用戶身份驗證

用戶身份驗證是直接在 LINE platform 上進行

在 Web App 中使用 LINE Login 無需實作身份驗證功能。

被重新導向到授權 URL 的用戶,可以使用以下任一種身份驗證方法進行登入:

身份驗證方法 說明
自動登入 用戶無需操作即可登入。不會顯示 LINE Login 畫面和確認畫面
以電子郵件地址登入 在 LINE Login 畫面中,輸入電子郵件地址和密碼進行登入
掃瞄 QR Code 登入 利用手機版的 LINE QR Code 讀取功能,掃瞄 LINE Login 畫面中顯示的 QR Code 進行登入
透過單一登入功能(SSO)進行登入 在顯示「使用以下帳號登入」的確認畫面中,點擊登入按鈕進行登入
關於自動登入與 SSO

在可以同時使用自動登入和 SSO 的環境中,將以 SSO 優先運作。詳細內容請參閱 FAQ 中的「登入 LINE 時,會顯示「使用以下帳號登入」的畫面。

關於登入通知

登入時,將從 LINE 官方帳號發送登入通知。有關登入通知,請參閱「常見問題」中的「收到「您的LINE帳號已在其他裝置上成功登入」的提醒訊息 (opens new window)」和「收到「您已使用◯◯登入◯◯」的提醒訊息 (opens new window)」。

驗證用戶的身份驗證

可以透過 ID Token 驗證用戶的身份。有關 ID Token,請參閱「取得 Access token」中的「回應」。

# 自動登入

用戶無需操作即可登入。將不會顯示 LINE Login 畫面和確認畫面。

在已登入 LINE 手機版的狀態下,使用以下瀏覽器,存取授權 URL 時,將會自動登入:

  • LINE 中的瀏覽器
  • 首次登入 LINE 的外部瀏覽器
自動登入不適用於 PC 版的 LINE

有關可以使用自動登入的環境,詳情請參閱 FAQ 的「請告知有關自動登入的資訊」。

# 電子郵件地址登入、QR Code 登入

用戶可以使用以下任一種身份驗證方法登入:

  • 電子郵件地址登入
  • QR Code 登入(只會在 PC 版的 LINE 中顯示)

LINE Login畫面

在尚未登入 LINE 手機版的狀態下,在外部瀏覽器中,首次開啟授權 URL 時,可以使用這些登入方法。

# 透過單一登入功能(SSO)進行登入

用戶只需點擊登入按鈕即可登入。

確認畫面

當用戶已經登入過 LINE,此時訪問外部瀏覽器來存取授權 URL 時,即可使用 SSO。

使用 Cookie 打造 SSO 的功能

只要曾經從 Web app 登入到 LINE,將會在 Cookie 中儲存 access.line.me 的網域名稱。只要 Cookie 尚未過期,當您在相同的瀏覽器登入時,就會顯示 SSO 畫面。

# 進行用戶授權

授權流程將會直接在 LINE platform 上執行

已加入 LINE Login 的 Web app 不必實作與用戶授權的功能。

用戶可以授權給開發者在指定的 scope 屬性中存取權。

注意,用戶可以在使用 Web app 時不同意授予存取權限。因此在開發 Web app 時,請考慮到用戶可能會拒絕授予在授權 URL 中指定欄位的權限。

同意畫面範例:

同意畫面

不會顯示同意畫面
  • scope 屬性指定的權限為 profileopenid時 ,如果用戶已授予許可,就不會顯示同意畫面。
  • 如果權限中包括 email,只要電子郵件地址不變,從用戶同意後的一定週期內,不會再顯示同意畫面。

# 在 Web app 中接收授權碼或錯誤訊息

完成用戶的身份驗證和授權過程後,會將用戶重新導向至當時所設定的 Callback URL 上。

當用戶授予對應用程式的存取權限時,將會傳送授權碼(authorization code)。另外,如果用戶拒絕授予應用程式存取權限時,則將回傳錯誤訊息。

# 取得授權碼

完成用戶的身份驗證和授權後,用戶將被重新導向到當時設定 Callback URL 並包含以下屬性:

屬性 類型 說明
code String 只能使用一次,用於取得 Access token 的授權碼,有效期間為 10 分鐘。
state String 用以防止跨站請求偽造 (cross-site request forgery) (opens new window),此值應由開發者的應用程式隨機產生。請確認該值與授予授權 URL 時的 state 屬性值一致。
friendship_status_changed Boolean 如果在登入時,被綁定的 LINE 官方帳號與用戶之間的狀態發生變更,則為true,其餘則為false
注意:只有在要求用戶進行身份驗證和授權時,指定bot_prompt屬性,並且在用戶登入後,顯示將 LINE 官方帳號加入好友的選項時,才會回傳此屬性。詳細內容請參閱「在登入時將 LINE 官方帳號 (LINE Official Account) 加為好友 (bot link)」。

重新導向 URL 範例:

https://example.com/callback?code=abcd1234&state=0987poi&friendship_status_changed=true

# 接收錯誤訊息

如果用戶拒絕授予應用程式請求的權限時,則會將其重新導向至當時設定的 Callback URL 並包含以下屬性:

屬性 類型 必須 說明
error String 必須 錯誤代碼
error_description String 選填 錯誤內容
state String 選填 授權 URL 中包含的 state 屬性。此值可以讓您確定是哪個過程遭到拒絕。

重新導向 URL 範例:

https://example.com/callback?error=access_denied&error_description=The+resource+owner+denied+the+request.&state=0987poi

# 透過 Web app 取得 Access token

透過 LINE platform 接收到授權碼與 state 屬性,需與要求用戶進行身份驗證和授權時所指定的 state 屬性一致,即可取得 Access Token。

關於更多取得 Access Token 的內容,請參閱 LINE Login v2.1 API 中的 issue access token

範例:

curl -v -X POST https://api.line.me/oauth2/v2.1/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=xxx' \
-d 'redirect_uri=xxx' \
-d 'client_id=xxx' \
-d 'client_secret=xxx'

# 回應

LINE platform 會驗證來自應用程式的請求,並將回傳如下表中 Access token 的屬性給應用程式。

注意

新增或修改 LINE Login 功能時,Response 或者 ID Token 的 JSON 內容可能會有所差異,差異的內容包括新增屬性、更改順序、數值之間是否存在空格或換行、以及長度的更改。為了避免因為收到和以往不同的 Payload 時導致問題產生,因此建議請架設伺服器處理相關問題。

屬性 型別 描述
access_token String Access token。有效期間為 30 天。
expires_in Number Access token 過期之前的秒數。
id_token String 包含用戶資訊JSON Web Token(JWT) (opens new window),只有將 Scopes 指定為openid時,才會回傳此屬性。詳細內容請參閱「從 ID Token 取得個人資訊和 Email」。
refresh_token String 為了取得新的 Access token,所需要的 Token。有效期間為發行 Access token 後的 90 天內。
scope String 使用者提供的權限屬性。然而,即便使用者已提供權限,email scope 仍不會被回傳。
token_type String Bearer

以下是 JSON 回傳內容範例:

{
  "access_token": "bNl4YEFPI/hjFWhTqexp4MuEw5YPs...",
  "expires_in": 2592000,
  "id_token": "eyJhbGciOiJIUzI1NiJ9...",
  "refresh_token": "Aa1FdeggRhTnPNNpxr8p",
  "scope": "profile",
  "token_type": "Bearer"
}

詳細內容請參閱「LINE Login v2.1 API 參考資料」中的「發行 Access token」。

# 從 ID Token 取得個人資訊

LINE platform 是依據 OpenID Connect (opens new window) 的規格發行 ID Token,因此可以安全的從 LINE platform 取得用戶的個人資訊(User ID、顯示名稱、個人圖片、Email)。

若您已經有 LINE Profile+ 的權限,可以取得在 LINE Profile+ 中註冊的資訊(姓名、性別、生日、電話號碼、地址)。詳細內容請參閱「取得在 LINE Profile+ 中註冊的資訊」。

使用 ID Token 時,可以透過以下任一方式來驗證 ID Token。

# 使用 LINE Login API 的 Endpoint

能在您所設定的 Endpoint 獲取 ID Token ,並從中取得 Access token 以及 LINE Login 的 channel ID,如此一來就能驗證 ID Token 以及取得用戶的個人資訊和 Email。

請求範例:

curl -v -X POST 'https://api.line.me/oauth2/v2.1/verify' \
 -d 'id_token=eyJraWQiOiIxNmUwNGQ0ZTU2NzgzYTc5MmRjYjQ2ODRkOD...&client_id=1234567890'

回傳範例:

{
  "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": "taro.line@example.com"
}

詳細內容請參閱「LINE Login API」中的「驗證 ID Token」。

# 撰寫驗證 ID Token 的驗證程式

您可以使用任意的JWT 函式庫 (opens new window)或自行撰寫驗證程式來驗證 ID Token,並取得用戶的個人資訊和 Email。

# ID Token

ID Token 是包含用戶資訊的 JSON Web Token(JWT)。ID Token 是由(.)來劃分 Header、Payload 以及 Signature 並組成的字串,每個部分均以 base64url 編碼而成。詳細內容請參閱JWT 的規格 (opens new window)

以下表格則是 Header 裡會包含的屬性:

屬性 型別 描述
alg String ID token 的簽證演算法。若是 Native Apps、LINE SDK 或 LIFF apps 將會回傳 ES256 (ECDSA using P-256 and SHA-256),而若使用 Web Login,將會回傳 HS256 (HMAC using SHA-256)。
type String Payload 格式,將會回傳 JWT
kid String Public key ID,當 Header 的屬性中 algES256 時將會被包含至其中。若想了解更多關於 kid 屬性的資訊,參閱 JSON Web Key (JWK) document (opens new window)。如何取得 public key,參閱 Get public key using kid

以下是解碼後的 Header 範例。

algHS256時:

{
  "typ": "JWT",
  "alg": "HS256"
}

algES256時:

{
  "typ": "JWT",
  "alg": "ES256",
  "kid": "a2a459aec5b65fa..."
}
# Payload

Payload 中的用戶資訊:

屬性 型別 描述
iss String https://access.line.me 產生 ID token 的 URL。
sub String 據以產生 ID token 的 user ID。
aud String Channel ID
exp Number Token 的有效期限(UNIX 時間)
iat Number ID token 產生的時間 (UNIX 時間)。
auth_time Number 使用者接受認證的時間 (UNIX 時間)。如未於授權要求中指定 max_age parameter,將不包含此 property。
nonce String 於授權 URL 中指定的 nonce 值。如未於授權要求中指定 nonce 值,將不包含此 property。
amr Array of String 用戶使用的身份驗證方法列表。包含以下其中之一的值。有關各種驗證方法,請參閱「由用戶進行用戶驗證」。
  • pwd:以 email 與密碼登入
  • lineautologin:使用 LINE 自動登入(包括使用 LINE SDK)
  • lineqr:使用 QR Code 登入
  • linesso:單一登入 (single sign-on)
name String 用戶的顯示名稱。如果未在授權請求中指定 profile 範圍時,則不包含在內。
picture String 用戶個人資料的圖片 URL。若尚未授權狀態中請求 Scope 中的 profile,則該欄位不包含在內。
email String 用戶的電子郵件。若尚未授權狀態中請求 Scope 中的 email,則該欄位不包含在內。

經解碼的 payload 範例:

{
  "iss": "https://access.line.me",
  "sub": "U1234567890abcdef1234567890abcdef ",
  "aud": "1234567890",
  "exp": 1504169092,
  "iat": 1504263657,
  "nonce": "0987654asdf",
  "amr": ["pwd"],
  "name": "Taro Line",
  "picture": "https://sample_line.me/aBcdefg123456"
}

# 使用 kid 取得 public key

透過 Native apps、LINE SDK 或 LIFF apps 去得 ID token 後並解碼其 Header 部分,以獲取 kid 屬性當中的 public key,步驟如下:

  1. 確認 kid 是來自於解碼 ID token 後的 Header
  2. 訪問 LINE OpenID Connect Discovery document URL (opens new window) 並接收 Response 物件。 有關 Response 物件中包含的屬性以及更多資訊,請參閱 OpenID Connect Discovery 1.0 (opens new window)
  3. Response 物件包含了 jwks_uri 屬性及其值 https://api.line.me/oauth2/v2.1/certs,在訪問 JSON Web Key (JWK) document URL (opens new window) 後並接收 JSON Web Key(JWK) 的陣列,更多關於 JWK 的規格,請參閱 JSON Web Key (JWK) (opens new window)
  4. 在 JSON 物件中,會包含在步驟 1 所定義的公鑰 kid 屬性。
# Signature

使用 Signature 來驗證 Response 的是否正確。整個字串以 Header + "." + Payload 後組合成一組字串,使用 channel secret 作為密鑰並透過 HMAC SHA-256 演算法加密在以 base64url 編碼之後作為 Signature 發出。為了確保應用程式的安全,必須驗證 Signature 的 Token。

# 將 ID Token 解碼並進行驗證

要解碼和驗證 ID Token 時,請選擇您熟悉的 JWT 函式庫或遵循此步驟來驗證。

# 使用 JWT 函式庫

可以透過普遍的 JWT 函式庫 (opens new window)將 ID Token 解碼並進行驗證。以下是使用 Python® 函式庫解碼 ID Token 的範例。

import jwt

decoded_id_token = jwt.decode(id_token,
                              channel_secret,
                              audience=channel_id,
                              issuer='https://access.line.me',
                              algorithms=['HS256'])

# check nonce (Optional. But strongly recommended)
nonce = '_stored_in_session_'
expected_nonce = decoded_id_token.get('nonce')
if nonce != decoded_id_token.get('nonce'):
    raise RuntimeError('invalid nonce')
# 將 ID Token 解碼並進行驗證
  1. 以句點 "." 作為分隔符號,來分割 Header、Payload 和 Signature。
  2. Base64-URL 為每部分進行解碼。
  3. 計算 hash,以 base64url 編碼的 header + "." + payload 為值,並以 channel secret 為金鑰 (key)。請驗證此值與解碼的 signature 相符。
  4. 確認 iss 的值為 https://access.line.me,並確認 ID Token 是否來自 LINE。
  5. 確認 aud 的值與您使用的 Channel ID 相符,以確保 ID token 屬於此 channel。
  6. 請確認驗證時 exp 值大於 UNIX timestamp,以確保 ID token 的有效性。
  7. 為防止 replay attacks,請確認 nonce 與指定於授權要求中的 nonce 值相符。請將 nonce 值與使用者 session 一同儲存,儘管並非必要步驟,仍強烈建議開發者儲存 nonce 值。

以下是使用 Python 3 的範例:

import base64
import hashlib
import hmac
import json
import time


def base64url_decode(target):
    rem = len(target) % 4
    if rem > 0:
        target += '=' * (4 - rem)

    return base64.urlsafe_b64decode(target)


def check_signature(key, target, signature):
    calc_signature = hmac.new(
        key.encode('utf-8'),
        target.encode('utf-8'),
        hashlib.sha256
    ).digest()

    return hmac.compare_digest(signature, calc_signature)


def decode_id_token(id_token, channel_id, channel_secret, nonce=None):
    # step 1
    header, payload, signature = id_token.split('.')

    # step 2
    header_decoded = base64url_decode(header)
    payload_decoded = base64url_decode(payload)
    signature_decoded = base64url_decode(signature)

    # step 3
    valid_signature = check_signature(channel_secret,
                                      header + '.' + payload,
                                      signature_decoded)
    if not valid_signature:
        raise RuntimeError('invalid signature')

    payload_json = json.loads(payload_decoded.decode('utf-8'))

    # step 4
    if payload_json.get('iss') != 'https://access.line.me':
        raise RuntimeError('invalid iss')

    # step 5
    if payload_json.get('aud') != channel_id:
        raise RuntimeError('invalid aud')

    # step 6
    if int(time.time()) > payload_json.get('exp'):
        raise RuntimeError('invalid exp')

    # step 7 (Optional. But strongly recommended)
    if nonce is not None:
        if payload_json.get('nonce') != nonce:
            raise RuntimeError('invalid nonce')

    return payload_json

# 下一步

您可以使用取得的 Access token,來執行以下操作: