繁體中文(台灣)
  • 自定義 JWT
  • JWT 聲明
  • 授權
  • 身份驗證
  • OAuth 2.0
  • Logto

使用 Logto 增強 JWT 訪問令牌的自定義聲明以提升授權功能

在本文中,我們將介紹如何使用 Logto 的自定義 JWT 聲明功能,通過一個實例提高授權的靈活性和服務提供者的性能。

Darcy Ye
Darcy Ye
Developer

在之前的文章中,我們提到越來越多的系統正在使用 JWT 格式的訪問令牌進行用戶身份驗證和訪問控制。這其中的一個重要原因是 JWT 可以包含一些有用的信息,比如用戶的角色和權限。這些信息可以幫助我們在伺服器和客戶端之間傳遞用戶身份信息,從而實現用戶身份驗證和訪問控制。

通常,JWT 中包含的信息是由身份驗證伺服器決定的。根據 OAuth 2.0 協議,JWT 通常包含諸如 sub (主體)、aud (受眾)和 exp (到期時間)等字段,這些字段通常被稱為聲明。這些聲明可以幫助驗證訪問令牌的有效性。

然而,使用 JWT 進行驗證的場景不計其數,普通的 JWT 聲明往往無法滿足用戶的需求。人們常常想,既然 JWT 可以包含一些信息,那麼我們是否可以添加一些信息來讓授權更簡單?

答案是肯定的,我們可以向 JWT 添加自定義聲明,比如當前用戶的範圍和訂閱級別。這樣,我們可以在客戶端和伺服器之間(這裡指提供各種不同服務的伺服器,也稱為服務提供者)傳遞用戶身份信息,以實現用戶身份驗證和訪問控制。

對於標準 JWT 聲明,請參考 RFC7519。作為一個支持身份驗證和授權的身份解決方案,Logto 在此基礎上擴展了資源和範圍聲明以支持標準 RBAC。儘管 Logto 的 RBAC 實現是標準的,但它還不夠簡單和靈活,以適應所有使用情境。

基於此,Logto 啟動了一項新的功能,允許用戶自定義額外的 JWT 聲明,使得用戶身份驗證和訪問控制可以更靈活地實現。

Logto 自定義 JWT 聲明如何運作?

你可以通過點擊側邊欄中的 "JWT claims" 按鈕進入自定義 JWT 列表頁。

custom-jwt-listing-page

讓我們從為最終用戶添加自定義聲明開始。

在左側的編輯器中,你可以自定義你的 getCustomJwtClaims 函數。這個方法有三個輸入參數:tokendataenvVariables

  • token 是基於當前最終用戶的憑證和系統配置獲得的原始訪問令牌負載,以及 Logto 中用戶的訪問相關信息。
  • data 是 Logto 中有關用戶的所有信息,包括用戶的所有角色、社交登錄身份、SSO 身份、組織成員身份等。
  • envVariables 是你在 Logto 中為當前最終用戶訪問令牌使用情境配置的環境變數,如所需外部 API 的 API key 等。
details-page-user-data

右側的卡片可以展開,以顯示對應參數的介紹,並且你也可以在此設置當前情境的環境變數。

details-page-user-test

閱讀了所有卡片右側的介紹後,你可以切換到測試模式,編輯測試數據,並使用編輯後的測試數據來檢查你在左側代碼編輯器中編寫的腳本是否符合預期行為。

這是一個序列圖,顯示了當最終用戶向 Logto 發起身份驗證請求並最終獲得 Logto 返回的 JWT 格式訪問令牌時 getCustomJwtClaims 函數的執行過程。

如果未啟用自定義 JWT 功能,圖中的第 3 步將被跳過,第 2 步結束後會直接執行第 4 步。此時,Logto 將假定 getCustomJwtClaims 的返回值為一個空對象,然後繼續進行後續步驟。

使用自定義 JWT 聲明提升授權:一個實際示例

在上一節中,我們介紹了 Logto 自定義 JWT 的運作原理。在這一部分,我們將通過一個實際示例向你展示如何使用 Logto 自定義 JWT 聲明來提高授權的靈活性和服務提供者的性能。

場景設置

John 的團隊開發了一款 AI 助手應用,允許用戶與 AI 機器人對話以獲得各種服務。

AI 機器人服務分為免費服務和付費服務。免費服務包括特價機票推薦,而付費服務包括股票預測。

AI 助手應用使用 Logto 管理所有用戶,這些用戶分為三種類型:免費用戶、預付費用戶和高級用戶。免費用戶只能使用免費服務,預付費用戶可以使用所有服務(按使用量收費),而高級用戶可以使用所有服務(但有速率限制以防止惡意使用)。

此外,AI 助手應用使用 Stripe 管理用戶付款,並有自己的日誌服務來記錄用戶操作日誌。

Logto 配置

我們首先為 AI 助手應用服務創建 API 資源並創建兩個範圍,recommend:flightpredict:stock

ai-assistant-app-resource

然後我們創建了兩個 角色free-userpaid-user,並分配了相應的範圍:

  • recommend:flight 範圍分配給 free-user 角色。
  • recommend:flightpredict:stock 範圍都分配給 paid-user 角色。
free-user-role
paid-user-role

最後,我們創建三個用戶,free-userprepaid-userpremium-user,並分配相應的角色:

  • free-user 角色分配給用戶 free-user
  • paid-user 角色分配給用戶 prepaid-userpremium-user
