繁體中文(台灣)
  • 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. 訪問用戶信息: 為獲取更多用戶信息,應用可以向 UserInfo 端點發出附加請求,使用從初始令牌交換流中獲得的不透明 access_token。這可以檢索有關用戶的其他詳細信息,例如他們的電子郵件地址或個人資料照片。
  2. 授予訪問保護資源的權限: 如果需要,應用可以向令牌交換端點發出附加請求,使用 refresh_token 結合 resourcescope 參數,來獲取一個為用戶訪問目標資源提供的專用 access_token。這個過程會簽發一個 JWT 格式的 access_token,包含訪問保護資源所需的所有授權信息。

實現

我們將遵循一些設計策略,在我們的 @logto/client JavaScript SDK 之內,展示如何為你自己的客戶端應用實作一個簡單的 SDK。請謹記,具體的代碼結構可能會根據你正在使用的客戶端框架而有所不同。隨意選擇任何 Logto 官方 SDK 作為你自己 SDK 專案的範例。

預覽

建構函數

構造函數應接受一個 logtoConfig 作為輸入。這提供了所有必要的配置,你將需要通過此 SDK 建立一個認證連接。

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

初始化用戶認證

在生成認證請求 URL 之前,完成若干準備步驟以確保流程安全是必需的。

從授權伺服器獲取 OIDC 配置

定義一個私有方法 `getOidcConfigs``來從授權伺服器的發現端點提取 OIDC 配置。OIDC 配置響應包含客戶端可以用來與授權伺服器進行互動的所有元數據信息,包括其端點位置和伺服器的功能。(更多詳情請參考 OAuth 授權伺服器元數據規範。)

PKCE 生成器

一個 PKCE (Proof Key for Code Exchange) 驗證流對於所有公開的客戶授權碼交換流至關重要。它減少了 authorization_code 攔截攻擊的風險。因此一個 code_challengecode_verifier 是所有公開客戶應用(如原生應用和 SPA)授權請求所必需的。

根據你正在使用的語言和框架,實作的方法可能不同。請參考 code_challengecode_verifier 規範以獲取詳細定義。

生成狀態參數

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

存儲會話過渡信息

有幾個參數需要在用戶認證並被重定向回客戶端後保存在存儲中以供驗證使用。我們將實作一個方法來將這些過渡參數設置到存儲中。

登入

讓我們將以上實現的所有東西封裝起來,定義一個方法生成用戶登入 URL 並將用戶重定向到授權伺服器進行身份驗證。

處理用戶登入回調

在上一節中,我們創建了一個生成用戶認證 URL 的登入方法。此 URL 包含從客戶應用發起認證流程所需的所有參數。該方法將用戶重定向到授權伺服器的登入頁面進行身份驗證。在成功登入後,終端用戶將被重定向回上面提供的 redirect_uri 位置。所有必要的參數將在 redirect_uri 中攜帶,以完成後續的令牌交換流。

提取並驗證回調 URL

這個驗證步驟對防止任何形式的偽造授權回調攻擊極為重要。在向授權伺服器發送進一步的碼交換請求前,必須仔細驗證回調 URL。 首先,我們需要從應用存儲中檢索之前存儲的 signInSession 信息。

然後在發出令牌交換請求前,驗證回調 URL 的參數。

  • 使用先前存儲的 redirectUri 驗證 callbackUri 是否與我們發送的相同。
  • 使用先前存儲的 state 驗證返回的 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 scope 存在於授權請求中):這個刷新令牌允許客戶端應用在不需要用戶重新認證的情況下獲取新的 access token,從而為資源授予更長期的訪問。
  • expires_in:access token 在過期前有效的時間(以秒為單位)。

ID token 驗證

驗證並從 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 兼容的授權伺服器提供了一個開箱即用的用戶信息端點。此端點允許你通過請求特定的用戶資料 scope 獲取額外的用戶資料數據。

當不指明特定 API 資源時,在請求一個令牌交換端點後,授權伺服器通常會發出一個不透明類型的 access_token。此 access_token 只能用於訪問用戶信息端點,從而檢索基本用戶資料數據。

獲取受保護資源授權的 access token

在大多數情況下,客戶應用不僅需要用戶認證,還需要用戶授權以訪問某些受保護的資源或 API 端點。在這裡,我們將使用登入過程中獲得的 refresh_token 來獲取特別授予管理特定資源的 access_token(s)。這使我們能夠獲得訪問那些受保護 API 的權限。

總結

我們提供了客戶端應用用戶認證和授權流程的基本方法實現。根據你的具體情境,你可以相應地組織並優化 SDK 邏輯。請注意,由於不同的平台和框架,可能會有差異存在。

要獲取更多詳細信息,請探索 Logto 提供的 SDK 套件。我們鼓勵更多的用戶加入開發並與我們交流。隨著我們不斷提升和擴展 SDK 的能力,我們非常珍視你的回饋與貢獻。

附錄

保留的 scopes

Logto 保留的 scopes 你將需要在初始授權請求中傳入。這些 scopes 都是 OIDC 保留或 Logto 保留的基本 scopes,以完成成功的授權流程。

Scope 名稱描述
openid成功認證後授予 id_token 的所需項目。
offline-access授予 refresh_token,允許你的客戶應用在螢幕外交換和刷新 access_token 的所需項目。
profile獲得訪問基本用戶信息的所需項目

Logto 配置

屬性名稱類型必需描述預設值
appIdstringtrue獨特的應用標識。由授權伺服器生成以識別客戶端應用。
appSecretstring與應用 ID 一起用於驗證請求者身份的應用秘鑰。它對於機密客戶端應用(如 Go web 或 Next.js web 應用)而言是必需的,對於公共客戶端應用(如原生或單頁應用,SPAs)則是可選的。
endpointstringtrue你的授權伺服器根端點。此屬性將被廣泛用於生成授權請求端點。
scopesstring list表示用戶可能需要授予訪問任何給定保護資源所需的所有資源範圍。[reservedScopes]
resourcesstring list用戶可能請求訪問的所有受保護資源指標

實用方法

generateRandomString