The OAuth 2.0 Authorization Framework
OAuth 2.0授權框架支援第三方支援訪問有限的HTTP服務,通過在資源所有者和HTTP服務之間進行一個批准互動來代表資源者去訪問這些資源,或者通過允許第三方應用程式以自己的名義獲取訪問許可權。
為了方便理解,可以想象OAuth2.0就是在使用者資源和第三方應用之間的一箇中間層,它把資源和第三方應用隔開,使得第三方應用無法直接訪問資源,從而起到保護資源的作用。
為了訪問這種受保護的資源,第三方應用(客戶端)在訪問的時候需要提供憑證。即,需要告訴OAuth2.0你是誰你要做什麼。
你可以將使用者名稱和密碼告訴第三方應用,讓第三方應用直接以你的名義去訪問,也可以授權第三方應用去訪問。
可以聯想一下微信公眾平臺開發,在微信公眾平臺開發過程中當我們訪問某個頁面,頁面可能彈出一個提示框應用需要獲取我們的個人資訊問是否允許,點確認其實就是授權第三方應用獲取我們在微信公眾平臺的個人資訊。這裡微信網頁授權就是使用的OAuth2.0。
1. Introduction
在傳統的client-server認證模型中,客戶端通過提供資源所有者的憑證來請求伺服器訪問一個受限制的資源(受保護的資源)。為了讓第三方應用可以訪問這些受限制的資源,資源所有者共享他的憑證給第三方應用。
1.1. Roles
OAuth定義了四種角色:
- resource owner(資源所有者)
- resource server(資源伺服器)
- client(客戶端):代表資源所有者並且經過所有者授權去訪問受保護的資源的應用程式
- authorization server(授權伺服器):在成功驗證資源所有者並獲得授權後向客戶端發出訪問令牌
1.2. Protocol Flow
抽象的OAuth2.0流程如圖所示:
- (A) 客戶端向資源所有者請求其授權
- (B) 客戶端收到資源所有者的授權許可,這個授權許可是一個代表資源所有者授權的憑據
- (C) 客戶端向授權伺服器請求訪問令牌,並出示授權許可
- (D) 授權伺服器對客戶端身份進行認證,並校驗授權許可,如果都是有效的,則發放訪問令牌
- (E) 客戶端向資源伺服器請求受保護的資源,並出示訪問令牌
- (F) 資源伺服器校驗訪問令牌,如果令牌有效,則提供服務
1.3. Authorization Grant
一個授權許可是一個憑據,它代表資源所有者對訪問受保護資源的一個授權,是客戶端用來獲取訪問令牌的。
授權型別有四種:authorization code, implicit, resource owner password credentials, and client credentials
1.3.1. Authorization Code
授權碼是授權伺服器用來獲取並作為客戶端和資源所有者之間的中介。代替直接向資源所有者請求授權,客戶端定向資源所有者到一個授權伺服器,授權伺服器反過來指導資源所有者將授權碼返回給客戶端。在將授權碼返回給客戶端之前,授權伺服器對資源所有者進行身份驗證並獲得授權。因為資源所有者只對授權伺服器進行身份驗證,所以資源所有者的憑據永遠不會與客戶機共享。
1.3.2. Implicit
隱式授權是為了兼顧到在瀏覽器中用諸如JavaScript的指令碼語言實現的客戶端而優化的簡化授權程式碼流程。在隱式授權流程中,不是發給客戶端一個授權碼,而是直接發給客戶端一個訪問令牌,而且不會對客戶端進行認證。隱式授權提高了一些客戶端(比如基於瀏覽器實現的客戶端)的響應能力和效率,因為它減少了獲得訪問令牌所需的往返次數。
1.3.3. Resource Owner Password Credentials
資源所有者的密碼憑據(比如,使用者名稱和密碼)可以直接作為授權許可來獲取訪問令牌。這個憑據只應該用在高度信任的資源所有者和客戶端之間(比如,客戶端是系統的一部分,或者特許的應用),並且其它授權模式不可用的時候。
1.3.4. Client Credentials
客戶端憑據通常用作授權許可
1.3.5. Access Token
訪問令牌是用來訪問受保護的資源的憑據。一個訪問令牌是一個字串,它代表發給客戶端的授權。令牌代表資源所有者授予的對特定範圍和訪問的時間(PS:令牌是有範圍和有效期的),並由資源伺服器和授權伺服器強制執行。訪問令牌可以有不同的格式、結構和使用方法。
1.3.6. Refresh Token
Refresh Token是用於獲取Access Token的憑據。重新整理令牌是授權伺服器發給客戶端的,用於在當前訪問令牌已經失效或者過期的時候獲取新的訪問令牌。重新整理令牌只用於授權伺服器,並且從來不會發給資源所有者。
重新整理的流程如圖所示:
- (A) 客戶端請求獲取訪問令牌,並向授權伺服器提供授權許可
- (B) 授權伺服器對客戶端身份進行認證,並校驗授權許可,如果校驗通過,則發放訪問令牌和重新整理令牌
- (C) 客戶端訪問受保護的資源,並向資源伺服器提供訪問令牌
- (D) 資源伺服器校驗訪問令牌,如果校驗通過,則提供服務
- (E) 重複(C)和(D)直到訪問令牌過期。如果客戶端直到訪問令牌已經過期,則跳至(G),否則不能繼續訪問受保護的資源
- (F) 自從訪問令牌失效以後,資源伺服器返回一個無效的令牌錯誤
- (G) 客戶端請求獲取一個新的訪問令牌,並提供重新整理令牌
- (H) 授權伺服器對客戶端進行身份認證並校驗重新整理令牌,如果校驗通過,則發放新的訪問令牌(並且,可選的發放新的重新整理令牌)
2. Client Registration
在使用該協議之前,客戶端向授權伺服器註冊。
2.1. Client Types
OAuth定義了兩種客戶端型別:
- confidential:能夠維護其憑證的機密性的客戶端
- public:不能維護其憑證的機密性的客戶端
2.2. Client Password
擁有客戶端密碼的客戶端可以使用HTTP Basic向伺服器進行認證,當然前提是授權伺服器支援HTTP Basic認證。
例如:Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3
二者選其一的,授權伺服器可能支援在請求體中用下列引數包含客戶端憑據:
- client_id:必須的,在授權伺服器中註冊過的客戶端識別符號。
- client_secret:必須的,客戶端祕鑰。如果祕鑰是空字串的話可以省略該引數。
用這兩個引數將客戶端憑據包含在請求體中這種方式不推薦,並且應該限制客戶端不能直接用HTTP Basic認證方案。
3. Protocol Endpoints
授權處理用兩個授權伺服器端點:
- Authorization endpoint:用於客戶端從資源所有者那裡獲取授權
- Token endpoint:用於客戶端用授權許可互動訪問令牌
還有一個端點
- Redirection endpoint:用於資源伺服器通過資源所有者使用者代理將包含授權憑據的響應返回給客戶端
3.1. Authorization Endpoint
授權端點用於和資源所有者互動並獲取一個授權許可的。授權伺服器必須首先校驗資源所有者的身份。
3.1.1. Response Type
客戶端用以下引數通知授權伺服器自己渴望的授權型別:
- response_type:必須的。為了請求一個授權碼這個值必須是"code",為了請求一個訪問令牌這個值必須是"token"
3.1.2. Redirection Endpoint
在完成和資源所有者的互動以後,授權伺服器直接將資源所有者的user-agent返回給客戶端。授權伺服器重定向到這個user-agent
3.2. Access Token Scope
授權和令牌端點允許客戶端使用“scope”請求引數指定訪問請求的範圍。反過來,授權伺服器使用“scope”響應引數通知客戶機它所發放的訪問令牌的範圍。
4. Obtaining Authorization
為了獲得一個訪問令牌,客戶端需要先從資源所有者那裡獲得授權。授權是以授權許可的形式來表示的。
OAuth定義了四種授權型別:
- authorization code
- implicit
- resource owner password credentials
- client credentials
4.1. Authorization Code Grant
授權碼流程如圖所示:
- (A) 客戶端通過將資源所有者的使用者代理指向授權端點來啟動這個流程。客戶端包含它的客戶端識別符號,請求範圍,本地狀態,和重定向URI,在訪問被允許(或者拒絕)後授權伺服器立即將使用者代理返回給重定向URI。
- (B) 授權伺服器驗證資源所有者(通過使用者代理),並確定資源所有者是否授予或拒絕客戶端的訪問請求。
- (C) 假設資源所有者授權訪問,那麼授權伺服器用之前提供的重定向URI(在請求中或在客戶端時提供的)將使用者代理重定向回客戶端。重定向URI包括授權碼和前面客戶端提供的任意本地狀態。
- (D) 客戶端用上一步接收到的授權碼從授權伺服器的令牌端點那裡請求獲取一個訪問令牌。
- (E) 授權伺服器對客戶端進行認證,校驗授權碼,並確保這個重定向URI和第三步(C)中那個URI匹配。如果校驗通過,則發放訪問令牌,以及可選的重新整理令牌。
4.1.1. Authorization Request
客戶端通過使用“application/x-www-form- urlencoding”格式向授權端點URI的查詢元件新增以下引數來構造請求URI
- response_type:必須的。值必須是"code"。
- client_id:必須的。客戶端識別符號。
- redirect_uri:可選的。
- scope:可選的。請求訪問的範圍。
- state:推薦的。一個不透明的值用於維護請求和回撥之間的狀態。授權伺服器在將使用者代理重定向會客戶端的時候會帶上該引數。
例如:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
4.1.2. Authorization Response
如果資源所有者授權訪問請求,授權伺服器發出授權程式碼並通過使用“application/x-www-form- urlencoding”格式向重定向URI的查詢元件新增以下引數,將其給客戶端。
- code:必須的。授權伺服器生成的授權碼。授權程式碼必須在釋出後不久過期,以減少洩漏的風險。建議最大授權程式碼生命期為10分鐘。客戶端不得多次使用授權程式碼。如果授權程式碼不止一次使用,授權伺服器必須拒絕請求,並在可能的情況下撤銷先前基於該授權程式碼釋出的所有令牌。授權程式碼是繫結到客戶端識別符號和重定向URI上的。
- state:如果之前客戶端授權請求中帶的有"state"引數,則響應的時候也會帶上該引數。
例如:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
4.1.3.1. Error Response
- error:取值如下error_description:可選的
- invalid_request
- unauthorized_client
- access_denied
- unsupported_response_type
- invalid_scope
- server_error
- temporarily_unavailable
- error_description
- error_uri:可選的
4.1.3. Access Token Request
客戶端通過使用“application/ www-form-urlencoding”格式傳送以下引數向令牌端點發出請求
- grant_type:必須的。值必須是"authorization_code"。
- code:必須的。值是從授權伺服器那裡接收的授權碼。
- redirect_uri:如果在授權請求的時候包含"redirect_uri"引數,那麼這裡也需要包含"redirect_uri"引數。而且,這兩處的"redirect_uri"必須完全相同。
- client_id:如果客戶端不需要認證,那麼必須帶的該引數。
例如:
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
4.1.4. Access Token Response
例如:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
4.2. Implicit Grant
隱式授權用於獲取訪問令牌(它不支援重新整理令牌),它針對已知的操作特定重定向URI的公共客戶端進行了優化。這些客戶端通常在瀏覽器中使用指令碼語言(如JavaScript)實現。
因為它是基於重定向的流程,所以客戶端必須有能力和資源所有者的使用者代理(典型地,是一個Web瀏覽器)進行互動,同時必須有能力接收來自授權伺服器的重定向請求。
隱士授權型別不包含客戶端身份驗證,它依賴於資源所有者的存在和重定向URI的註冊。由於訪問令牌被編碼到重定向URI中,所以它可能暴露給資源所有者以及同一臺裝置上的其它應用。
隱式授權流程如圖所示:
- (A) 客戶端引導資源所有者的user-agent到授權端點。客戶端攜帶它的客戶端標識,請求scope,本地state和一個重定向URI。
- (B) 授權伺服器對資源所有者(通過user-agent)進行身份認證,並建立連線是否資源所有者允許或拒絕客戶端的訪問請求。
- (C) 假設資源所有者允許訪問,那麼授權伺服器通過重定向URI將user-agent返回客戶端。
- (D) user-agent遵從重定向指令
- (E) web-hosted客戶端資源返回一個web頁面(典型的,內嵌指令碼的HTML文件),並從片段中提取訪問令牌。
- (F) user-agent執行web-hosted客戶端提供的指令碼,提取訪問令牌
- (G) user-agent將訪問令牌傳給客戶端
4.2.1. Authorization Request
- response_type:必須的。值必須是"token"。
- client_id:必須的。
- redirect_uri:可選的。
- scope:可選的。
4.3. Resource Owner Password Credentials Grant
資源所有者密碼憑證授予型別適用於資源所有者與客戶端(如裝置作業系統或高度特權應用程式)存在信任關係的情況。授權伺服器在啟用這種授予型別時應該特別小心,並且只在其他授權流程不可行的時候才允許使用。
這種授權型別適合於有能力維護資源所有者憑證(使用者名稱和密碼,典型地,用一個互動式的表單)的客戶端。
資源所有者密碼憑證流程如圖:
- (A) 資源所有者提供他的使用者名稱和密碼給客戶端
- (B) 客戶端攜帶從資源所有者那裡收到的憑證去授權伺服器的令牌端點那裡請求獲取訪問令牌
- (C) 授權伺服器對客戶端進行身份認證,並校驗資源所有者的憑證,如果都校驗通過,則發放訪問令牌
4.3.1. Access Token Request
客戶端通過在HTTP請求體中新增"application/x-www-form-urlencoded"格式的引數來向令牌端點請求。
- grant_type :必須的。而且值必須是"password"。
- username :必須的。資源所有者的使用者名稱。
- password :必須的。資源所有者的密碼。
- scope:可選的。
例如:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
4.3.2. Access Token Response
例如:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
4.4. Client Credentials Grant
客戶端用它自己的客戶單憑證去請求獲取訪問令牌
客戶端憑證授權流程如圖所示:
- (A) 客戶端用授權伺服器的認證,並請求獲取訪問令牌
- (B) 授權伺服器驗證客戶端身份,如果嚴重通過,則發放令牌
4.4.1. Access Token Request
- grant_type:必須的。值必須是"client_credentials"。
- scope:可選的。
例如:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
4.4.2. Access Token Response
例如:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
5. Issuing an Access Token
5.1. Successful Response
授權伺服器發放令牌
- access_token:必須的。
- token_type:必須的。比如:"bearer","mac"等等
- expires_in:推薦的。
- refresh_token:可選的。
- scope:可選的。
media type是application/json,引數被序列化成JSON物件。
授權伺服器必須包含"Cache-Control"HTTP頭,並且值必須是"no-store"。
例如:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
6. Refreshing an Access Token
請求引數
- grant_type:必須的。值必須是"refresh_token"。
- refresh_token:必須的。
- scope:可選的。
例如:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
參考
https://tools.ietf.org/html/rfc6749
https://oauth.net/2/
https://aaronparecki.com/oauth-2-simplified/
https://www.oauth.com/oauth2-servers/access-tokens/password-grant/
https://www.oauth.com/oauth2-servers/access-tokens/authorization-code-request/
https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/
https://www.oauth.com/oauth2-servers/device-flow/token-request/
https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/
https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
https://developer.okta.com/blog/2018/05/24/what-is-the-oauth2-implicit-grant-type
https://www.oauth.com/