繁體中文(香港)
  • SDK
  • OIDC

實現一個簡單的用戶端 OIDC SDK

Logto 提供針對不同平台的多種 SDK。除了我們的官方 SDK,我們鼓勵來自社群的開發者創建他們自己的用戶友好的 SDK。本文將指導你逐步構建一個基本的用戶端 OIDC SDK。

Simeng
Simeng
Developer

簡介

Logto 為我們的開發者和業務群體提供一個全面的客戶身份與存取管理 (CIAM) 解決方案。我們提供多種即用型 SDK,適用於不同的平台和應用框架。結合我們的 Logto 雲服務,你可以在幾分鐘內輕鬆建立一個高安全性的用戶授權流程。 作為一家從開發者社群中誕生的公司,Logto 擁抱並重視我們的社群參與。除了那些由 Logto 官方開發的 SDK,我們持續鼓勵並熱烈邀請來自社群的開發者,通過創建更多多樣化且用戶友好的 SDK 來貢獻他們的專業知識,滿足各種平台和框架的獨特需求。 本文將簡要演示如何逐步實現一個符合 OIDC 標準的身份驗證 SDK。

背景

OpenID Connect (OIDC) 流程是一種建立在 OAuth 2.0 框架之上的身份驗證協議,提供身份驗證和單一登陸功能。它允許用戶與應用程序進行身份驗證並獲取對任何私人資源的進一步安全訪問。請參考 OIDC 規範以獲取更多詳細信息。

工作流程

一個標準的授權碼流程包含以下步驟:

認證流程

  1. 用戶啟動登錄請求: 一個匿名用戶從一個公共入口進入你的應用。嘗試獲得身份驗證,也許進一步請求進入第三方應用或服務上的受保護資源。
  2. 用戶身份驗證: 客戶端應用生成身份驗證 URI 並將請求發送到授權伺服器,授權伺服器將用戶重定向到其登錄頁面。用戶通過各種登錄方法與登錄頁進行交互,並被授權伺服器驗證。
  3. 處理登錄回調: 在成功身份驗證後,用戶將被重定向回你的應用,並授予 authorization_code。這個 authorization_code 包含所有與身份驗證狀態和所請求授權數據相關的權限。
  4. 代碼交換: 使用從上述重定向地址中提取的 authorization_code 請求進行代碼交換。作為回報:
    • id_token: 一個包含認證用戶身份信息的數字簽名 JWT。
    • access_token: 一個不透明的 access_token,可用來訪問用戶基本信息端點。
    • refresh_token: 憑證令牌允許用戶持續交換 access_token

授權流程

  1. 訪問用戶信息: 若要訪問更多用戶信息,應用可以利用從初始令牌交換流中獲得的不透明 access_token 向 UserInfo 端點發送附加請求。這使得可以檢索更多與用戶相關的信息,比如用戶的電子郵件地址或個人資料照片。
  2. 授權訪問保護資源: 如果需要,應用可以利用 refresh_token 結合 resourcescope 參數,向令牌交換端點發送額外的請求,從而獲得一個專用的 access_token,允許用戶訪問目標資源。這個過程將會發行一個 JWT 格式的 access_token,其中包含所有訪問保護資源所需的授權信息。

實施

我們將遵循一些設計策略,在我們的 @logto/client JavaScript SDK 中演示如何為自己的用戶端應用程序實現一個簡單的 SDK。請記住,具體的代碼結構可能會因所使用的用戶端框架而異。隨意選擇任何來自 Logto 官方的 SDK 作為你的 SDK 專案的例子。

預覽

構造函數

構造函數應取一個 logtoConfig 作為其輸入。這提供了通過此 SDK 建立身份驗證連接所需的所有配置。

根據你為 SDK 使用的平台或框架,你可能需要將一個持久的本地存儲實例傳遞給構造函數。這個存儲實例將用於存儲所有與授權相關的令牌和密鑰。

初始化用戶身份驗證

