# 整合 LINE Login 與 web app

本頁介紹如何整合 LINE Login 與 web 應用。如開發者尚未建立應用,但仍想透過範例應用體驗 LINE Login,請至開始使用 LINE Login

注意

本頁介紹如何整合 LINE Login v2.1,此版本支援 OpenID Connect 協議,並讓開發者可用 ID token 取得使用者資料。如欲整合 LINE Login v2 與 web app,請至 Integrating LINE Login v2

此外,如 app 環境許可,強烈建議開發者以 LINE SDK 整合 LINE Login。請查看 Integrating with native apps

# Login 流程

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

LINE Login auth flow

Web login 流程包含下列步驟:

  1. 開發者的應用將使用者導向 LINE Login 授權 URL,該 URL 包含所需的 query parameter。 LINE Login 視窗將於 browser 中開啟,使用者將登入以接受認證。在 LINE Platform 驗證使用者憑證 (credential) 後,使用者也須同意為開發者的 app 提供所要求的存取權限。 LINE Platform 透過 query string 包含授權碼與 stateredirect_uri 將使用者導回 app。
  2. 開發者的應用持授權碼,要求 https://api.line.me/oauth2/v2.1/token endpoint 核發 access token。
  3. LINE Platform 驗證此應用的要求後,將核發 access token。

開發者取得 access token 後,便可用以呼叫 Social API

# 開始之前

開始整合應用與 LINE Login 前,請確認已先完成下列事項:

# 設定 Channel

# 設定重新導向的 URL

如欲設定使用者登入後被重新導向的頁面,請至 LINE Developers Console 中 channel settings 下的 LINE Login 表單設定 callback URL。

開發者可設定多個 callback URL。

Redirect settings

# 申請 Email 存取權限

開發者可要求透過 LINE Login 登入的使用者提供權限,以取得使用者的 email address。首先,請於 LINE Developers Console 申請權限。

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

    Applying for email permission

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

開發者可使用 LINE Login button 或 URL,將使用者導向指定頁面。

URL 請包含下列所需的 query parameter:

Parameter 類型 Required 說明
response_type String Required code 指定 LINE Platform 回傳授權碼。
client_id String Required Channel ID。LINE 為開發者 channel 發佈的專屬識別碼 (unique identifier)。
redirect_uri String Required Callback URL。使用者於認證及授權後,將被重新導向至此 URL 頁面。此 URL 必須與 LINE Developers Console 中為 channel 註冊的其中一個 callback URL 相符。
state String Required 專屬的 alphanumeric string,用於防止跨站請求偽造 (cross-site request forgery)。此值應由開發者的應用隨機產生。URL 編碼字串不適用。
scope String Required 使用者提供的權限屬性。開發者可用 URL 編碼空白字元 (%20) 區隔,指定多個 scope。如欲取得更多相關訊息,請至 scopes
nonce String Optional 用以防止 replay attack 的字串。此值將附加於 ID token
prompt String Optional consent 用於強迫顯示同意畫面,無論使用者是否已提供所有被要求的存取權限。
max_age Number Optional 認證有效的實際時間 (以秒為單位)。與 OpenID Connect Core 1.0 中 "Authentication Request" 介紹所定義的 max_age parameter 相同。
ui_locales String Optional 於 LINE Login 畫面中顯示語言。指定一個或更多 RFC 5646 (BCP 47) 語言 tag,以 space 區隔,並依偏好決定順序。與 OpenID Connect Core 1.0 中 "Authentication Request" 介紹所定義的 ui_locales parameter 相同。
bot_prompt String Optional 指定如何顯示將 LINE 官方帳號加為好友的選項,可指定為 normalaggressive。如欲取得更多相關訊息,請至 Linking a LINE Official Account with your LINE Login channel

# Scopes

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

  • profile:存取使用者資料的權限。
  • openid: 用以取得 ID token。如欲取得更多相關訊息,請至 ID tokens
  • email: 存取使用者 email address 的權限。必須同時指定 openid。如欲取得更多相關訊息,請至 ID tokens

Access token 必須包含 profile scope,以取得好友狀態 (此處指使用者與 LINE 官方帳號間的好友狀態)。

# 認證與授權流程

當使用者被重新導向授權 URL,LINE Platform 將自動檢視使用者的 session 以確認其登入狀態。

# 如使用者未登入 LINE

使用者會被重新導向至 LINE Login 視窗以接受認證。使用者必須登入 LINE。(特定情況下,使用者不須進行登入。請查看 FAQ entry 中的 "How does auto login work?")

Login dialog

# 如使用者已登入 LINE 且 Session 已產生

則螢幕將顯示確認畫面,而非 LINE Login 視窗。

Confirmation Screen

使用者登入後,螢幕便會顯示同意畫面,要求使用者提供 scope parameter 指定的權限。如指定的權限為 profile 與/或 openid,且使用者已同意提供權限,則不會顯示同意畫面。如權限包含 email,則特定時間內將不會顯示同意畫面,除非使用者的 email address 經過變更。

同意畫面

# 取得授權碼

當完成使用者認證與授權,HTTP status code 302 與下列 query parameter 將被包含於 callback URL:

Parameter 類型 說明
code String 用以取得 access token 的授權碼。效期為 10 分鐘。此授權碼僅能使用一次。
state String state parameter,包含於 original request authorization URL 中。開發者應確認此值與 original request 的值相符。
friendship_status_changed Boolean true 如使用者與 LINE 官方帳號的好友狀態於登入時改變。如未改變,則為 false。返回 (return) 此值的條件為:於授權要求中指定 bot_prompt query parameter,且顯示的同意畫面包含將開發者 LINE 官方帳號加為好友的選項。如欲取得更多相關訊息,請至 Linking a LINE Official Account with your LINE Login channel

Response 範例:

HTTP/1.1 302 Found
Location : https://client.example.org/cb?code=abcd1234&state=0987poi&friendship_status_changed=true

# Error Response

如使用者拒絕為開發者的應用提供權限,下列變數將被附加至 callback URL query string。

Parameter 類型 Required 說明
error String Required Error code。
error_description String Optional 此 error 的敘述為可讀的 ASCII 編碼文字。
state String Optional OAuth 2.0 state 值。如 state parameter 包含授權要求,則此為必要值。

Error response 範例:

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

如使用者拒絕為開發者的應用提供權限,該應用應妥善處理此錯誤。

# 取得 Access Token

為取得 access token,請以授權碼提交 HTTP POST request。取得 access token 後,開發者便可將其用於呼叫 API。此 access token 是透過下列 endpoint 發佈:

# Request

POST https://api.line.me/oauth2/v2.1/token
Request header 說明
Content 類型 application/x-www-form-urlencoded

# Request Body

Request body 中的資料為 form-urlencoded 格式。

Parameter 類型 Required 說明
grant_type String Required authorization_code 指定授權類型。
code String Required 授權碼
redirect_uri String Required Callback URL
client_id String Required Channel ID 於 LINE Developers Console 取得。
client_secret String Required Channel secret。於 LINE Developers Console 取得。

# 範例 Request

HTTP POST request 取得 access token 的範例:

curl -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'

# Response

LINE Platform 驗證 request 並核發 access token 及下表中的資料。

新的或經變更的 LINE Login 功能,可能導致 payload JSON object 結構變化。此類變化可能包括新增 property、property 順序改變,以及新增/移除空白與換行。請進行後端設計以因應非預期的 payload data object 結構。

Property 類型 說明
access_token String Access token。效期為 30 天。
expires_in Number Access token 到期前的時間 (以秒為單位)。
id_token String 包含使用者相關資料的 JSON Web Token (JWT),必須將 scope 指定為 openid 才會回傳此 JWT。如欲取得更多相關訊息,請至 Verify ID token
refresh_token String 用於取得新 access token 的 token。Access token 到期後 10 天內有效。
scope String 使用者提供的權限屬性。然而,即便使用者已提供權限,email scope 仍不會被回傳。
token_type String Bearer

JSON response 範例:

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

# 透過 ID Token 取得使用者資料與 Email Address

LINE Platform 發佈的 ID token 符合 OpenID Connect 規範,開發者可安全地從 LINE Platform 取得使用者資料與 email address。然而,使用 ID token 中的資料前,請先以下列方法之一驗證 token:

# 使用 Social API Endpoint

如欲驗證接收到的 ID token (此 ID token 為 access token 的一部分),並取得使用者資料與 email,請將此 ID token 發送至專用的驗證 endpoint。

如欲取得更多相關訊息,請查看 Social API reference 頁面中的驗證 ID token

# 撰寫程式碼以驗證 ID Token

開發者可使用任何 JWT library 或自行撰寫程式碼,以驗證 ID token 及取得使用者資料與 email address。

# ID Token

ID token 為包含使用者資料的 JSON web token (JWT)。ID token 是由 header、payload 及 signature 組成,三者以句號 (.) 字元區隔。每個部分皆為 base64url 編碼值。如欲取得更多訊息,請查看 JWT 規格。

經解碼的 header 值範例。此範例中,header 宣告經加密的 object 使用 HMAC SHA-256 演算法。LINE Login 僅使用 HMAC SHA-256。

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

可自 payload 中取得使用者資訊。

Property 類型 說明
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 strings 使用者所採用的認證方式列表,可採用其中一項或更多:
  • pwd: 以 email 與密碼登入
  • lineautologin: LINE 自動登入 (包含透過 LINE SDK)
  • lineqr: 以 QR code 登入
  • linesso: 單一登入 (single sign-on)
name String 使用者的顯示名稱。如未於授權要求中指定 profile scope,則使用者資訊將不包含此項目。
picture String 使用者的個人圖片 URL。如未於授權要求中指定 profile scope,則使用者資訊將不包含此項目。
email String 使用者的 email address。如未於授權要求中指定 email scope,則使用者資訊將不包含此項目。

經解碼的 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"
}
# Signature

Signature 用於驗證 response 的有效性。Signature 為 base64url-encoded hash,採用 HMAC SHA-256 演算法,以 base64url-encoded header + "." + payload 為值,並以 channel secret 為金鑰 (key)。為確保 app 的安全性,開發者應隨時驗證 token signature。

# 解碼與驗證 ID Token

為解碼與驗證 ID token,開發者可使用 JWT library,或依下列步驟進行操作

# 使用 JWT Library

開發者可使用公開的 JWT library 解碼與驗證 ID token。以下範例說明如何於 Python® 中使用 library 為 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-encoded 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 attack,請確認 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 後,將其用於呼叫 Social API 以將使用者登出、管理 access token,以及取得用戶資料。如欲取得更多相關訊息,請至: