OAuth 2.1 框架

OAuth 2.1 Draft

當前版本:v2-1-05
失效時間:2022/09/08

本文對部分原文翻譯,同時加了一些筆記,以便理解。

單詞 譯意
identifiler 識別碼
Resource Owner 資源擁有者
User-Agent 用戶代理
Authorization Code 授權碼
Access Token 訪問令牌
refresh token 刷新令牌
scope 可選
endpoint 端點
AS 授權服務器

許可類型

要獲取訪問令牌,客戶端需要從資源擁有者那裏獲得授權。本規約定義了以下幾種授權許可類型。

  • 授權碼(authorization code)
  • 客戶端證書(client credentials)
  • 刷新令牌(refresh token)

本規約還提供了擴展機制,以便定義其他許可類型。

授權碼許可

授權碼許可類型用於獲取訪問令牌和刷新令牌。

許可類型使用額外的授權端點,實現授權服務器與資源擁有者交互,以便獲取資源訪問准許。

由於這是一個基於重定向的工作流,客戶端必須能夠通過資源擁有者(比如某個用戶)的用戶代理(一般指 web 瀏覽器)初始化工作流,並且能夠從授權服務器重定向回來。

授權碼流程圖

注意:圖中所示的步驟(1)、(2)、(3)在通過用戶代理的時候會分爲兩個部分。

圖中包括的步驟如下:

(1)客戶端通過將資源擁有者的用戶代理指向授權端點來發起授權流程。請求攜帶客戶端自己的識別碼、code challenge(來自生成的 code verifier)、請求範圍(可選)、local state(可選,這裏的意思是可以傳遞一些客戶端的數據,回調的時候會把這些數據原樣傳回來)、回調 URI,當授權服務器許可(或拒絕)的時候會向該 URI 發送用戶代理。