在生成身份驗證請求 URL 之前,必須完成幾個準備步驟以確保流程安全。

從授權伺服器獲取 OIDC 配置

定義一個私有方法 `getOidcConfigs`` 用於從授權伺服器的發現端點獲取 OIDC 配置。OIDC 配置響應包含所有元數據信息,客戶端可以使用這些信息與授權伺服器進行交互,包括其端點位置及伺服器能力。(詳情請參閱 OAuth OAuth 授權伺服器元數據規範)。

PKCE 生成器

PKCE(Proof Key for Code Exchange) 驗證流程對於所有公開客戶端授權碼交換流程都至關重要。它減輕了授權碼攔截攻擊的風險。因此,code_challengecode_verifier 是所有公開客戶端應用程式(例如本地應用和 SPA)授權請求所必需的。

具體實現方法可能會因所使用的語言和框架而異。請參閱 code_challengecode_verifier 規範以獲取詳細定義。

生成 state 參數

在授權流程中,state 參數 是一個隨機生成的值,包含在客戶端發送的授權請求中。它充當一種安全措施以防止跨站請求偽造 (CSRF) 攻擊。

存儲中介會話信息

有幾個參數需要在用戶進行身份驗證並重定向回客戶端後保存到存儲中以供驗證。我們將實現一個方法將這些中介參數設置到存儲中。

登錄

我們將把上面實現的所有內容包裝起來,定義一個生成用戶登錄 URL 的方法,並將用戶重定向到授權伺服器進行身份驗證。

處理用戶登錄回調

在上一節中,我們創建了一個生成用戶身份驗證 URL 的登錄方法。這個 URL 包含了啟動客戶端應用身份驗證流程所需的所有參數。該方法將用戶重定向到授權伺服器的登錄頁進行身份驗證。登錄成功後,最終用戶將被重定向回到提供的 redirect_uri。所有必要的參數將攜帶在 redirect_uri 中,以完成後續代碼交換流程。

提取並驗證回調 URL

此驗證步驟對防止任何形式的偽造授權回調攻擊至關重要。回調 URL 必須在進行進一步代碼交換請求之前被仔細驗證。 首先,我們需要從應用存儲中檢索我們之前存儲的 signInSession 數據。

然後在發送代碼交換請求之前驗證回調 URL 的參數。

  • 使用之前存儲的 redirectUri 來驗證 callbackUri 是否與我們發送到授權伺服器相同。
  • 使用之前存儲的 state 來驗證返回的狀態是否與發送到授權伺服器相同。
  • 檢查授權伺服器返回的任何錯誤
  • 檢查返回的 authorization_code 是否存在

發送代碼交換請求

作為用戶身份驗證流程的最後一步,我們將使用返回的 authorization_code 發送令牌交換請求並獲取所需的授權令牌。有關請求參數定義的更多詳細信息,請參閱 令牌交換規範

  • code: 我們從回調 URI 收到的 authorization_code
  • clientId: 應用 ID
  • redirectUri: 在生成用戶登錄 URL 時使用的同一值。
  • codeVerifier: PKCE 代碼校驗器。類似於 redirectUri,授權伺服器將此值與我們之前發送的值進行比較,以確保傳入令牌交換請求的驗證。

處理登錄回調

我們把已經實現的一切包裝起來。讓我們構建 signInCallback 處理方法:

結果,令牌交換請求將返回以下令牌:

  • id_token: OIDC idToken,一個包含認證用戶身份信息的 JSON Web Token (JWT)。id_token 也可以作為用戶身份鑑別狀態的唯一信息來源(SSOT)。
  • access_token: 授權伺服器返回的默認授權碼。可用於調用用戶信息端點並檢索身份驗證的用戶信息。
  • refresh_token:(如果在授權請求中存在 offline_access 範圍):此刷新令牌允許客戶端應用程序在不要求用戶重新驗證的情况下獲取新的訪問令牌,授予資源的長期訪問權。
  • expires_in: 訪問令牌在過期前有效的時間,以秒為單位。

ID 令牌驗證

驗證和提取 id_token 中的聲明是確保令牌真實性和完整性的一個關鍵步驟。這裡是驗證 idToken 所涉及的主要步驟。

  • 簽名驗證:id_token 由授權伺服器使用其私鑰數字簽名。客戶端應用需要使用授權伺服器的公鑰驗證簽名。這確保令牌沒有被篡改,並且確實是由合法的授權伺服器發行的。
  • 發行者驗證**:**檢查 id_token 中的 "iss"(發行者)聲明是否與預期值匹配,指示令牌是由正確的授權伺服器發行的。
  • 受眾驗證:確保 id_token 中的 "aud"(受眾)聲明匹配客戶端應用的客戶 ID,確保令牌是針對客戶端的
  • 過期檢查:驗證 id_token 中的 "iat"(發行時間)聲明是否已經過了當前時間,確保令牌仍然有效。由於網絡傳輸成本,我們需要在驗證接收到的令牌 iat 聲明時設置一個發行時間容忍範圍。

返回的 id_token 是一個標準的 JSON Web Token (JWT)。根據你所使用的框架,你可以找到各種方便的 JWT 令牌驗證插件來協助解碼和驗證令牌。對於此示例,我們將在我們的 JavaScript SDK 中使用 jose 來輔助令牌的驗證和解碼。

獲取用戶信息

在成功用戶身份驗證之後,可以從授權伺服器發行的 OIDC id_token 中檢索基本用戶信息。但由於性能考慮,JWT 令牌的內容受到限制。若要獲得更詳細的用戶資料信息,符合 OIDC 的授權伺服器提供了一個即用型用戶信息端點。此端點允許你請求特定用戶資料範圍以檢索附加的用戶資料數據。

當調用一個未指明特定 API 資源的令牌交換端點時,授權伺服器將默認發行一個不透明類型的 access_token。此 access_token 只能用于訪問用戶信息端點,以獲取基本用戶資料數據。

獲取保護資源授權的訪問令牌

在大多數情况下,客戶端應用不僅需要用戶身份驗證,還需要用戶授權才能訪問某些保護資源或 API 端點。在這裡,我們將使用在登錄時獲得的 refresh_token 來獲取特定被授予的 access_token(s) 來管理特定資源。這使我們能夠獲取對那些受保護 API 的訪問。

總結

我們提供了用戶端應用的用戶身份驗證和授權過程的重要方法實現。你可以根據特定場景,組織和優化 SDK 邏輯。請注意,由於不同平台和框架的存在可能會有變體。

如需更多詳情,請探索 Logto 提供的 SDK 套件。我們鼓勵更多用戶加入開發並與我們討論。在我們持續提升和擴展 SDK 的功能過程中,你的反饋和貢獻對我們非常重要。

附錄

保留範圍

Logto 的保留範圍在初始身份驗證請求期間需要傳遞。這些範圍是 OIDC 保留或 Logto 保留的基本範圍,以完成成功的授權流程。

範圍名稱描述
openid成功身份驗證後代理 id_token 所需。
offline-access代理使你的客戶端應用程序可以交換並在屏幕外刷新 access_token 的 refresh_token 所需。
profile獲取訪問基本用戶信息所需

Logto 配置

屬性名稱類型必需描述默認值
appIdstringtrue唯一的應用 ID。由授權伺服器生成以識別客戶端應用。
appSecretstring應用秘密與應用 ID 一起使用,以驗證請求者的身份。對於機密客戶端,如 Go web 或 Next.js web 應用,這是必需的,對於公開客戶端,如本地或單頁應用(SPAs),則可選。
endpointstringtrue你的授權伺服器根端點。此屬性將廣泛用於生成授權請求端點。
scopesstring list表示用戶可能需要授權以進入任何已保護資源的所有必要範圍。[reservedScopes]
resourcesstring list用戶可能請求進入的所有保護資源指示器

Util 方法

generateRandomString