Try   HackMD

OIDC(OpenID Connection)

tags: OIDC,OAuth

OIDC 是一種基於 OAuth 2.0 的身份認證協議,允許使用者端通過 Authorization Server 驗證使用者身份,並以標準的 ID Token 獲取使用者的基本資訊。

OAuth 只討論如何授權(authorization),並沒有明確定義身分驗證(authentication)機制,而 OIDC 協定補足了這部分缺漏。

身份

UserAgent (ResourceOwner)

資源擁有者,基本上就是使用者

OIDC Client

想要取得 user 認證及授權的 client 端,必須在 Authorization Server 有註冊。

Authorization Server / OpenID Provider

  • 身兼驗證跟授權功能
  • 支援 SSO
  • 提供 OPID Client 註冊申請,至少需要以下資料
Attributes description 範例
client_id OIDC Client ID awesome-service
audience 允許接受 ID Token 的 URI https://awesome.burgess.com
redirect_uri 登入後轉導的頁面 https://awesome.burgess.com/login/callback
backchannel_logout_uri 主動發送登出請求給 client 端的 URI https://awesome.burgess.com/logout/callback
post_logout_redirect_uris 登出後轉導的頁面 https://awesome.burgess.com/logout/backchannel

註冊後 Authorization Server 會發給 client 端一個 client_secret,之後換 ID_TOKEN 會用到

基本的登入 flow

路徑 說明
GET /oauth2/auth 取得 Authorization Code
POST /oauth2/token 取得 ID Token
Created with Raphaël 2.2.0User AgentUser AgentOIDC ClientOIDC ClientAuthorization ServerAuthorization Server登入(1)回傳 state, scope(2)call /oauth2/auth 取得 Authorization Code使用者帳密驗證(3)回傳 302 & redirect_uri 與 (4) 所需參數(4)redirect to redirect_uri(5)驗證 state(6)call /auth2/token 取得 `ID Token`(7) 回傳 ID Token & access token登入成功

登入成功後就可以拿 ID Token 去取得 user info

(1) 回傳 state, scope

state 由 OIDC Client 隨機生成,之後步驟可與 Authorization Server 回傳的 state 比對,目的是為了確認轉導過程中的使用者都是同一個人,防止 CSRF 攻擊。

scope 為這次 OIDC Client 想取得使用者的 scope,依 ORY Hydra Authentication
,登入驗證並取得 ID Token 的 scope 是填 openid

步驟 (2) 其他所需的參數可在這回傳。

(2) call /oauth2/auth 取得 Authorization Code

透過 GET 方法發送至 authorize endpoint

GET 參數

parameters description
client_id OIDC Client ID,要跟 Authorization Server 註冊的一致
nonce 隨機字串,驗證後會存在 ID Token 內
backchannel_logout_uri 主動發送登出請求給服務的 URI
redirect_uri 登入後轉導的頁面,要跟 Authorization Server 註冊的一致
response_type 回應類型,通常固定填 code
scope 登入驗證並取得 ID Token 的 scope 為 openid
state 為避免 CSRF 攻擊的一個隨機字串

(3) (4) 回傳 Authorization code, state 及授權 scope 到 redirect_uri

Authorization Server 會回傳一個 302 轉址到 redirect_uri 的訊息給 UserAgent,轉址內容含有 Authorization code, state and scope 等參數。

redirect_uri 必須是該 Client ID 在 Authorization Server 註冊的 uri

(5)驗證 state

步驟 (1) OIDC Client 有回傳 state,這時可驗證從 Authorization Server 轉導回來的 state 是否跟先前發出的一致。

(6) call /auth2/token 取得 ID Token

透過 POST 方法發送至 token endpoint

Basic access authentication : 通常為註冊的 client_id 與 註冊完 Authorization Server 發的 client_secret 之組合。client_id:client_secret 的 base64 編碼的 credentials,再放到 header 中,以下為 Basic access authentication 內容範例:

Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l

body 參數

parameters description
code 先前拿到的 Authorization Code
grant_type 採用到授權方式,授權碼授權方式基本上填 authorization_code
redirect_uri Authorization Server 的 callback 網址,必須跟步驟 (2) 一樣

(7) 回傳 ID Token & access token

驗證成功後 redirect_uri 會拿到 ID Token & access tokenID Token 帶有使用者資訊,access token 則是使用者授權使用某些服務或取得某些資訊的權限,換句話說,得到授權的 service 可以透過 access token 呼叫授權方的 API,使用某些服務或取得某些資訊。

token 很重要,要避免外洩,一般只會放在後端能存取的地方,應避免放在 cookie 或 LocalStorage 等等前端 user 可以查看的地方。

ID Token 是 JWT 格式,通常 payload 會紀錄使用者 ID、token 有效期限、發行單位、發行時間、OIDC Client ID、Authorization Server Login Session ID 等等資訊。

拿到 ID Token 之後要檢查一下下列資料:

  • JWT header 所標示的加密演算法是否符合 Authorization Server 所提供的演算法,EX: RS256
  • JWT payload 所標示的有效期限(exp)是否有過期
  • JWT payload 所標示的 issuer (iss)是否為 Authorization Server
  • JWT payload 所標示的 cliend_id (aud)是不是 OIDC Client 自己的 client_id

