繁體中文(香港)
  • oauth
  • jwt
  • api
  • security
  • authentication
  • authorization
  • machine-to-machine

為機器間通訊保護你的 API 資源

學習如何利用 OAuth 2.0 和 JWT 來保障你的 API 資源,用於機器間通訊。

Gao
Gao
Founder

當建立一個涉及多個服務的項目時,API 資源的安全性是一個關鍵問題。在這篇文章中,我將向你展示如何利用 OAuth 2.0 和 JWT 來保障服務之間的通訊(機器對機器),以及如何應用基於角色的存取控制(RBAC)來遵循最低權限原則。

開始

為了跟隨本文,我假設你已具備以下先決條件:

  • 一個 Logto Cloud 帳戶,或一個自託管的 Logto 實例
  • 至少兩個需要彼此通訊的服務

為了演示目的,讓我們假設我們有以下服務:

  • 一個購物車服務,提供用於管理購物車的 API
    • Endpoint: https://cart.example.com/api
  • 一個支付服務,提供用於處理支付的 API
    • Endpoint: https://payment.example.com/api

認證流程

現在,我們的購物車服務需要調用支付服務以處理支付。認證流程如下:

以上圖中的一些關鍵概念:

  • JWT (RFC 7519): JSON Web Token。請參閱我們的 上一篇文章 簡介 JWT。
  • JWK (RFC 7517): JSON Web Key 用於驗證 JWT 的簽名。JWK 集是一組 JWK。
  • "client_credentials" 授權 (RFC 6749): OAuth 2.0 中的一種授權類型。它使用客戶端的憑證以獲取訪問權杖。我們將在接下來的章節中演示細節。

在上述圖中,每個參與者都在認證流程中扮演一個角色:

  • Cart service: 需要調用支付服務的客戶端。雖然它是一個服務,在 OAuth 2.0 的上下文中仍然是一個客戶端,我們在 Logto 中稱這樣的客戶端為 "machine-to-machine applications"。
  • Logto: 發放訪問權杖的 OAuth 2.0 授權伺服器。
  • Payment service: 提供用於處理支付的 API 資源。

讓我們一步一步地了解認證流程。

初步設置

要執行認證流程,我們需要在 Logto 中創建一個機器對機器應用(購物車服務)和一個 API 資源(支付服務)。

創建 API 資源

由於我們的購物車服務在進行認證時需要了解支付服務的 API,因此我們需要先創建一個 API 資源。 進入 Logto 控制台,在左側邊欄點擊 API resources,然後點擊 Create API resource。在打開的對話框中,我們提供了一些教程以幫助你入門。你也可以點擊 Continue without tutorial 跳過它。

輸入 API 名稱和標識符,例如 Payment servicehttps://payment.example.com/api,然後點擊 Create API resource

創建 API 資源後,你將被重定向到詳細信息頁面。現在我們可以先不做改動。

創建機器對機器應用

在左側邊欄點擊 Applications,然後點擊 Create application。在打開的對話框中,找到 Machine-to-machine 卡,然後點擊 Start building

輸入應用名稱,例如 Cart service,然後點擊 Create application。一個交互式的指南將會顯示來幫助你設置應用。你可以跟隨指南來了解基本用法,或者點擊 Finish and done 來跳過它。

請求訪問權杖

由於假設機器對機器應用程序是安全的(例如,它們在私有網絡中部署),我們可以使用 OAuth 2.0 "client_credentials" 授權來獲取訪問權杖。它使用 基本認證 來驗證客戶端:

  • 請求 URL 是你的 Logto 實例的 token 結點。你可以在機器對機器應用程序詳情頁的 Advanced settings 標籤中找到並複製它。
  • 請求方法是 POST
  • 請求 Content-Type 標頭是 application/x-www-form-urlencoded
  • 對於 Authorization 標頭,值是 Basic <base64(app_id:app_secret)>,其中 app_idapp_secret 是機器對機器應用的應用 ID 和應用密鑰。你可以在應用詳情頁找到它們。
  • 請求正文需要指定授權類型和 API 標識符。例如:grant_type=client_credentials&resource=https://payment.example.com/api
    • grant_type=client_credentials: "client_credentials" 授權的常量值。
    • resource=https://payment.example.com/api: 客戶端希望訪問的 API 資源的 API 標識符。
    • 如果應用需要授權範圍(許可權),你也可以在請求正文中指定範圍。例如:scope=read:payment write:payment。我們稍後會介紹範圍。

