前言:為什麼研究 OAuth 2.0 和 OIDC ?
前陣子在工作中接觸到 Workload Identity Federation (WIF),發現背後繞不開 OAuth 2.0
和 OIDC
。
雖然這些名詞之前也看過,但總是混淆,對於細節的掌握度也不高。
於是我花了一些時間研究,並透過一個小故事來整理筆記。
皇帝、大將軍與玉璽
直接交玉璽的風險
想像你是某個王朝的皇帝。某天邊關告急,外族入侵,朝堂上聲望極高的大將軍請纓出兵平亂。
要讓大將軍能調兵,最快的方法是把「傳國玉璽」交給大將軍。玉璽象徵皇權,見玉璽如見皇帝,各地軍營自然會聽命。
但問題也很明顯:
如果大將軍心懷不軌,拿著玉璽就能隨意調動兵馬,甚至反過來攻打皇宮。
這樣的權力過於集中,風險太大。
多一道程序 : 詔書、腰牌與虎符
為了降低風險,皇帝(你)設計了一個新流程 :
- 大將軍先上奏,說明需要多少兵馬、出兵目的。
- 奏摺經御前總管轉呈,皇帝審核後頒布「詔書」。
如果僅憑詔書即可調兵,很有可能在傳遞的過程中,詔書被調包,讓有心之士偽冒大將軍的身份。
爲了避免此問題,在將軍取得詔書後,必須憑藉詔書和自己獨有的腰牌來「驗明身份」,才能兌換「虎符」。
這樣一來,就算詔書在傳遞過程被調包,沒有大將軍的腰牌,也無法換到虎符。
而虎符是唯一能調動兵馬的信物,權限被限制在皇帝核可的範圍內。
把故事翻譯成技術語言
讓我們試著把故事裡的角色翻譯成技術語言 :
故事角色 | 意涵 | 現實定義或範例 | OAuth 2.0 / OIDC 名詞 |
---|---|---|---|
皇帝 | 擁有最高權力的人 | 使用者本人,真正擁有資料的人 (例:Google 帳號的擁有者) | Resource Owner |
部隊 | 完成任務所需的資源 | 使用者的資料 (例 : Google Calendar 的事件) | Resource |
大將軍 | 代替皇帝執行任務 | 想存取資料的應用程式 (例:行程管理 App) | Client |
御前總管 | 皇帝與將軍之間的中介 | 負責驗證身份、簽發授權碼和 Token 的伺服器 | Authorization Server |
傳國玉璽 | 皇權象徵 | 使用者的帳號密碼 (最直覺但高風險) | Password |
兵營 | 管理部隊的地方 | 儲存並保護資料的伺服器 (例 : Google Calendar API) | Resource Server |
詔書 | 皇帝的臨時命令 | Authorization Server 簽發的一次性授權碼 | Authorization Code |
腰牌 | 將軍自身的憑證 | Client 用來證明自己身份的密鑰 | Client Secret |
虎符 | 最終能調兵的信物 | 能直接存取資源的憑證 | Access Token |
OAuth 2.0 流程解析
理解故事後,讓我們再用技術語言把流程走一次。
假設我們 (Resource Owner
) 今天使用一個管理行程的 App (Client
) ,這個 App 能夠存取使用者的 Google Calendar (Resource Server
) 來匯入行程資訊 (Resource
)。
如果你直接將 Google 的密碼交給 Client
,很難確保它只做約定好的行為 (例如:「讀取」行程) 。
OAuth 2.0
希望能解決的問題就是:
如何讓 Application 可以取得我的資料? (但不要擁有我的密碼)
Step 1 - 使用者授權
- 使用者 (
Resource Owner
) 想讓 App (Client
) 存取 Google Calendar (Resource Server
)。 - App (
Client
) 導向 Google 的Authorization Server
並送出幾個資訊 :- redirect_uri : 認證完成後,要將結果回傳至何處。
- scope :
Client
需要的權限及資源。 - 除了這兩個資訊外,還有
client_id
和state
,因篇幅關係暫不討論。
Authorization Server
會根據 Client
提交的 Scope,轉換成容易閱讀的授權畫面,稱為 Consent
:
Step 2 - Authorization Code
- 使用者 (
Resource Owner
) 核准後,Authorization Server
將Authorization Code
回傳至Client
的 redirect_uri。 Authorization Code
本身不能直接存取資料。(如同故事裡的詔書必須換成虎符)
Step 3 - 換取 Token (Back Channel)
Client
收到 Authorization Code
後,會再向 Authorization Server
換取關鍵的 Access Token
。
為什麼要進行第二步交換呢?細節在於:交換的方式不同。
Step 1 ~ 2 屬於 “Front Channel” : 資訊會經過瀏覽器,容易被攔截。
而換取 Access Token
時,是由 Client
的後端直接和 Authorization Server
溝通,為了確認請求是由合法的 Client
發出,Client
需要同時附上事先與 Authorization Server
約定好的 Client Secret
。
Authorization Server
驗證正確後,才會簽發 Access Token
,後續才能用來取用資源。
Step 4 - 使用 Access Token
Client
帶著Authorization: Bearer <access_token>
呼叫Resource Server
(例如:Google Calendar API)。Resource Server
驗證Access Token
後,回傳資料。
至此,Client
成功在沒有使用者密碼的情況下,取用資料。
OAuth 2.0 的不足
雖然 OAuth 2.0
解決了 “Authorization” 的問題,但它並沒有辦法處理 “Authentication”。
原因在於 OAuth 2.0
的設計初衷,是讓第三方應用程式能在使用者同意的前提下,取得有限範圍的存取權限,而不是確認「這個人是誰」。
在流程中,Client
拿到的 Access Token
,只能代表:
使用者 (
Resource Owner
) 授權了某些操作(例如讀取日曆)。
但 Access Token
並沒有描述使用者的身份。
Client
可以使用它去存取 API,但無法判斷「持有這個 Token 的人是 Allen, Bob 還是 MingLun」。
如果有人單純把 OAuth 2.0
當成「登入機制」,其實是誤用。因為單靠 OAuth 2.0
,應用程式 (Client
) 只能知道「有一個人給了我授權」卻不知道「這個人是誰」。
為了解決這個缺口,才會誕生後來的 OpenID Connect (OIDC)
,在 OAuth 2.0
的基礎上補上「身份驗證」(Authentication) 的能力。
OIDC : 補上身份驗證
OIDC
協定其實就是在 OAuth 2.0
的基礎上,加入額外 5 ~ 10 % 的 Layer 擴充,卻帶來關鍵性的差別:
具體來說,OIDC
在 OAuth 2.0
基礎上加入了:
- ID Token :
- 由
Authorization Server
簽發的 JWT,內含使用者的識別資訊。 - 這讓
Client
能在獲得授權的同時,也知道「使用者是誰」。
- 由
- UserInfo Endpoint :
- 支援
OIDC
協定的Authorization Server
都會實作這個 API Endpoint,Client
可以使用Access Token
呼叫,進一步取得使用者的詳細資訊。
- 支援
流程與 OAuth 2.0
相同,只是在 Scope
中可以加入 openid
。
當 Client
的 Scope
包含 openid
時,Authorization Server
除了 Access Token
還會額外回傳 ID Token
。
ID Token
以 JWT 的形式存在,經過適當的驗證和解碼後,就能確認使用者身份。
這組 JWT 包含三個部分 :
- Header
- Payload (Claims)
- Signature
其中 Payload 的部分經過反解後,會包含類似下列資訊 :
{
"iss": "https://accounts.google.com", // 發行者 (Issuer)
"sub": "110169484474386276334", // 使用者唯一 ID,不會隨 email 更改
"aud": "s6BhdRkqt3", // 此 Token 的受眾 (Audience),通常是 Client ID
"exp": 1311281970, // Token 到期時間 (Unix Timestamp)
"iat": 1311280970, // Token 簽發時間
"auth_time": 1311280969, // 使用者通過驗證的時間
"email": "minglunwu@gmail.com", // 使用者 Email (可變動資訊)
"email_verified": true, // Email 是否已驗證
"name": "MingLun Wu" // 使用者名稱
}
關於 JWT 的部分,礙於篇幅關係,不多做介紹。重點在於:
ID Token
經過 Decode 後,可以從 Payload 中取得基本的使用者資訊。
Client
取得使用者資訊後,即可進行驗證,完成 “Authentication”。
如果需要更多資訊,則可以呼叫 UserInfo
的 endpoint 來取得更多用戶資料。
小結:授權與驗證的邊界
整理到這裡,可以看到兩者的差異:
OAuth 2.0
的核心是授權 (Authorization):讓應用程式在不拿到密碼的情況下,能安全地存取資料。OIDC
則在此基礎上補上身份驗證 (Authentication):讓應用程式知道「這個人是誰」。
換句話說:OAuth 2.0
解決「能不能取資料」,OIDC
解決的是「誰在取資料」。
理解這層分工後,再回頭看 Workload Identity Federation (WIF),會發現它的本質也是相同的:
只是把「使用者登入」換成「Workload 之間的信任交換」,背後仍是 OAuth 2.0
/ OIDC
的精神在運作。
關於 WIF 的內容,留待下篇筆記。
Reference & Credit
如果你想更深入理解 OAuth 2.0
與 OIDC
,我強烈推薦這支影片:
影片長約一小時,但講解得非常完整、清楚。
這篇筆記的靈感和部分理解也受到它的啟發。
結語
希望今天這篇分享能幫助你更快了解 OAuth 2.0
及 OIDC
的核心概念。
謝謝你的閱讀,我們下次見。
About Byte & Ink
我會定期在部落格分享不同主題的文章,目前包含:
- 職涯心得
- 個人成長
- 筆記軟體 - Obsidian 教學
- 技術相關 (
K8S
,DevOps
,軟體測試
…)
如果你覺得內容有幫助,歡迎你點此訂閱我的文章,你的訂閱會帶給我更多動力,持續分享有意義的內容!