關於 code verifier,可查看下一節 [[#授權請求]]

(2)授權服務器認證資源擁有者的身份(通過用戶代理),並確定資源擁有者是許可還是拒絕客戶端的訪問請求。

(3)假設資源擁有者許可訪問,則授權服務器通過之前(在發起請求時或者客戶端註冊期間)提供的重定向 URI 將用戶代理重定向回客戶端。重定向 URI 裏包括授權碼和客戶端之前提供的任何 local state。

(4)客戶端從授權服務器的令牌端點請求訪問令牌,請求中需要攜帶上個步驟中獲取的授權碼、以及客戶端自己的 code verifier。當發起請求時,如果授權服務器可以認證身份,客戶端將通過授權服務器認證身份。客戶端爲了驗證,將攜帶重定向 URI 以便獲取授權碼。

(5)授權服務器儘可能的認證客戶端的身份,驗證授權碼、code verifier,並且保證接收到的 URI 與步驟(3)中重定向到客戶端的 URI 是匹配的。如果通過認證,授權服務器返回反問令牌,以及刷新令牌(可選)。

授權請求

要發起授權請求,客戶端需要將參數添加到授權服務器的授權端點 URI 上,以此構建授權請求 URI。客戶端最終會將用戶代理重定向到此 URI 上來發起請求。

這裏看上去不是很好理解,我提供一個示例
``` java
	private final String authorizationRequestUri = UriComponentsBuilder  
	  //授權服務端點 URI
      .fromPath("/oauth2/authorize") 
	  //參數
      .queryParam("response_type", "code") 
      .queryParam("client_id", "messaging-client")  
      .queryParam("scope", "openid message.read message.write")  
      .queryParam("state", "state")  
      .queryParam("redirect_uri", this.redirectUri)  
      .toUriString();

客戶端每次發起授權請求都使用唯一的密鑰,以此避免授權碼注入,CSRF 攻擊。客戶端先生成此密鑰,它可以在使用授權碼時使用它來證明使用授權碼的客戶端就是請求它的客戶端。

客戶端通過 application/x-www-form-urlencoded 格式,添加以下參數到授權端點 URI 的查詢組件中,構造客戶端的請求 URI。

參數 是否必填 說明
response_type 授權端點支持不同的請求集合和響應數據。客戶端根據 response_type 的值來決定授權流程。本規約定義了值的代碼,該代碼必須用於指示客戶端要使用授權碼流程。

"response_type":必填。授權端點支持不同的請求集合和響應數據。客戶端根據 response_type 的值來決定授權流程。本規約定義了值的代碼,該代碼必須用於指示客戶端要使用授權碼流程。

擴展的響應類型可能是包含空格分隔符(%x20)的列表,這些響應類型的值在列表中順序不會產生影響(例如,響應類型 a b 等同於 b a)。這類組合響應類型的含義有它們各自的規範定義。

某些擴展響應類型由 OpenID 定義。

如果授權請求缺少 response_type 參數,或者如果響應類型無法理解,授權服務器必須返回錯誤響應。

參數 是否必填 說明
client_id 客戶端識別碼
code_challenge 是或推薦 Code challenge
code_challenge_method 可選 默認值 plain,Code verifier 轉換方法爲 S256 或 plain
redirect_uri 可選
scope 可選
state 可選 客戶端用於維護請求與回調之間的狀態。授權服務器在重定向用戶代理回客戶端時將此值加入請求中

code_verifier 是唯一的熵很高的加密隨機字符串,每次授權請求生成一次,使用 unreserved 字符包括 [A-Z]、[a-z]、[0-9]、“-”、“.”、“ _ ”、“~”,最小字符串長度爲 43,最大字符長度 128。

1948年,香農Claude E. Shannon引入信息(熵),將其定義爲離散隨機事件的出現概率。一個系統越是有序,信息熵就越低;反之,一個系統越是混亂,信息熵就越高。所以說,信息熵可以被認爲是系統有序化程度的一個度量。

客戶端臨時存儲 code_verifier,計算用於授權請求的 code_challenge。

用於 code_verifier 的 ABNF(巴科斯範式)如下。

code-verifier = 43 * 128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / " _ " / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

注意:code verifier 的熵應該足夠高,以至於值不會被猜到。建議使用合適的隨機數生成器來創建一個 32 octet 的序列。每個 octect 序列使用 base64url 編碼後生成一個 43 octet 的 URL 安全的字符串作爲 code verifier。

1 octet = 8 bits

爲什麼不使用 byte,因爲 byte 的語義存在歧義,歷史上的 byte 不是固定的 8 位。

客戶端然後在 code verifier 的基礎上創建 code_challenge:

S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

plain
code_challenge = code_verifier

如果客戶端能夠使用 S256,則必須使用 S256,因爲服務器上強制執行(Mandatory To Implement,MTI) S256。客戶端只能在由於某些技術原因不支持 S256 的情況下,才能使用 pain,例如,受環境限制不能使用哈希函數,並且通過帶外配置或者授權服務器元數據得知服務器支持 plain。

用於 code_challenge 的 ABNF(巴科斯範式)如下。

code-challenge = 43 * 128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / " _ " / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

code_challenge 和 code_verifier 的屬性吸取了 OAuth 2.0 的擴展”Proof-Key for Code Exchange“,也叫做 PKCE,也是這項技術的起源地。

授權服務器必須支持 code_challenge 和 code_verifier 參數。

客戶度必須使用 code_challenge 和 code_verifier,除了一些在 7.6 節 中描述的條件外,服務器也必須強制客戶端使用 code_challenge 和 code_verifier。在當前情況下,我們仍然推薦按照下面列出的方式強制使用 code_challenge 和 code_verifier。

state 和 scope 參數不應該在 plain 文本中包含和客戶端、資源擁有者相關的敏感信息,因爲它們能夠通過不安全的通道傳輸,或者以不安全的方式存儲。

客戶端通過 HTTP 重定向或者用戶代理提供的其他方式,指示資源擁有者構造 URI。

例如,客戶端指示用戶代理髮起以下 HTTP 請求(額外的斷行符只是爲了顯示的目的):

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
       &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
       &code_challenge=6fdkQaPm51l13DSukcAH3Mdx7_ntecHYd1vi3n0hMZY
       &code_challenge_method=S256 HTTP/1.1
   Host: server.example.com

授權服務器驗證請求,以確保所有的必須的參數都有效。

特別地,如果請求中存在 redirect_uri,授權服務器必須驗證,確保其值與客戶端註冊的 URI 匹配。當比較兩個 URI 時,授權服務器必須一個字符一個字符的比較。

如果請求有效,授權服務器認證資源擁有者的身份,並且獲取授權決策(通過詢問資源擁有者或者通過其他方式建立批准)。

這種獲取授權決策,具體體現,比如:使用微信登錄其他 App 時,會跳轉到微信 App,詢問用戶是否允許。

當完成決策後,授權服務器指示用戶代理使用 HTTP 重定向響應或用戶代理提供的其他方式,提供客戶端重定向 URI。

授權響應

如果資源擁有者許可訪問請求,授權服務器將頒發一個授權碼並傳送給客戶端,使用 application/x-www-form-urlencoded 格式,在重定向 URI 查詢組件中添加以下參數:

參數 是否必填 說明
code 由授權服務器生成的授權碼,並且對客戶端不透明。授權碼在頒發後,必須在短期內失效,以防泄漏。推薦授權碼的生命週期爲 10 分鐘。客戶端只能使用一次授權碼。如果使用授權碼超過一次,授權服務器必須拒絕請求,並且應該撤銷(如果可能的話)基於上次頒發的授權碼生成的所有訪問令牌和刷新令牌。授權碼與客戶端識別碼、code challenge、重定向 URI綁定。
state 如果客戶端授權請求中包含 state 參數。確切值來自客戶端。

例如,授權服務器通過發送以下 HTTP 響應重定向用戶代理:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
             &state=xyz

客戶端必須忽略不能識別的響應參數。本規約未定義授權碼字符串的大小。客戶端應該避免假定授權碼的大小。授權服務器應該記錄頒發的授權碼的大小。

服務器關聯頒發的授權碼與 code_challenge 的確切方法,超出了本規約的範圍。code challenge 應該存儲在服務器上,並且在服務器上關聯授權碼。code_challenge 和 code_challenge_method 的值可以以加密的方式存儲在代碼自身中,但是服務器不能在響應參數重包括 code_challenge 的值,只能提取 AS 以外的實體。

客戶端必須防止攻擊者注入授權碼到授權響應中。使用 code_challenge 和 code_verifier 可以阻止注入授權碼,原因是如果 code_verifier 不匹配,授權服務器將拒絕令牌請求。

錯誤響應

如果請求由於重定向 URI 缺失、無效、或者不匹配失敗,或者如果客戶端識別碼缺失、無效,授權服務器應該通知資源擁有者錯誤,並且不能自動將用戶代理重定向到錯誤的 URI。

AS 必須拒絕不攜帶 code_challenge 來自公共客戶端的請求,並且必須拒絕來自其他客戶端的這類請求,除非能保證客戶端客戶端不會以其他方式注入授權碼。

如果服務器不支持請求的 code_challenge_method 轉換方法,授權端點必須返回錯誤響應,並將 error 的值設爲 invalid_request。error_description 或者 error_uri 應該解釋錯誤的本質,比如,不支持的轉換算法。

如果資源擁有者拒絕訪問請求,或者如果請求失敗是因爲除了重定向 URI 缺失或失效之外的原因,授權服務器應該使用 application/x-www-form-urlencoded 格式,向重定向 URI 查詢組件中添加以下參數:

參數 是否必填 描述 錯誤代碼 錯誤代碼描述
error 必填 錯誤參數不能包含特殊代碼 %x20-21、%x23-5B、%x5D-7E。 invalid_request 請求缺少必須的參數,存在無效的參數值,同一個參數出現多次,個稅不正確
unauthorized_client 使用此方式請求授權碼時,客戶端未被授權
access_denied 資源擁有者或者授權服務器拒絕請求
unsupported_response_type 授權服務器不支持使用此方法獲取授權碼
invalid_scope 請求範圍無效、未知、或者格式錯誤
server_error 授權服務器遇到未知的情況,進而不能完成請求。(此錯誤代碼時必須的,因爲不能通過 HTTP 重定向,返回 500 錯誤到客戶端)
temporarily_unavailable 由於授權服務器當前不能處理超載或維護狀態,導致不能處理請求。(此錯誤代碼時必須的,因爲不能通過 HTTP 重定向,返回 503 狀態到客戶端)
error_description 可選 具備可讀性的文本,提供額外的信息,當發生錯誤時,協助開發者理解錯誤。來自 error_description 的參數不能包含特殊字符 %x20-21、%x23-5B、%x5D-7E。
error_uri 可選 指定一個包含錯誤信息的 web 頁面 URI,爲客戶端開發者提供和錯誤相關的額外信息。error_uri 的參數符合 URI 引用的語法,因此不能包含特殊字符 %x20-21、%x23-5B、%x5D-7E。
state 必填 如果客戶端授權請求中存在 state 參數,則該參數是必填。該參數的值來自客戶端。

例如,授權服務器通過發送以下請求重定向用戶代理:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz

令牌端點擴展

授權准許類型在令牌端點通過 authorization_code 的 grant_type 的值來識別。

如果設置此值。則需要設置以下額外令牌請求參數:

參數 是否必填 描述
code 必填 來自授權服務器的授權碼
redirect_uri 必填 如果授權請求中有 redirect_uri 參數,如[[#授權請求]]中所述,在此情形下,它們的值必須相同。如果授權請求中沒有 redirect_uri,此參數是可選的。
code_verifier 必填 如果授權請求中有 code_challenge 參數。絕對不能在其他地方使用。原始的 code verifier 字符串。

例如,客戶端發起如下 HTTP 請求(包含用於顯示目的地換行符):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
&code_verifier=3641a2d12d66101249cdf7a79c000c1f8c05d2aafcf14bf146497bed

除了[[請求令牌]]的處理規則外,授權服務器必須

  • 確保授權碼頒發給經過認證的受信任的客戶端或者有憑據的客戶端,如果是公開客戶端,確保授權碼頒發給請求中的 client_id。
  • 驗證授權碼的有效性
  • 驗證 code_verifier 參數,當且僅當授權請求中存在 code_challenge 參數時。
  • 如果存在 code_verifier,則從接收到的 code_verifier 中計算 code challenge,並與之前關聯的 code_challenge 經過客戶端指定的 code_challenge_method 轉換後進行比較,以此驗證 code_verifier。
  • 確保包含 redirect_uri 參數,如果 redirect_uri 如[[#授權請求]]中描述的那樣包含在初始授權請求中,如果存在,則要確保它們的值是相同的。

客戶端證書許可

當客戶端在其控制範圍內請求訪問受保護的資源時,客戶端能夠只使用它的客戶端證書請求訪問令牌(或者其他支持的認證方式),或者另一個資源擁有者提前安排了授權服務器(處理方式超出了此規約的範圍)。

客戶端證書許可類型必須只能被受信任的客戶端或者有憑據的客戶端使用。

客戶端證書許可包含以下步驟:
(1)通過授權服務器做客戶端身份認證,從令牌端點請求訪問令牌。
(2)授權服務器認證客戶端身份,如果有效,頒發訪問令牌。

令牌端點擴展

授權許可類型通過 grant_type 的值 client_credentials 識別令牌終端。

如果設置了此值,則不需要[[#令牌請求]]之外參數:

例如,客戶端發起如下 HTTP 請求:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

授權服務器必須認證客戶端的身份。

刷新令牌許可

刷新令牌是授權服務器頒發給客戶端的憑證,可以用它基於現有的許可方式獲取新的(刷新)訪問令牌。客戶端使用此選項,要麼因爲上一個訪問令牌過期,要麼因爲上次獲取的訪問令牌 scope 比各自通過的許可窄,並且在相同的許可下請求 scope 不同的訪問令牌。

刷新令牌必須安全保存,只在授權服務器和頒發刷新令牌的客戶端之間共享。授權服務器必須在刷新令牌和頒發給的客戶端之間維護綁定關係。

當客戶端身份被認證時,授權服務器必須驗證刷新令牌和客戶端身份的綁定關係。當客戶端無法認證身份時,授權服務器應該頒發 sender-constrained 刷新令牌或者使用刷新令牌反轉(參見 [[#刷新訪問令牌]])。

授權服務器必須確保刷新令牌不能被未授權的第三方生成、修改、或者猜着生成有效的刷新令牌。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章