assign-free-user-role
assign-paid-user-role

如下面的圖所示,為了實現上面所述場景所需的授權信息,我們希望將當前登入用戶的 rolesbalancenumOfCallsToday 信息包含在 JWT 中。在 AI 助手應用中驗證訪問令牌時,這些信息可以用於快速執行權限驗證。

test-custom-jwt-claims

配置 envVariables 後,我們實現了 getCustomJwtClaims 函數,並點擊 "Run test" 按鈕來查看基於當前測試數據的額外 JWT 聲明的結果。

由於我們還沒有為 data.user.roles 配置測試數據,因此結果中顯示的 roles 是一個空數組。

檢查自定義 JWT 功能是否生效

根據上面的 Logto 配置,我們在測試中得到了相應的結果。接下來,我們將使用 Logto 提供的示例應用驗證我們的自定義 JWT 是否有效。在 Logto SDKs 中找到你熟悉的 SDK,並根據文檔和相應的 GitHub repo 部署一個示例應用。

基於我們上面描述的配置,以 React SDK 為例,我們需要在 LogtoConfig 中更新相應的配置:

在模擬 AI 助手應用的示例應用中為用戶 free_user 登錄後,我們可以通過查看 JWT 訪問令牌的負載部分,看到我們添加的 rolesbalancenumOfCallsTodayisPaidUserisPremiumUser 信息。

sample-app-access-token-preview-free

balancenumOfCallsTodayisPaidUserisPremiumUser 的值與先前測試一致,roles 等於 ["free-user"]。這是因為在實際的最終用戶登入過程中,我們將獲得用戶的所有可訪問數據並相應地處理它。

sample-app-access-token-preview-premium

對於高級用戶,我們可以看到 roles["paid-user"]isPaidUserisPremiumUser 都是 true

更新服務提供者的授權邏輯

在前面的步驟中,我們根據業務需求向用戶訪問令牌添加了自定義聲明。接下來,我們可以使用這些聲明快速執行授權驗證。

這裡提供了 Logto 在 API 端驗證 JWT 訪問令牌的邏輯。完整的代碼實現可以在 GitHub repo 中找到:

你可以參考 Logto API 驗證訪問令牌的邏輯,並根據自己的業務邏輯進行自定義。例如,對於這裡描述的 AI 助手應用場景,你可以在 verifyBearerTokenFromRequest 函數中增加對自定義聲明的驗證邏輯,例如 rolesbalancenumOfCallsTodayisPaidUserisPremiumUser

上述示例是影響最終用戶登錄和獲取 JWT 訪問令牌的場景。如果你的使用案例是機器對機器(M2M),你也可以為 M2M 應用單獨配置自定義 JWT 聲明。

用戶的自定義 JWT 配置不會影響 M2M 應用獲取訪問令牌的結果,反之亦然。

由於 M2M 連接的一般性,Logto 目前不提供 M2M 應用的 getCustomJwtClaims 方法以接受來自 Logto 的內部數據。在其他方面,M2M 應用的自定義 JWT 配置方法和用戶應用相同。本文將不再加以闡述。你可以使用 Logto 的自定義 JWT 功能開始入手。

為什麼使用自定義 JWT 聲明?

我們已經提供了 John 的 AI 助手應用場景以及如何使用 Logto 的自定義 JWT 功能來實現更靈活的授權驗證。在此過程中,我們可以看到自定義 JWT 功能的優勢:

  1. 沒有自定義 JWT 功能,用戶每次檢查權限時都需要請求外部 API(例如你在 getCustomJwtClaims 中做的事)。對於提供此 API 的服務提供者,這可能會增加額外的負擔。使用自定義 JWT 功能,這些信息可以直接放入 JWT,減少對外部 API 的頻繁調用。
  2. 對於服務提供者來說,自定義 JWT 功能可以幫助他們更快地驗證用戶權限,特別是在客戶端頻繁調用服務提供者時,提升服務性能。
  3. 自定義 JWT 功能可以幫助你快速實現業務所需的額外授權信息,並且由於 JWT 是自包含的且可以被加密,因此信息可以在客戶端和服務提供者之間以安全的方式傳遞,使得很難被偽造。

同時,由於每當用戶需要 Logto 簽發訪問令牌時 getCustomJwtClaims 都會被執行,有必要避免執行過於複雜的邏輯和具有高帶寬需求的外部 API 請求。否則,最終用戶在登錄過程中可能需要等待 getCustomJwtClaims 結果過長時間。如果你的 getCustomJwtClaims 返回一個空對象,我們強烈建議你暫時刪除此配置項,直到真的需要使用它。

結論

在本文中,Logto 擴展了基本 JWT 訪問令牌並擴展了額外 JWT 聲明的功能,允許用戶根据自己的業務需求將額外的最終用戶信息放入 JWT 訪問令牌,以便在用戶登入后快速驗證用戶的權限。

我們提供了 John 的 AI 助手應用場景,並演示了如何使用 Logto 的自定義 JWT 功能實現更靈活的授權驗證。我們還指出了使用自定義 JWT 的一些關鍵點。結合實際業務場景,用戶可以根據業務需求將各種與用戶相關的信息置於 JWT 訪問令牌中,以便服務提供者能夠快速驗證用戶的權限。