iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Software Development

從登入到授權-現代軟體的身分架構指南系列 第 11

Day 11|OIDC:在 OAuth 2.0 之上蓋出真正的身份驗證

  • 分享至 

  • xImage
  •  

前言

上一篇把 PKCE 這個 Authorization Code Grant 的標準配備講完了,但到目前為止,這系列談的都還是 OAuth 2.0 的「授權(Authorization)」——讓 Client 拿到存取某個資源的權限。一個很現實的問題是:Client 真的知道操作它的這個人是誰嗎?這篇要接上的 OpenID Connect(OIDC),就是為了在 OAuth 2.0 之上,補上「身份驗證(Authentication)」這一塊。

今天內容涵蓋:

  1. OIDC 要解決的問題:OAuth 2.0 為什麼不夠
  2. OIDC 的核心組成
  3. OIDC 跟 OAuth 2.0 的角色與名詞對照
  4. OIDC 認證流程
  5. ID Token 的內容與驗證
  6. UserInfo Endpoint 是什麼
  7. OIDC Discovery:自動找到端點設定
  8. OIDC 專屬的三種流程

一、OIDC 要解決的問題:OAuth 2.0 為什麼不夠

OAuth 2.0 專注在「授權」:讓 Client 在取得使用者同意後,可以拿著 Access Token 存取受保護的資源。也就是說,Access Token 解決的是「這個 Client 能不能存取某個 API」的問題;但它本身並不是設計來告訴 Client「目前操作的人是誰」。

這個差異在第三方登入情境中特別明顯。假設同一個 Client 同時支援 Google 與 GitHub 登入,Client 可以分別向 Google 和 GitHub 請求授權,取得 Access Token 後,再拿 Token 去呼叫各平台提供的使用者資料 API。問題是:OAuth 2.0 只規範授權流程,並沒有統一規範「使用者身份資料」應該用什麼欄位、什麼格式回傳。

https://ithelp.ithome.com.tw/upload/images/20260914/20181928HvpK9iSvCL.png

以上圖來看,Client 做的事情其實很像:都是向不同 Provider 取得 Access Token,再用 Access Token 請求使用者資料。但各平台回傳的資料格式可能完全不同。以圖中的範例來說,Google 可能用 sub 表示使用者唯一識別碼、用 picture 表示頭像;GitHub 則可能用 id 表示使用者識別碼、用 avatar_url 表示頭像。

因此,在沒有 OIDC 的情況下,Client 如果要同時串接多個身份來源,就必須自行撰寫對應與轉換邏輯,把不同 Provider 回傳的資料整理成自己系統內部統一的使用者模型。例如把 Google 的 sub 和 GitHub 的 id 都轉成內部的 user_id,把 Google 的 picture 和 GitHub 的 avatar_url 都轉成內部的 avatar

更重要的是,這樣做仍然沒有真正標準化「身份驗證」這件事。Client 拿到 Access Token,只能知道某個授權流程完成了,並且這個 Token 可以用來存取某些資源;但如果要正式判斷「目前登入的使用者是誰」,就需要一個標準化、可驗證、能明確表達使用者身份的機制。

⚠️常見的誤用是把 OAuth 2.0 直接當成登入機制。Access Token 的用途是讓 Client 存取受保護資源,而不是讓 Client 判斷使用者身份。OAuth 2.0 回答的是「能不能存取資料」,不是「誰正在使用系統」。

OpenID Connect(OIDC)就是為了解決這個問題而出現。它建立在 OAuth 2.0 之上,補上標準化的身份驗證層,讓 Client 可以透過 ID Token 與標準化的 Claims,確認目前登入的使用者是誰。


二、OIDC 的核心組成

OIDC 可以簡單理解成一個公式:

OIDC = OAuth 2.0 + ID Token + UserInfo Endpoint

它在 OAuth 2.0 的基礎上,最核心是補上兩個東西:

  1. ID Token:一個標準格式的 JWT,內含使用者的身份資訊(Claims),例如使用者的唯一識別碼、驗證時間等。
  2. UserInfo Endpoint:一個受保護的 API,Client 可以用 Access Token 向它換取更詳細的使用者資料。