以下是使用 curl 的請求示例:

成功的響應正文可能如下:

帶授權標頭發送請求

現在我們有了訪问權杖,我們可以將其附加到 API 資源請求的 Authorization 標頭中。例如,如果我們想調用支付服務的 POST /payments API,我們可以發送以下請求:

驗證 JWT

你可能注意到支付服務需要使用 JWK 集驗證 JWT,並可能有一個本地 JWK 集緩存以避免每次都從 Logto 獲取 JWK 集。幸運的是,由於 JWT 的普及,有許多庫可以幫助你用幾行代碼達成目標。

這些庫通常稱為 "jose"(JavaScript Object Signing and Encryption)或 "jsonwebtoken"。例如,在 Node.js 中我們可以使用 jose 來驗證 JWT:

如果驗證成功,payload 變量將是解碼的 JWT 載荷。否則,將拋出錯誤。

應用基於角色的存取控制

現在我們已經成功的保護了購物車服務和支付服務之間的通訊。然而,認證流程只能確保客戶端是真正的購物車服務,卻不能確保購物車服務有權執行支付服務上的操作。

假設我們想允許購物車服務創建支付,但不允許讀取支付。

定義許可權

在 Logto 中,"範圍" 和 "許可權" 是可以互換的。前往支付服務的 API 資源詳情頁,並導航到 Permissions 標籤。現在應該是空的。點擊 Create permission,輸入 read:payment 為許可權名稱,並輸入 Read payments 作為許可權描述。然後點擊 Create permission

重複上述步驟以創建另一個名為 write:payment 描述為 Create payments 的許可權。

創建機器對機器角色

角色是一組許可權。在 Logto 中,機器對機器應用可以分配角色以授予許可權。在左側邊欄點擊 "Roles",然後點擊 Create role

  1. 輸入 checkout 作為角色名稱,並輸入 Checkout service 為角色描述。
  2. 點擊 Show more options。選擇 "Machine-to-machine app role" 作為角色類型。
  3. 在 "Assigned permissions" 部分中,點擊 API 資源名稱(支付服務)左邊的箭頭圖標,然後選擇 write:payment 許可權。
    Create role
  4. 點擊 Create role
  5. 由於我們已經有一個機器對機器應用(購物車服務),我們可以直接在下一步中將角色分配給它。選擇應用名稱左側的複選框瀏覽(購物車服務),然後點擊 Assign applications
Assign role to application

帶範圍請求訪問權杖

除我們在 請求訪問權杖 中提到的請求正文參數之外,我們也可以在請求正文中指定範圍。例如,如果我們想請求 write:payment 許可權,我們可以發送以下請求:

要請求多個範圍,你可以用空格分隔。例如:scope=write:payment read:payment

驗證範圍

如果在支付服務中一個操作需要 write:payment 許可權,我們可以通過確認 JWT 載荷中的 scope 聲明來驗證範圍:

結論

如果你想保護購物車服務的訪問,你也可以應用相同的認證流程。這次,購物車服務是 API 資源,而客戶端是另一個需要訪問的服務。

通過 Logto,你的 API 資源被 OAuth 2.0 和 JWT 保護,並且你可以通過應用基於角色的存取控制來遵循最低權限原則。此外,你還可以利用 Logto 管理你的用戶及其許可權,甚至可以與第三方身份提供者整合。