完整指南:如何將 OIDC 伺服器整合到你的項目中
了解將 OIDC(OpenID Connect)伺服器整合到你的項目的最佳實踐,並了解各個組件如何在舞台上相互作用。
你可能會遇到需要一個集中身份驗證和授權系統的情況,又稱身份存取管理(IAM)或身份提供者(IdP)。有時候,人們會在名字後面加上一個詞來指定業務,例如客戶 IAM 和員工 IAM。
暫時放下這些華麗的名字。需要 IAM 的原因可能是因為你的應用程序正在增長,或者你計劃一開始就將艱苦的工作委託給供應商。不過,你正達到一個需要在你的項目中引入身份系統的階段。
考慮到 OAuth 2.0 的流行,OpenID Connect(OIDC)對許多開發者來說是一個自然的選擇。由於 OIDC 是建立在 OAuth 2.0 之上的身份驗證層,當你開始使用 OIDC 時,你可能會感到熟悉。現在開始吧!
什麼是 OIDC 伺服器,我為什麼應該整合 OIDC 伺服器?
OIDC 伺服器,或身份提供者,是一個集中式系統,用於管理用戶身份驗證和授權。如我們在為什麼需要集中式身份系統來管理多應用業務中所討論的,集中式身份系統有很多好處。
假設你的項目以一個簡單的 Web 應用程序開始,而它內建了身份驗證:
隨著項目增長,你需要引入移動版:
如果用戶需要為每個應用程序創建一個帳戶,這將是糟糕的用戶體驗。在你從 Web 應用程序開始時,你讓移動應用程序與 Web 應用程序通信以進行身份驗證:
現在,一個新的 API 服務被引入。因為它是為付費用戶提供的服務,你需要確保用戶身份已驗證並被授權訪問該服務。為實現這一點,你可以通過 Web 應用程序代理該服務:
或者,使用一些 token 技術來驗證用戶身份,並通過與服務中的 Web 應用程序通信來驗證 token。因此,移動應用程序可以直接使用該服務:
事情變得亂七八糟。所以你決定將身份驗證和授權邏輯分離為一個獨立的服務:
重構過程可能是痛苦的。你可能注意到,隨著向項目添加更多應用程序和服務,其複雜性將成倍增加。更糟的是,你可能需要維護多種身份驗證方法,例如無密碼登錄、社交登錄、SAML 等。
這就是為什麼在你計劃擴展項目時,我們最好在開始時就引入身份提供者。
整合 OIDC 伺服器的最佳實踐
尋找 OIDC 提供者
市面上有很多 OIDC 提供者。你可以根據自己的需求和偏好選擇一個。只要提供者符合 OIDC 規範,它在你的項目中就能發揮相同的作用。
“subject”、“client”和“audience”在 OIDC 中是什麼意思?
為了簡化這個概念,我們可以將subject想像成是通過client來請求訪問audience的實體。
讓我們來看看一些典型的場景:
1. 用戶點擊 Web 應用程序上的登錄按鈕
在傳統和服務端渲染的 Web 應用程序中,前端和後端是耦合的。假設 Web 應用程序同時提供前端和後端:
- Subject:用戶
- Audience:OIDC 伺服器
- Client:Web 應用程序
可能看起來不可逆地是 audience 是 OIDC 伺服器。事實上,這是為最終用戶實現 SSO(單點登入)體驗的關鍵。讓我們看看一個簡化的 授權碼流程 時序圖:
code
是一個一次性代碼,可以用來交換多種 token,如存取 token、ID token 和刷新 token。如果你現在不太理解這些 token,也沒關係。隨著我們的進步,你會對它們有更好的理解。
在上述情況下,當用戶切換到另一個應用程序時,無需重新登錄,因為用戶(subject)已在 OIDC 伺服器(audience)上進行身份驗證。
2. 用戶使用單頁應用程序
在單頁應用程序(或移動應用程序)中,前端和後端是分離的。假設後端是 API 服務:
- Subject:用戶
- Audience:API 服務
- Client:單頁應用程序(SPA)
一個授權碼流程的簡化時序圖:
由於 API 服務是非交互式的,SPA 需要使用 access token 以 API 服務作為 audience(token 中的 aud
)。
為什麼 OIDC 伺服器仍然是 audience?
從技術上講,你可以從 audience 列表中刪除 OIDC 伺服器。由於在大多數情況下,你需要從 OIDC 伺服器獲取用戶信息(這需要 OIDC 伺服器是 audience),因此,當涉及用戶交互時,最好始終將 OIDC 伺服器包含在 audience 列表中。
等等,你是說我們可以在授權請求中擁有多個 audiences?
正是如此!記住,OIDC 是建立在 OAuth 2.0 之上的,可以利用 RFC 8707:用於 OAuth 2.0 的資源指示符 在授權請求中指定多個 audiences。這需要授權和 OIDC 伺服器的支持。Logto 原生支持此功能。
3. 機器對機器的通信
假設你有一個服務 A 需要調用服務 B:
- Subject:服務 A
- Audience:服務 B
- Client:服務 A
一個 客戶端憑證授權 的簡化時序圖:
當服務 B 需要調用服務 A 時,角色就是簡單互換。
回顧
- Subject:它可以是用戶、服務或任何需要訪問 audience 的實體。
- Client:可以是 Web 應用程序、移動應用程序或任何發起請求或代表 subject 行動的實體。
- Audience:可以是服務、API 或任何提供 subject 訪問的實體。
什麼是 access token、ID token 和 refresh token?
在使用 OIDC 時,你可能會遇到三種類型的 token:
- Access token:用於訪問 audience。它可以是 JWT(JSON Web Token)或不透明的 token(通常是一個隨機字符串)。
- ID token:OIDC 特定的 token,包含用戶信息。它始終是 JWT。客戶端可以解碼 token 以獲取用戶信息。
- Refresh token:當 access token 或 ID token 過期時,用於獲取新的一組 token。
有關這些 token 的詳細解釋,你可以參考理解 OIDC 協議中的刷新 token、access token 和 ID token。
在上述場景 1 和 2 中,術語 authorization request 指的是通過特定 授權 獲取一組 token 的請求。
當一切順利時,在「使用 code 交換 token」步驟中將返回一組 token。可用的 token 集取決於多個因素,尤其是授權請求中的 scope
參數。為簡單起見,我們假設所有 token 都會在集裡返回。一旦 access token 過期,客戶端可以使用 refresh token 獲取新的 token 集,而無需用戶交互。
對於場景 3,會更簡單,因為客戶端憑證授權僅返回 access token。
如何在 OIDC 中處理多個 audiences?
你可能注意到每次只有一個 access token 被返回。那客戶端需要訪問多個 audiences 的時候我們該怎麼處理?
有兩種常見解決方案:
在代碼交換請求中指定 resource
當客戶端將代碼交換為 token 時,它可以在請求中指定 resource
參數。如果適用,OIDC 伺服器將為指定的 audience 返回一個 access token。
這是一個非規範的示例:
然後,OIDC 伺服器將為 API_SERVICE
audience 返回一個 access token(如果適用)。
使用 refresh token 獲取新的 access token
有了 RFC 8707,客戶端甚至可以通過多次使用 resource
參數來指定多個 audiences。現在,如果客戶端中有可用的 refresh token,客戶端可以在刷新 token 時指定 audience 的 resource
參數。
這是一個非規範的示例:
它與先前的解決方案具有相同的效果。同時,在未來的 token 請求中,其他授權的 audiences 仍然是可用的。
客戶端憑證授權
你還可以在客戶端憑證授權中使用 resource
參數來指定 audience。在這個授權中,沒有多個 audiences 的問題,因為你可以通過簡單發送另一個 token 請求來為不同的 audience 請求一個新的 access token。
保護你的 API 服務
在場景 2 中的「API 服務」和場景 3 中的「服務 B」有一個共同特點:它們需要驗證 access token 以確定請求是否被授權。根據 access token 的格式,不同的驗證過程可能有所不同。
- 不透明的 token:API 服務需要調用 OIDC 伺服器來驗證 token。OIDC 伺服器通常會為此目的提供一個說明端點。
- JWT:API 服務可以通過檢查 token 中的簽名和聲明來本地驗證 token。OIDC 伺服器通常會提供一個JSON Web Key Set (JWKS) 端點(
jwks_uri
),供 API 服務獲取公鑰以驗證簽名。
如果你是 JWT 的新手,你可以參考什麼是 JSON Web Token (JWT)?。事實上,通常不需要手動驗證簽名和聲明,因為有很多庫可以為你完成這些工作,例如 jose 用於 Node.js 和 Web 瀏覽器。
斷言宣稱
除了驗證 JWT 簽名外,API 服務應始終檢查 token 中的聲明:
iss
:token 的發行者。應該與 OIDC 伺服器的發行者 URL 相符。aud
:token 的 audience。應該與 API 服務的 audience 值相符(通常是有效的 URI)。exp
:token 的過期時間。如果 token 過期,API 服務應拒絕該 token。scope
:token 的作用域(權限)。API 服務應檢查 token 中是否存在所需的作用域。
其他聲明,例如 sub
(subject)和 iat
(簽發時間),在某些情況下也很重要。如果你有額外的安全措施,應根據聲明進行檢查。
授權
一個未回答的問題遺留在那裡:我們如何確定是否可以將某個 scope(即許可)授予 subject?
單個問題導致了一個全新的授權世界,這超出了本文的範疇。簡而言之,有一些常見的方法,如 RBAC(基於角色的訪問控制)和 ABAC(基於屬性的訪問控制)。以下是一些資源可供你入門:
結語
將 OIDC 伺服器引入到你的項目中是一個重大步驟。它可以顯著提高項目的安全性和可擴展性。同時,理解各個概念和組件之間的交互可能需要一些時間。
選擇一個符合你需求和偏好的 OIDC 提供者可以顯著降低整合過程的複雜性,因為提供者通常會提供整套的包,包括 OIDC 伺服器、授權機制、SDK,以及將來可能需要的企業功能。
希望這本指南可以幫助你理解整合 OIDC 伺服器的基本知識。如果你正在尋找一個開始的 OIDC 伺服器,我會自私地推薦Logto,我們的開發者身份基礎架構。