此外,OIDC 也在 OAuth 2.0 既有的 Scope 機制上,定義了一組與身份資訊相關的標準 Scope,例如 openidprofileemailaddressphone。其中 openid 是 OIDC 認證請求的必要 Scope,用來明確表示這不是單純的 OAuth 2.0 授權請求,而是希望 Authorization Server 依照 OIDC 的規則處理身份驗證,並回傳與使用者身份相關的結果。其他 Scope 則用來描述 Client 希望取得哪些類型的使用者身份資訊。

💡一個常見的比喻:OAuth 2.0 像飯店的房卡——房卡能開門,但卡片本身不記名,門鎖只在乎這張卡有沒有權限。OIDC 則像是飯店在給房卡的同時,額外核發一張身分證(ID Token)——上面明確寫著你的名字,還由飯店(Authorization Server/OpenID Provider)掛保證。


三、OIDC 跟 OAuth 2.0 的角色與名詞對照

OIDC 是建立在 OAuth 2.0 之上,許多角色與名詞都延續自 OAuth 2.0;不過因為 OIDC 多了「認證(Authentication)」這一層,所以有些名稱在 OIDC 情境下會換成更貼近身份驗證的說法:

OAuth 2.0 OpenID Connect(OIDC)
資源擁有者(Resource Owner) 終端使用者(End User)
客戶端(Client) 依賴方(Relying Party, RP)
授權伺服器(Authorization Server) OpenID 提供者(OpenID Provider, OP)
授權請求(Authorization Request) 認證請求(Authentication Request)
Scope OIDC 標準 Scope,例如 openidprofileemail
Access Token Access Token + ID Token

其中最關鍵的是 OpenID 提供者(OP):它不只是 OAuth 2.0 的 Authorization Server,也會把「使用者的身份資訊」當作受保護資源來處理,所以回應中除了 Access Token,還多了 ID Token。


四、OIDC 認證流程

一個較完整的 OIDC 登入流程,通常會把三件事串在一起看:使用者登入、Relying Party 驗證 ID Token,以及後端建立自己的 Session。

https://ithelp.ithome.com.tw/upload/images/20260914/20181928rbs9H4dIb0.png

以上圖為例,可以直接依照圖上的編號閱讀:

  1. 使用者造訪應用程式:使用者開啟網站或 App,並點擊登入。
  2. 發出 Authentication Request:Relying Party / App 將使用者重新導向到 OpenID Provider 的驗證頁面,開始 OIDC 認證流程。
  3. 顯示登入頁面:OpenID Provider 向使用者顯示登入頁面,要求使用者完成身份驗證。
  4. 使用者登入並同意授權:使用者輸入帳密或完成其他驗證方式,並同意 App 需要的授權範圍。
  5. 回傳 Authorization Code:OpenID Provider 將使用者重新導回 App,並在回應中附上 Authorization Code。
  6. 以 Authorization Code 交換 Token:App 使用 Authorization Code 向 OpenID Provider 的 Token Endpoint 交換 ID Token 與 Access Token。
  7. 回傳 ID Token 與 Access Token:OpenID Provider 驗證 Authorization Code 後,將 ID Token 與 Access Token 回傳給 App。
  8. 送出 ID Token,建立登入請求:App 將 ID Token 送到自己的 Backend,要求後端建立登入狀態。
  9. 後端驗證 ID Token 並建立 Session:在這張前後端分離的流程圖中,主要的 ID Token 驗證放在第 9 步,由 Backend 完成。Backend 會驗證 ID Token 的簽章、發行者、受眾與有效期限;確認無誤後,才建立應用程式自己的 Session。若 Relying Party 本身就是後端 Web App,則可能在收到 ID Token 後直接完成驗證與 Session 建立,不一定會另外出現「App 送到 Backend」這一步。
  10. 呼叫 UserInfo Endpoint(可選):如果需要更多使用者資訊,Backend 可以使用 Access Token 呼叫 OpenID Provider 提供的 UserInfo Endpoint。
  11. 回傳 Claims:UserInfo Endpoint 驗證 Access Token 後,回傳使用者資訊,也就是相關 Claims。
  12. 登入完成:Backend 將登入結果回傳給 App,App 再向使用者顯示登入後的畫面或使用者資料。

