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/