驗證 ID Token 完整性

ID Token 是有可能被竄改的,因此可以用 Authorization Server 提供的 public key 進行完整性的驗證,才可信任 ID Token 所夾帶的資訊。

路徑 說明
GET /.well-known/jwks.json 取得 Authorization Server 的 public key

可以使用開源的 JWT 驗證工具進行驗證

基本的登出 flow

登出有分兩種 flow ,OpenID Connect Front-Channel Logout 1.0OpenID Connect Back-Channel Logout 1.0,可以參考 Hydra user-logout 說明,Front-Channel Logout 主要是 Authorization Server 對 User Agent 發出 302 轉址到 OIDC Client 註冊的 frontchannel_logout_uri ; Back-Channel Logout 主要是 Authorization Server 直接通知 OIDC Client 註冊的 backchannel_logout_uri,而下面內容為 Back-Channel Logout。

路徑 說明
GET /oauth2/sessions/logout 登出
Created with Raphaël 2.2.0User AgentUser AgentOIDC ClientOIDC ClientAuthorization ServerAuthorization Server登出(1) 回傳 state(2) call /oauth2/sessions/logout登出流程  註銷 id_token  清除 session or cookie 等等(3) call OIDC Client backchannel_logout_uri(4) call /oauth2/revoke 註銷 Access token(5) 回傳 302 & post_logout_redirect_uri 與 (6) 所需參數(6) redirect to post_logout_redirect_uriOIDC Client 自身服務的登出作業登出成功

流程圖步驟 (3) 簡化只畫一條回 OIDC Client,其實還有其他的 OIDC Client 會被呼叫,這些 OIDC Clients 都會執行清除各自 ID token 的流程

(1) 回傳 state

state 由 OIDC Client 隨機生成,之後步驟可與 Authorization Server 回傳的 state 比對,目的是為了確認轉導過程中的使用者都是同一個人,並且防止 CSRF 攻擊。

(2) call /oauth2/sessions/logout

透過 GET 方法發送至 Authorization Server logout endpoint

GET 參數

parameters description
id_token 登入過程所拿到的的 ID token
post_logout_redirect_uri 必須跟 Authorization Server 註冊的一致
state 為避免 CSRF 攻擊的一個隨機字串

(3) call OIDC Client logout_uri

Authorization Server 會進行一系列登出流程,並針對所有已登入的 OIDC Clients 發出 POST logout 請求,這些 uri 即為當初各 OIDC Clients 註冊的 backchannel_logout_uri。

Authorization Server 所發出 POST logout 帶有 logout_token,各個 OIDC Cliens 收到後要先驗證 logout_token 要檢查一下下列資料:

  • JWT header 所標示的加密演算法是否符合 Authorization Server 所提供的演算法,EX: RS256
  • JWT payload 所標示的 issuer (iss)是否為 Authorization Server。
  • JWT payload 所標示的 cliend_id (aud)是不是 OIDC Client 自己的 client_id。
  • 根據 JWT payload 所標示的 Authorization Server Login Session ID (sid),找出 OIDC Client 中對應的 ID token ( ID token 的 payload 也會有 sid),該 ID token 就是要被 OIDC Client 清除的。

(4) call /oauth2/revoke 註銷 Access token

透過 POST 方法發送至 revoke endpoint

body 參數:

parameters description
token 要註銷的 Access token

(5)(6) 回傳 state 到 logout_redirect_uri

Authorization Server 會回傳一個 302 轉址到 post_logout_redirect_uri 的訊息給 User Agent,轉址內容含有 state 。

post_logout_redirect_uri 是有在 Authorization Server 註冊的 uri。

OIDC Client 的 post_logout_redirect_uri 執行自身的登出流程,EX: 清 cookie, session 等等,成功後即完成登出手續。

ID token 失效

當 OIDC Client 發現持有的 ID token 或過期,請走登出流程,讓使用者重新再登入取新的 ID token

Broswer 安全性

即使近年來 Cross-Origin Resource Sharing (CORS) 被廣泛採用,不論是 OIDC 登入流程或 OAuth 身份授權,不建議由前端發起,前端取得的 ID tokenaccess token 會被竊取。

最簡單的安全處理方式是由後端 server 發起 OIDC 登入流程或 OAuth 身份授權,把 ID tokenaccess token 儲存在後端(放入 Session or 自行處理儲存方式),登入完成後由後端設定 SameSiteHttpOnly 的 Cookie(Session-ID or User-Info) 及 Signed Cookie。
前端每次發起請求,後端就驗證 Cookie & Signed Cookie,並找到相關 token 資料(可由 Session ID 找到 token 值或是自行設定的 Cookie 對應後端儲存資料內容),再帶著 access token 轉發到 Resource Server 或 Auth Server 要資料。

Cookie SameSite 設定可禁止跨網站發送該 Cookie,防止 CSRF 攻擊。
Cookie HttpOnly 設定可禁止 JavaScript 存取該 cookie,防止 XSS 攻擊。
Cookie 也可設定 Secure 屬性,傳輸時會進行加密。