這裡先抓住一個重點:**ID Token 用來確認使用者身份;Access Token 用來呼叫受保護 API。**在這張圖的流程中,Backend 驗證 ID Token 後才建立應用程式自己的 Session;Access Token 則會在需要更多使用者資訊時,用來呼叫 UserInfo Endpoint。

💡如果 Relying Party 是第三方應用,OpenID Provider 通常會在登入後顯示同意畫面,讓使用者確認是否允許該應用取得指定範圍的資料。這個同意步驟屬於 OAuth 2.0 原本的授權邏輯;OIDC 補上的 ID Token,則負責把使用者身份用標準格式交給 Relying Party 驗證。


五、ID Token 的內容與驗證

ID Token 是一個 JWT,因此也可以拆成 Header、Payload、Signature 三個部分。這裡先看中間的 Payload,也就是 Token 裡實際承載的資料內容;在 ID Token 裡,Payload 主要放的是使用者身份相關的 Claims:

{
  "iss": "https://server.example.com",
  "sub": "24400320",
  "aud": "s6BhdRkqt3",
  "exp": 1311281970,
  "iat": 1311280970,
  "auth_time": 1311280969,
  "nonce": "n-0S6_Wox69"
}
Claim 說明
iss 發行者(Issuer),也就是 OP 的識別碼
sub 使用者的唯一識別碼(Subject),不會隨 email 等資訊變動
aud 受眾(Audience),應該要等於發出請求的 Client ID
exp / iat 過期時間與簽發時間
nonce Client 發起請求時附上的隨機值,原封不動地被放回 ID Token,用來防止重放攻擊

Relying Party 收到 ID Token 後,不能只因為「有拿到 Token」就直接判定使用者登入成功,還必須先確認這個 ID Token 是有效且可信任的:

  • 簽章:用 OP 的公開簽署金鑰驗證 Token 沒有被竄改。
  • iss:確認發行者是不是預期中的 OP。
  • aud:確認這個 Token 是發給自己(而不是別的 Client)。
  • exp:確認 Token 還沒過期。
  • nonce:比對是否跟自己當初發送的值一致,避免這是一個被攔截後重複使用的舊 Token。

⚠️常見的錯誤是將 Access Token 視為使用者身份的證明。Access Token 的主要用途是讓 Resource Server 判斷請求是否具備存取特定資源的權限;它不一定包含完整、穩定且可供 Client 判斷使用者身份的資訊。因此,Client 若要確認目前登入的使用者身份,應以經過驗證的 ID Token 為依據,而不是直接依賴 Access Token。


六、UserInfo Endpoint 是什麼

UserInfo Endpoint 可以理解成 OpenID Provider 提供的標準查詢端點。當 Relying Party 需要取得目前登入使用者的補充資料,例如姓名、Email、生日或地址,就可以帶著 Access Token 呼叫這個端點。也就是說,ID Token 主要用來確認「使用者是誰」;UserInfo Endpoint 則用來補充取得「這個使用者有哪些資料」。

請求方式如下:

GET /userinfo HTTP/1.1
Host: openid-provider.example.com
Authorization: Bearer some-access-token

OP 驗證 Access Token 有效後,會回傳使用者資料:

{
  "sub": "user123",
  "name": "Gloria",
  "email": "gloria@foo.com",
  "birthdate": "2001-05-25",
  "address": {
    "street": "信義路五段 7 號",
    "city": "Taipei",
    "country": "TW"
  }
}

UserInfo Endpoint 回傳的欄位,會依照 Relying Party 在認證請求中要求的 Scope,以及使用者實際同意的範圍而有所不同。因此,Relying Party 不應一次要求過多資料,而是應遵守最小權限原則,只請求真正需要的使用者資訊。

一般情況下,UserInfo Endpoint 會回傳 JSON 格式的使用者資料;有些 OpenID Provider 為了提高資料完整性或保密性,也可能把回應包成 JWT。這種做法可以讓 Relying Party 驗證回應是否由可信任的 OpenID Provider 產生,或避免使用者資料在傳遞過程中被直接讀取。


七、OIDC Discovery:自動找到端點設定

每個 OP 需要曝露的端點(授權、Token、UserInfo、簽署金鑰...)都不太一樣,如果每次串接都要手動查文件填 URL,很容易出錯。OIDC Discovery 就是讓 Client 能自動找到這些設定的機制。

