跳到內容

OAuth 2.1

OAuth 2.1 是 OAuth 2.0 的演進版本,由 draft-ietf-oauth-v2-1 定義,目前仍是 IETF 草案。它不是破壞性的新版本,而是把過去十年 OAuth 2.0 生態系累積的最佳實踐、安全修補與獨立 RFC(如 PKCE、Security BCP)整合成一份清楚的文件。

OAuth 2.0(RFC 6749)在 2012 年發布,當時的設計決策有些在後來被證明是不安全的。這些年間,IETF 透過獨立 RFC 陸續發布修補:

  • RFC 7636(PKCE):防止授權碼攔截
  • RFC 8252:行動 App 最佳實踐
  • RFC 9700(Security BCP):安全最佳實踐整合

OAuth 2.1 的目標是把這些散落的文件整合起來,讓新開發者有明確的單一參考點,不需要讀完一堆 RFC 才知道「正確」的做法。

1. 強制使用 PKCE(所有 Authorization Code Flow)

Section titled “1. 強制使用 PKCE(所有 Authorization Code Flow)”

OAuth 2.0 中,PKCE 原本只建議 Public Client 使用。OAuth 2.1 要求所有使用 Authorization Code Flow 的 Client,無論是否有 client_secret,都必須使用 PKCE

OAuth 2.0:PKCE 建議 Public Client 使用
OAuth 2.1:PKCE 所有 Client 必須使用

→ 詳見 Authorization Code + PKCE

歷史背景:Implicit Flow 的設計源於 2012 年前瀏覽器的技術限制。當時瀏覽器的 Same-Origin Policy 非常嚴格,JavaScript 幾乎無法對跨域端點(如 Authorization Server 的 /token)發送 POST 請求,CORS 尚未普及。為了讓 SPA 也能取得 Token,Implicit Flow 設計了一條捷徑:直接把 Access Token 放在 redirect 的 URL fragment(#access_token=...)回傳給瀏覽器,省去後端換 Token 的步驟。

然而這個設計有嚴重缺陷:Token 出現在 URL 中,容易透過瀏覽器歷史紀錄、Referer header、或同頁的第三方 JavaScript 外洩。現代瀏覽器已全面支援 CORS,SPA 可以直接對 /token 端點發送請求,Implicit Flow 存在的理由已不復存在。

OAuth 2.1 正式廢除 Implicit Flow,SPA 應改用 Authorization Code + PKCE

3. 廢除 Resource Owner Password Credentials(ROPC)

Section titled “3. 廢除 Resource Owner Password Credentials(ROPC)”

歷史背景:ROPC 的設計初衷是提供一條「遷移路徑」,讓原本使用帳號密碼直接登入的舊系統能夠平滑過渡到 OAuth。RFC 6749 明確說明這個 grant type 只應在其他流程不可行時使用,且只適用於高度信任的 Client(例如第一方 App)。換句話說,它從設計之初就不是主流選擇,而是不得已的過渡方案。

ROPC 要求使用者直接把帳號密碼交給 Client,Client 再去換 Token——這完全違反了 OAuth 的核心設計:使用者不應該把憑證交給第三方應用。多年實踐後,業界發現 ROPC 不僅破壞安全模型,也無法支援 MFA、CAPTCHA 等進階驗證機制。

OAuth 2.1 正式廢除 ROPC,原本使用 ROPC 的場景應改用 Authorization Code + PKCE,或 Device Code Flow(無瀏覽器裝置)

OAuth 2.1 對 Refresh Token 加強了安全規範,引入 Refresh Token Rotation 機制:

sequenceDiagram
    participant Client
    participant AuthServer as Authorization Server

    Note over Client, AuthServer: 第一次
    Client->>AuthServer: Refresh Token (RT-1)
    AuthServer->>Client: Access Token + Refresh Token (RT-2)
    Note right of AuthServer: RT-1 立即失效

    Note over Client, AuthServer: 第二次
    Client->>AuthServer: Refresh Token (RT-2)
    AuthServer->>Client: Access Token + Refresh Token (RT-3)
    Note right of AuthServer: RT-2 立即失效
  • Rotation:每次使用 Refresh Token 換新 Token 後,舊的 Refresh Token 必須立即失效,同時核發新的 Refresh Token
  • Replay Detection:如果一個已失效的 Refresh Token 被再次使用,Authorization Server 應視為 Token 可能已遭竊,應撤銷該 Token 家族的所有 Token,要求使用者重新登入

所有 redirect_uri 必須使用 HTTPS(localhost 除外,用於開發測試)。

Authorization Server 必須對 redirect_uri完整字串比對,不允許部分匹配或模糊匹配。

Before(OAuth 2.0 時代,部分實作允許前綴比對):

已註冊:https://app.example.com/callback
請求中:https://app.example.com/callback?injected=evil ← 部分實作允許
請求中:https://app.example.com/callback/extra ← 部分實作允許

After(OAuth 2.1 要求完整比對):

已註冊:https://app.example.com/callback
請求中:https://app.example.com/callback ← ✅ 完全相符,允許
請求中:https://app.example.com/callback?injected=evil ← ❌ 不符,拒絕
請求中:https://app.example.com/callback/extra ← ❌ 不符,拒絕

這個要求是為了防止 開放重導向攻擊(Open Redirect),攻擊者可能透過 redirect_uri 操控把 Authorization Code 導向惡意站點。

流程狀態
Authorization Code + PKCE✅ 保留,且強制 PKCE
Client Credentials✅ 保留
Device Code✅ 保留
Refresh Token✅ 保留,但加強安全要求
Implicit❌ 廢除
Resource Owner Password Credentials❌ 廢除

OAuth 2.1 目前仍是草案,沒有正式的「升級版本號」。但如果你的系統:

  • 使用 Authorization Code Flow → 確認有加上 PKCE
  • 使用 Implicit Flow → 改用 Authorization Code + PKCE
  • 使用 ROPC → 評估改用其他流程
  • 核發 Refresh Token → 確認有做 Token Rotation

做到以上這些,你的實作就已經符合 OAuth 2.1 的精神。