完整指南:將 OIDC 伺服器整合到你的專案中
了解將 OIDC(開放 ID 連接)伺服器整合到專案中的最佳實踐,並了解元件如何在舞台上互動。
你可能會遇到需要一個集中式身份驗證和授權系統的情況,這也稱為身份存取管理(IAM)或身份提供者(IdP)。有時人們會加上一個字來指出業務,例如客戶 IAM 和員工 IAM。
暫時先放下這些華麗的名稱。對 IAM 的需求可能出現是因為你的應用程式正在成長,或者你打算一開始就將繁重的工作委託給供應商。不論如何,你正達到需要將身份系統引入專案的階段。
考慮到 OAuth 2.0 的普及性,開放 ID 連接(OIDC)對於許多開發者來說是個自然選擇。由於 OIDC 是構建在 OAuth 2.0 之上的身份驗證層,當你開始使用 OIDC 時,可能會覺得熟悉。讓我們開始吧!
什麼是 OIDC 伺服器,為什麼要整合 OIDC 伺服器?
OIDC 伺服器或身份提供者是一個集中式系統,用來管理用戶驗證和授權。如我們在 為什麼多應用業務需要集中式身份系統 中所討論的,集中式身份系統有許多優點。
假設你的專案以一個簡單的網頁應用程式開始,且內建了驗證:
隨著專案成長,你需要引入一個行動版:
如果用戶需要為每個應用程式創建帳號,那將是糟糕的體驗。由於你開始時使用網頁應用程式,你讓行動應用程式與網頁應用程式進行驗證溝通:
現在,引入了一個新的 API 服務。由於這是為付費用戶設置的服務,你需要確保用戶已經驗證並授權獲取服務。為此,你可以通過網頁應用程式代理服務:
或者,使用某些令牌技術驗證用戶,並在服務中通過通信網頁應用程式驗證令牌。因此,行動應用程式可以直接使用該服務:
事態變得混亂。因此,你決定將驗證和授權邏輯拆分到一個單獨的服務中:
重構過程可能是痛苦的。你可能會注意到,隨著專案中增加應用程式和服務,複雜性將指數增加。更糟的是,你可能需要維護多種驗證方法,如無密碼登入,社交登入,SAML 等。
這就是為什麼當你有計劃要擴大專案時,最好從一開始就引入一個身份提供者。
整合 OIDC 伺服器的最佳實踐
尋找 OIDC 提供者
市場上有許多 OIDC 提供者。你可以根據需求和偏好選擇一個。只要該提供者符合 OIDC 規範,它將在你的專案中扮演相同角色。
在 OIDC 中,「主體」、「客戶端」和「受眾」的含義是什麼?
為簡化概念,我們可以將主體視為通過客戶端請求訪問受眾的實體。
讓我們看看一些典型情況:
1. 用戶點擊網頁應用程式上的登入按鈕
在傳統的伺服器端渲染的網頁應用程式中,前端和後端是耦合的。假設網頁應用程式提供前端和後端:
- 主體:用戶
- 受眾:OIDC 伺服器
- 客戶端:網頁應用程式
受眾是 OIDC 伺服器可能看起來直覺上是反常的。事實上,這正是為終端用戶實現 SSO(單一登入體驗)的關鍵。讓我們看看 授權碼流 的簡化序列圖:
code
是可以用來交換各種令牌(如訪問令牌、ID 令牌和刷新令牌)的一次性代碼。如果你目前不理解這些令牌,也沒關係。隨著我們繼續,你將會更好地理解它們。
在上述情況中,用戶切換至另一個應用程式時不需要重新登入,因為用戶(主體)已與 OIDC 伺服器(受眾)驗證過。
2. 用戶使用單頁應用程式
在單頁應用程式(或行動應用程式)中,前端和後端是分開的。假設後端是一個 API 服務:
- 主體:用戶
- 受眾:API 服務
- 客戶端:單頁應用程式 (SPA)
授權碼流的簡化序列圖:
由於 API 服務是無互動的,SPA 需要以 API 服務作為受眾(令牌中的 aud
)使用訪問令牌。
為什麼 OIDC 伺服器仍然是受眾?
技術上,你可以從受眾列表中移除 OIDC 伺服器。由於在大多數情況下,你需要從 OIDC 伺服器獲取用戶資訊(這需要 OIDC 伺服器作為受眾),因此在涉及用戶互動時,最好總是在受眾列表中包含 OIDC 伺服器。
等等,你是說我們可以在授權請求中有多個受眾?
沒錯!記住 OIDC 是構建在 OAuth 2.0 之上的,可以在授權請求中利用 RFC 8707:OAuth 2.0 資源指標 指定多個受眾。這需要授權和 OIDC 伺服器的支持。Logto 原生支持這個特性。
3. 機器對機器的通信
假設你有一個服務 A 需要調用服務 B:
- 主體:服務 A
- 受眾:服務 B
- 客戶端:服務 A
具有 客戶端憑證授權 的簡化序列圖:
當服務 B 需要調用服務 A 時,角色僅需互換。
總結
- 主體:可以是用戶、服務或任何需要訪問受眾的實體。
- 客戶端:可以是網頁應用程式、行動應用程式或任何發起請求或代表主體行事的實體。
- 受眾:可以是服務、API 或任何向主體提供訪問的實體。
什麼是訪問令牌、ID 令牌和刷新令牌?
在使用 OIDC 時,你可能會遇到三種類型的令牌:
- 訪問令牌:用於訪問受眾。可以是 JWT(JSON 網頁令牌)或不透明令牌(通常是一個隨機字串)。
- ID 令牌:特定於 OIDC 的令牌,包含用戶資訊。它永遠是 JWT,客戶端可以解碼令牌獲取用戶資訊。
- 刷新令牌:當訪問令牌或 ID 令牌過期時,用於獲取新的令牌集。
關於這些令牌的詳細說明,你可以參考 理解 OIDC 協定中的刷新令牌、訪問令牌和 ID 令牌。
在上述場景 1 和 2 中,術語 授權請求 指的是通過特定 授權 獲取令牌集的請求。
當一切順利時,一組令牌將在 "使用 code
交換令牌" 步驟中返回。令牌集中的可用令牌取決於多個因素,尤其是授權請求中的 scope
參數。為簡化起見,我們假設所有令牌都在集返回。一旦訪問令牌過期,客戶端可以使用刷新令牌獲取新的令牌集,無需用戶互動。
對於場景 3,它更簡單,因為客戶端憑證授權僅返回訪問令牌。
如何在 OIDC 中處理多個受眾?
你可能會注意到一次只返回一個訪問令牌。如何處理客戶端需要訪問多個受眾的情況?
有兩種常見解決方案:
在代碼交換請求中指定 resource
當客戶端交換代碼以獲取令牌時,可以在請求中指定 resource
參數。OIDC 伺服器將針對指定的受眾返回訪問令牌(如果適用)。
這是非規範性範例:
然後如果適用,OIDC 伺服器將針對 API_SERVICE
受眾返回訪問令牌。
使用刷新令牌獲取新的訪問令牌
如採用 RFC 8707,客戶端甚至可以通過多次使用 resource
參數指定多個受眾。現在,如果在客戶端中可用的刷新令牌,客戶端可以在刷新令牌時在 resource
參數中指定受眾。
這是非規範性範例:
它與前一種解決方案效果相同。同時,其他授權的受眾在未來的令牌請求中仍然可用。
客戶端憑證授權
你也可以在客戶端憑證授權中使用 resource
參數指定受眾。在此授權中,多個受眾不會有問題,因為你總是可以通過簡單地再發送一個令牌請求來為不同的受眾請求新的訪問令牌。
保護你的 API 服務
場景 2 中的 "API 服務" 和場景 3 中的 "服務 B" 有一個共同點:它們需要驗證訪问令牌以確定請求是否被授權。根據訪问令牌的格式,驗證過程可能有所不同。
- 不透明令牌:API 服務需要調用 OIDC 伺服器驗證令牌。OIDC 伺服器通常提供一個內省端點 用於此目的。
- JWT:API 服務可以通過檢查令牌的簽名和聲明來本地驗證令牌。OIDC 伺服器通常提供 JSON Web 金鑰集 (JWKS) 端點 (
jwks_uri
) 供 API 服務獲取公開金鑰以驗證簽名。
如果你不熟悉 JWT,可以參考 什麼是 JSON 網頁令牌 (JWT)?。事實上,通常不需要手動驗證簽名和確認聲明,因為有許多庫可以替你完成這個工作,例如用於 Node.js 和網頁瀏覽器的 jose。
斷言聲明
除了驗證 JWT 簽名外,API 服務應始終檢查令牌中的聲明:
iss
: 令牌的發行者。應匹配 OIDC 伺服器的發行者 URL。aud
: 令牌的受眾。應匹配 API 服務的受眾值(通常為有效 URI)。exp
: 令牌的過期時間。如已過期,API 服務應拒絕該令牌。scope
: 令牌的作用域(權限)。API 服務應檢查令牌中是否存在所需作用域。
其他聲明,如 sub
(主體)和 iat
(發行時間),在某些情況下也很重要。如果你有其他安全措施,請相應檢查聲明。
授權
仍有未回答的問題:如何確定可以將 作用域(即權限)授予主體?
單一問題引出了一個嶄新的授權世界,這超出了本文範圍。簡而言之,有一些常見的方法,如 RBAC(基於角色的訪問控制)和 ABAC(基於屬性的訪問控制)。以下是一些起步資源:
結語
將 OIDC 伺服器引入專案是一個重要的步驟。它可以顯著提高專案的安全性和可擴展性。同時,可能需要一些時間去理解概念和元件之間的互動。
選擇適合你需求和偏好的優秀 OIDC 提供者可以顯著減少整合過程的複雜性,因為提供者通常提供整套解決方案,包括 OIDC 伺服器、授權機制、SDK 及你未來可能需要的企業功能。
希望本指南能幫助你理解整合 OIDC 伺服器的基礎。如果你在尋找一個起點,我會自私地推薦 Logto,我們為開發者提供的身份基礎設施。