只要知道 OP 的 issuer URL,就可以在它後面加上固定路徑 /.well-known/openid-configuration,取得一份 JSON 格式的設定文件,例如:

{
  "issuer": "https://oidc.example.com",
  "authorization_endpoint": "https://oidc.example.com/authorize",
  "token_endpoint": "https://oidc.example.com/token",
  "userinfo_endpoint": "https://oidc.example.com/userinfo",
  "jwks_uri": "https://oidc.example.com/.well-known/jwks.json"
}
欄位 說明
issuer OP 的唯一識別碼,也用來驗證 Token 的 iss
authorization_endpoint 發起認證請求的網址
token_endpoint 交換 Token 的網址
userinfo_endpoint UserInfo Endpoint 的網址
jwks_uri 取得公開簽署金鑰、驗證 Token 簽章用的網址

驗證程式庫通常會直接讀這份設定文件,開發者很少需要手動組出這些請求。如果連 issuer 的位置都還不知道,OIDC 也定義了透過 WebFinger(用 email 或 URL 查詢)來反查 issuer 的方式,但實務上更常見的是 OP 直接把 issuer URL 寫在文件裡。


八、OIDC 專屬的三種流程

OIDC 的流程設計其實不是從零開始,而是延續 OAuth 2.0 既有的流程,再加入身份驗證需要的內容。也就是說,前面在 OAuth 2.0 看到的 Grant Type 概念,到了 OIDC 仍然存在;只是 OIDC 會在原本的授權流程上,加入 openid Scope、ID Token,以及身份資訊相關的 Claims,讓 Relying Party 不只取得存取資源的權限,也能確認目前登入的使用者身份。

常見的 OIDC 流程可以分成三種:

  1. Authorization Code Flow:可以理解成 OAuth 2.0 Authorization Code Grant 的 OIDC 版本。流程一樣會先取得 Authorization Code,再用 Authorization Code 向 Token Endpoint 交換 Token;差別在於 OIDC 的認證請求會帶上 openid Scope,因此 Token Endpoint 回傳的結果除了 Access Token 之外,還會多一個 ID Token,讓 Relying Party 驗證使用者身份。
  2. Implicit Flow:可以先理解成 OAuth 2.0 Implicit Grant 加上 OIDC 身份驗證結果的版本。也就是在原本 Implicit Grant 的基礎上,額外回傳 ID Token,讓 Relying Party 能取得使用者身份資訊。至於它為什麼不適合新系統,後面介紹 Implicit Grant 時再一起說明。
  3. Hybrid Flow:可以先理解成 Authorization Code Flow 與 Implicit Flow 的混合形式。它會在前端先回傳一部分結果,同時保留 Authorization Code,讓 Relying Party 後續再交換其他 Token。

因此,這三種流程可以先用同一個角度理解:它們都延續 OAuth 2.0 的流程概念,只是 OIDC 會在流程中加入 ID Token。


小結

OAuth 2.0 本身主要解決的是「授權」問題;OIDC 則是在 OAuth 2.0 之上補上「身份驗證」的一層。它最重要的新增內容是 ID Token,讓 Relying Party 可以用標準化 Claims 驗證目前登入的使用者身份。

整理 OIDC 的重點:

  1. OAuth 2.0 回答「能不能存取資源」:Access Token 主要是給 Resource Server 判斷 API 存取權限用的。
  2. OIDC 回答「目前登入的人是誰」:ID Token 才是 Relying Party 用來確認使用者身份的核心資料。
  3. UserInfo Endpoint 用來補充使用者資料:如果 ID Token 裡的資訊不夠,Relying Party 可以用 Access Token 呼叫 UserInfo Endpoint,取得更多標準化 Claims。

下一篇會回到 OAuth 2.0 的授權流程,討論一個完全沒有使用者參與的場景:當後端服務、自動化程式或內部系統需要用自己的身份呼叫 API 時,Client Credentials Grant 是怎麼讓 Client 取得 Access Token 的。


參考資源


上一篇
Day 10|PKCE:讓 Authorization Code Grant 更安全的關鍵補丁
下一篇
Day 12|Client Credentials Grant:伺服器對伺服器的授權方式
系列文
從登入到授權-現代軟體的身分架構指南14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言