導航網(wǎng)站開發(fā)用戶文檔新站seo優(yōu)化快速上排名
前言
最近在項目中需要實現(xiàn)SSO
(單點登錄)功能,以實現(xiàn)一處注冊,即可在任何平臺之間登錄的功能。我們項目中并沒有直接對接第三方認證系統(tǒng)而是通過集成keycloak
完成一系類安全協(xié)議的對接工作。如果我們在代碼級別自己完成各種安全協(xié)議的對接是一項十分大的工程。不僅要走統(tǒng)一的一套安全體系還有兼容各種安全協(xié)議的對接。唉,想想就頭疼。
為了簡單高效實現(xiàn)SSO
功能,項目就集成了keycloak
.項目中只需要驗證Keycloak
簽發(fā)Token
即可。至于協(xié)議的對接交由keycloak
完成即可。
上圖所示:keycloak
提供了各種協(xié)議的配置。我們只需簡單配置就可以完成某種協(xié)議的對接工作。大大提高了開發(fā)效率。
前前后后陸陸續(xù)續(xù)對接了SAML
, OPENID
,OAuth2.0
等協(xié)議。 這個不得不說keycloak
對安全協(xié)議的集成還是很力的。在對接不同協(xié)議時,也想知道協(xié)議背后認證的執(zhí)行流程及細節(jié)。今天我們就討論一下今天的主題OAuth2.0
協(xié)議。
OAuth2.0
OAuth2.0
規(guī)范來自RFC-6749。事實上,核心OAuth
規(guī)范甚至不被稱為規(guī)范。從技術上將,它是一個“框架”,可以用它來構建規(guī)范。這樣做的部分原因是因為它留下了很多可選的東西,并要求實現(xiàn)者決定支持哪些授權類型,刷新令牌是否是一次性使用,甚至訪問令牌是應該持有者令牌還是使用某種簽名令牌機制。
這通常被認為是OAuth
的最大失敗,但也是OAuth
在過去10年中成功部署在大型公司中的很大一部分原因。
在過去十年中,OAuth
在部署使用它的系統(tǒng)的經(jīng)驗中得到了很多修補和擴展。它以原作者甚至看不到的方式進行了擴展。請記住,當OAuth 2.0
在2012年發(fā)布時,iPhone 5
是全新的,Microsoft
的最新瀏覽器是Internet Explorer 9
,單頁應用程序被稱為“AJAX應用程序”,而CORS
還不是W3C
標準。
不斷發(fā)展的 OAuth 2.0 環(huán)境
在傳統(tǒng)的客戶端-服務器身份驗證模型中,客戶端通過使用資源所有者的憑據(jù)向服務器進行身份驗證,從而請求服務器上的訪問受限資源(受保護的資源)。為了向第三方應用程序提供對受限資源的訪問權限,資源所有者與第三方共享其憑據(jù)。這會產生一些問題和限制:
- 第三方應用程序需要存儲資源所有者的憑據(jù)以供將來使用,通常是明文密碼。
- 盡管密碼存在固有的安全漏洞,但服務器仍需要支持密碼身份驗證。
- 第三方應用程序對資源所有者的受保護資源的訪問權限過于廣泛,使資源所有者無法限制持續(xù)時間或訪問有限的資源子集。
- 資源所有者無法撤消對單個第三方的訪問權限,在不撤銷對所有第三方的訪問的情況下,并且必須通過更改密碼。
- 任何第三方應用程序的泄露都會導致最終用戶的密碼以及受該密碼保護的所有數(shù)據(jù)泄露。
OAuth
通過引入授權層并將客戶端的角色與資源所有者的角色分開來解決這些問題。在 OAuth
中,客戶端請求訪問由資源所有者控制并由資源服務器托管的資源,并頒發(fā)一組與資源所有者不同的憑據(jù)。
客戶端不會使用資源所有者的憑據(jù)來訪問受保護的資源,而是獲取訪問令牌 – 表示特定范圍、生存期和其他訪問屬性的字符串。訪問令牌由授權服務器在資源所有者的批準下頒發(fā)給第三方客戶端。客戶端使用訪問令牌訪問資源服務器托管的受保護資源。
此規(guī)范設計用于 HTTP
([RFC2616])。在 HTTP
以外的任何協(xié)議上使用 OAuth
都超出了范圍。
1.1角色
OAuth
定義了四個角色:
- 資源所有者:能夠授予對受保護資源的訪問權限的實體。當資源所有者是個人時,它被稱為最終用戶。
- 資源服務器:托管受保護資源的服務器,能夠使用訪問令牌接受和響應受保護的資源請求。
- 客戶端:代表資源所有者并經(jīng)其授權發(fā)出受保護資源請求的應用程序。
- 授權服務器:服務器在成功驗證資源所有者并獲取授權后,向客戶端頒發(fā)訪問令牌。
1.2 協(xié)議流程
+--------+ +---------------+| |--(A)- Authorization Request ->| Resource || | | Owner || |<-(B)-- Authorization Grant ---| || | +---------------+| || | +---------------+| |--(C)-- Authorization Grant -->| Authorization || Client | | Server || |<-(D)----- Access Token -------| || | +---------------+| || | +---------------+| |--(E)----- Access Token ------>| Resource || | | Server || |<-(F)--- Protected Resource ---| |+--------+ +---------------+
A: Client
向Resource Owner
請求授權。授權請求可以由Client
直接向Resource Owner
發(fā)出,或者最好通過授權服務器間接發(fā)出(即通過重定向到Authorization Server
授權頁)。推薦后者
B: Client
收到Resource Owner
的授權許可,授權許可的類型是此規(guī)范中定義的四種授權類型之一或者是一個擴展授權類型。授權類型取決于Client
請求授權的方法和Authorization Server
支持的授權類型。
C: Client
通過向授權服務器進行認證,且出示授權許可來請求access token
。
D: Authorization Server
鑒定客戶端并且驗證授權許可,如果有效,則頒發(fā)access token
。
E: Client
通過access_token
請求Resource Server
上的受保護資源。
F: Resource Server
鑒定access token
,如果有效,處理此次請求。
1.3授權許可類型
授權授予是一種憑據(jù),表示客戶端用于獲取訪問令牌的資源所有者的授權(訪問其受保護的資源)。此規(guī)范定義了四種授權類型(授權代碼、隱式、資源所有者密碼憑據(jù)和客戶端憑據(jù)),以及用于定義其他類型的擴展性機制。
1.3.1授權碼(Authorization Code)
通過使用授權服務器作為客戶端和資源所有者之間的中介來獲取授權代碼。客戶端不是直接從資源所有者請求授權,而是將資源所有者重定向到授權服務器(通過 [RFC2616] 中定義的用戶代理),而授權服務器又使用授權代碼將資源所有者重定向回客戶端。
Client
引導資源擁有者訪問授權服務器來發(fā)起授權,授權服務器引導用戶攜帶授權碼返回Client
之前,會認證資源擁有并生成授權碼。因此用戶的憑證并不會與Client
分享。之后使用授權碼請求access token
。
上圖GitHub按鈕就是Client引導資源擁有者的方式,鼠標放上去時,瀏覽器坐下角的地址為:
https://ruby-china.org/account/auth/github
此請求返回302響應:
重定向地址為:
location:https://github.com/login/oauth/authorize?
clinet_id=f252166dc1760d1478aacd&redirect_uri=https%3A%2F@2Fruby-
china.org%2Fauth%2Fcallback&response_type=code&scope=user%3Aemail&state=2715116841611131dsds8114141
結果為:
當點擊同意授權時,會請求:
請求體為:
1.3.2 隱式(Implicit)
隱含模式是授權碼模式的簡化。隱含模式在用戶授權后直接給Client
頒發(fā)access token
。
隱含模式頒發(fā)access token
時不會鑒定Client
,在某些情況下,可以使用redirect url
來驗證Client
。
1.3.3 資源所有者密碼憑證(Resource Owner Password Credentials)
資源所有者密碼憑據(jù)(即用戶名和密碼)可以直接用作獲取訪問令牌的授權。僅當資源所有者和客戶端之間存在高度信任(例如,客戶端是設備操作系統(tǒng)或高特權應用程序的一部分)以及其他授權授予類型(例如授權代碼)不可用時,才應使用憑據(jù)。
盡管此授權類型需要客戶端直接訪問資源所有者憑據(jù),但資源所有者憑據(jù)將用于單個請求,并交換為訪問令牌。此授權類型可以通過將憑據(jù)與長期訪問令牌或刷新令牌交換來消除客戶端存儲資源所有者憑據(jù)以供將來使用的需要。
1.3.4 客戶端憑證(Client Credentials)
客戶端憑證模式適用于Client要訪問的受保護資源本就在Client的控制下時,即客戶端為自己行事(客戶端也是資源擁有者)
1.4 訪問令牌(Access Token)
訪問令牌是用于訪問受保護資源的憑據(jù)。訪問令牌是一個字符串,表示向客戶端頒發(fā)的授權。字符串通常對客戶端不透明。令牌表示特定的訪問范圍和持續(xù)時間,由資源所有者授予,并由資源服務器和授權服務器強制執(zhí)行。
令牌可以表示用于檢索授權信息的標識符,也可以以可驗證的方式自包含授權信息(即,由某些數(shù)據(jù)和簽名組成的令牌字符串)。客戶端可能需要超出此規(guī)范范圍的其他身份驗證憑據(jù)才能使用令牌。
訪問令牌提供了一個抽象層,將不同的授權結構(例如,用戶名和密碼)替換為資源服務器理解的單個令牌。這種抽象使頒發(fā)的訪問令牌比用于獲取訪問令牌的授權更嚴格,并且消除了資源服務器了解各種身份驗證方法的需要。
根據(jù)資源服務器的安全要求,訪問令牌可以具有不同的格式、結構和使用方法(例如,加密屬性)。訪問令牌屬性和用于訪問受保護資源的方法超出了本規(guī)范的范圍,并由配套規(guī)范(如 [RFC6750])定義。
1.5 刷新令牌(Refresh Token)
Refresh Token是Access Token過期、無效時獲取新的access token是的憑證。它也有Authorization Server頒發(fā),且與Access Token同時頒發(fā)。它對于Client也是不透明的。
+--------+ +---------------+| |--(A)------- Authorization Grant --------->| || | | || |<-(B)----------- Access Token -------------| || | & Refresh Token | || | | || | +----------+ | || |--(C)---- Access Token ---->| | | || | | | | || |<-(D)- Protected Resource --| Resource | | Authorization || Client | | Server | | Server || |--(E)---- Access Token ---->| | | || | | | | || |<-(F)- Invalid Token Error -| | | || | +----------+ | || | | || |--(G)----------- Refresh Token ----------->| || | | || |<-(H)----------- Access Token -------------| |+--------+ +---------------+
A: 客戶端通過向授權服務器進行身份驗證并提供授權來請求訪問令牌。
B: 授權服務器對客戶端進行身份驗證并驗證授權授予,如果有效,則頒發(fā)訪問令牌和刷新令牌。
C: 客戶端通過提供訪問令牌向資源服務器發(fā)出受保護的資源請求。
D: 資源服務器驗證訪問令牌,如果有效,則為請求提供服務。
E: 重復步驟 (C) 和 (D),直到訪問令牌過期。如果客戶端知道訪問令牌已過期,則跳到步驟 (G);否則,它會發(fā)出另一個受保護的資源請求。
F: 由于訪問令牌無效,資源服務器將返回無效令牌錯誤。
G: 客戶端通過向授權服務器進行身份驗證并提供刷新令牌來請求新的訪問令牌。客戶端身份驗證要求基于客戶端類型和授權服務器策略。
H: 授權服務器對客戶端進行身份驗證并驗證刷新令牌,如果有效,則頒發(fā)新的訪問令牌(以及可選的新刷新令牌)。
2 客戶注冊(Client Registration)
在Client使用服務提供商提供的登錄服務之前,需要在服務提供商上注冊Client
應用,具體注冊方法OAuth2.0
不作要求,由實現(xiàn)者自行決定。
但是注冊時Client
需要提供:
Client
類型Client
重定向URI
- 其它任何授權服務器需要的信息
2.1 客戶端類型(Client Types)
OAuth
定義了兩種客戶端類型,具體取決于它們向授權服務器安全進行身份驗證的能力(即維護其客戶端憑據(jù)機密性的能力):
- confidential(機密):能夠維護其憑證機密性的客戶端(例如,在對客戶端憑證的訪問受限的安全服務器上實現(xiàn)的客戶端)。
- public(公共):無法維護其憑證機密性的客戶端(例如,在資源所有者使用的設備上執(zhí)行的客戶端,例如安裝的本地應用程序或基于web瀏覽器的應用程序)。
客戶端類型指定基于授權服務器對安全認證的定義及其可接受的客戶端憑證暴露級別。授權服務器不應對客戶端類型做出假設。
一般實現(xiàn)中不要求Client類型
OAuth2.0
是圍繞以下Client
設計的:
web application
:web
應用web
應用程序是在web
服務器上運行的機密客戶端。資源所有者通過呈現(xiàn)在其用戶代理(即瀏覽器)上的由Client
提供的UI
來訪問Client
。客戶端憑據(jù)以及發(fā)給客戶端的任何訪問令牌都存儲在web
服務器上,并且不向資源所有者公開或由資源所有者訪問。user-agent-based application
: 基于用戶代理的應用程序 基于用戶代理的應用程序是一個公共客戶端,其中客戶端代碼從Web
服務器下載,并在資源所有者使用的設備上的用戶代理(例如Web
瀏覽器)中執(zhí)行。資源所有者可以輕松訪問(并且通??梢?#xff09;協(xié)議數(shù)據(jù)和憑據(jù)。由于此類應用程序駐留在用戶代理中,因此它們可以在請求授權時無縫使用用戶代理功能。native application
: 本機應用程序 本機應用程序是在資源所有者使用的設備上安裝和執(zhí)行的公共客戶端。資源所有者可以訪問協(xié)議數(shù)據(jù)和憑據(jù)。假定可以提取應用程序中包含的任何客戶端身份驗證憑據(jù)。另一方面,動態(tài)頒發(fā)的憑據(jù)(如訪問令牌或刷新令牌)可以獲得可接受的保護級別。至少,這些憑據(jù)受到保護,不會受到應用程序可能與之交互的惡意服務器的攻擊。在某些平臺上,這些憑據(jù)可能會受到保護,不會受到駐留在同一設備上的其他應用程序的影響。
2.2 客戶端標識符
授權服務器向已注冊的客戶端頒發(fā)客戶端標識符 – 表示客戶端提供的注冊信息的唯一字符串。客戶端標識符不是機密;它向資源所有者公開,不得單獨用于客戶端身份驗證??蛻舳藰俗R符對于授權服務器是唯一的。
2.3 客戶端身份驗證(Client Authentication)
如果客戶端類型為機密,則客戶端和授權服務器建立適合授權服務器安全要求的客戶端認證方法。授權服務器可以接受任何形式的客戶端身份驗證,以滿足其安全要求。
機密客戶端通常頒發(fā)(或建立)一組客戶端憑據(jù),用于向授權服務器進行身份驗證(例如,密碼、公鑰/私鑰對)。
授權服務器可以與公共客戶端建立客戶端身份驗證方法。但是,授權服務器不得依賴公共客戶端身份驗證來標識客戶端。
2.3.1 客戶端密碼(Client Password)
擁有Client Password
的客戶端應該使用RFC2617中定義的HTTP Basic
認證方式與授權服務器進行認證。即客戶端標識符作為username
,將username:password
使用application/x-www-form-urlencoded
編碼,并添加到Authorization
請求頭中。如:Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3。
或者,授權服務器可以提供接受request-body的認證接口,request-body包含:
- client_id
- client_serect
雖然OAuth標準不建議使用第二種方案認證,但是真實場景中并不使用Basic認證方式,例如github的OAuth實現(xiàn)就是使用第二種方式,
但是使用時絕對不能讓秘鑰暴露在URI中。
3 協(xié)議端點 (Protocol Endpoints)
授權過程中涉及兩個授權服務器端點:
1. Authorization EndPoint
:Client
通過用戶代理重定向到此端點,依此來獲取授權。
2. Token EndPoint
:Client
攜帶授權許訪問此端點,以交換access token
,此端點需要認證客戶端。
和一個客戶端端點:
3. Redirection EndPoint
:授權服務器通過用戶代理重定向到此端點,以此向客戶端返回包含授權憑證的響應
3.1 授權端點(Authorization Endpoint)
授權端點用于與資源擁有者交互并且獲得其對Client
的授權許可。但在此之前,授權服務器需要先驗證資源擁有者的身份(通過用戶名、密碼登錄,或session cookie
等)。
獲取授權端點的訪問地址的方式OAuth2.0
不作規(guī)范,但是授權端點地址通常會在服務提供商的文檔中給出。
由于授權端點的訪問會導致用戶認證和密碼的明文傳輸,所以授權服務器必須使用TLS
(也就是授權服務器必須使用HTTPS
)
授權服務器必須支持GET方式請求,也應該支持POST方式請求。
我的理解是,授權端點包括授權頁面和授權接口。對端點的GET請求返回授權頁面,資源擁有者在授權頁面同意授權后,對端點進行POST請求以生成正真的授權。
授權端點的參數(shù):
client_id
: 在lattice
中注冊應用時生成的id
redirect_uri
:注冊應用時指定的callback URL
response_type
:授權類型code
為授權碼類型scope
:需要授權范圍state
:client server
生成的隨機狀態(tài)碼
3.1.1 響應類型(Response Type)
授權端點只在授權碼模式和隱含模式流程中使用。Client
使用以下參數(shù)告知授權服務器自己想要的授權類型:
response_type
: 參數(shù)值為"code
"時,授權類型為授權碼;為"token
"時,授權類型為隱含模式。參數(shù)值也可以是其它的擴展值。
如果授權請求沒有攜帶respone_type參數(shù),或者參數(shù)值不受授權服務器支持時,授權服務器必須返回一個如4.1.2.1的錯誤響應
3.1.2 重定向端點(Redirection Endpoint)
在授權服務器完成與資源擁有者的交互后,授權服務器會將用戶引導回Client
(也就是返回302響應,重定向到Client
提供的UI
)。
重定向的URI
是在Client
向服務提供商注冊自身時提供的URI
,或者是在Client
引導資源擁有者執(zhí)行授權請求時所指定的redirect_uri
參數(shù)中的值。
3.1.2.1 端點請求保密性(Endpoint Request Confidentiality)
當重定向請求中包含敏感信息時,重定向端點必須使用TLS
,也就是要使用HTTPS
。
但是由于使用HTTPS
對于大部分Client
開發(fā)者來說有一些障礙(證書貴,免費的證書沒有完整的授權鏈路等),所以如果Client
不使用HTTPS
,則授權服務器必須在重定向之前提醒資源擁有著重定向端點的不安全性。
3.1.2.2 注冊要求(Registration Requirements)
授權服務器必須要求以下類型的Client
注冊他們的重定向端點:
- 所有的
Public Clients
- 使用隱含模式的
Confidential Clients
也就是說,在Client在向服務提供商注冊自身時必須提供重定向端點。
服務提供商應要求Client
提供完整的重定向URI, If requiring the registration of the complete redirection URI is not possible, the authorization server SHOULD require the registration of the URI scheme, authority, and path (allowing the client to dynamically vary only the query component of the redirection URI when requesting authorization)。我的理解是,如果不能注冊完整的URI
,則應該注冊URI
的部分結構,讓Client
在請求授權時指定redirect_uri
時,只能變更相應的查詢參數(shù)。
比如,如果Client
注冊的是https://example.client/index?custom_arg=12
,則在Client
請求授權時指定的redirect_uri
的值可以是:
https://example.client/index?custom_arg=1&append_arg=123
或https://example.client/index/callback?custom_arg=1&append_arg=123
等。也就是說注冊時的URI
格式必須包含在redirect_uri
中。
服務提供商應該允許注冊多個重定向端點。
如果不要求注冊重定向端點,會導致攻擊者將授權服務器用做開放的重定向器。也就是發(fā)送授權請求時redirect_uri
不是已注冊Client
的重定向端點,而服務提供商沒有要求Client
注冊重定向端點,則這個redirect_uri
無從驗證。
3.1.2.3 動態(tài)配置(Dynamic Configuration)
如果Client
在服務提供商中注冊了多個重定向端點,或只注冊了重定向端點的部分,或沒有注冊重定向端點,則Client
在發(fā)起授權請求時必須帶上redirect_uri
參數(shù)。
當Client
在授權請求中提供了redirect_uri
,授權服務器必須將其值與已注冊的重定向URI
進行比較,確保其有效性。如果沒有注冊重定向URI
,那么沒辦法,只能直接使用這個URI
。
3.1.2.4 無效的端點(Invalid Endpoint)
如果授權服務器發(fā)現(xiàn)授權請求中的重定向URI
無效,授權服務器必須提醒用戶,并且不能自動重定向到這個無效URI
。
3.1.2.5 端點內容(Endpoint Content)
通常,對重定向端點的請求最終都會返回HTML
響應。如果HTML
響應直接由重定向端點返回,任何在這個HTML
文檔上的腳本都能讀這個取重定向URI
,即能獲取授權憑證。
因此,重定向端點的響應內容不應該包含第三方腳本。
為了防止第三方腳本讀取憑證:
Client
要讓自身的腳本把URI
中的授權憑證提取出來,也就是重寫URI
,要確保Client
腳本在第三方腳本之前執(zhí)行。
或者,重定向端點應該從其URI
中提取出授權憑證,然后將用戶代理重定向到另一個Client
端點,重定向時授權憑證不能暴露在URI
中。
3.2 Token端點(Token Endpoint)
Client
攜帶授權許可或refresh token
來訪問token
端點以獲取獲取access token
。除了隱含模式,任何授權類型都可以訪問token
端點(隱含模式直接頒發(fā)access token
,沒必要訪問token
端點)。
Client
如何獲取troken
端點的URI
本規(guī)范不作要求,但是服務提供商文檔中一般會給出。token
端點要使用post
請求,且請求參數(shù)的Content-Type
為application/x-www-form-urlencoded
。token
端點必須使用HTTPS
。
3.2.1 客戶端身份驗證(Client Authentication)
當請求Token
端點時,保密Client
或其它被頒發(fā)憑據(jù)的客戶端必須與授權服務器進行身份認證。Client
認證是為了:
- 強制將
refresh token
和授權碼綁定到它們被頒發(fā)的Client
。當授權碼通過不安全的通道傳遞到重定向端點或重定向端點沒有注冊時,Client
認證非常由必要。 - 當
Client
泄漏時,防止攻擊者濫用盜取的refresh token
,并且可以通過改變客戶端憑據(jù)來恢復客戶端的保密性。且更改客戶端憑據(jù)比警用刷新令牌快的多。 - 為了實現(xiàn)認證管理,服務提供商會定期刷新
Client
憑據(jù)。刷新Client
憑據(jù)要比刷新refresh token
更快。
Client
應該在訪問token
端點時攜帶client_id
參數(shù)來標識本身。且client_id
參數(shù)可以用來防止授權碼被替換而不自知。
3.3 Token令牌范圍(Access Token Scope)
授權端點和token
端點允許Client
通過"scope
"請求參數(shù)指定要獲取的access_token
的作用域。相應的,授權服務器使用"scope
"響應參數(shù)來告知Client
它頒發(fā)的access_token
的作用域。
scope
參數(shù)的值是一個大小寫敏感的以空格分隔的字符串列表。列表中的每個字符串都有授權服務器定義,每個字符串代表一個作用域。
基于授權服務器的策略和資源擁有者的指示,授權服務器應該忽略Client
請求的部分作用域,也就是說,Client
要求的作用域只起聲明作用,真正要給予它的作用域主要依靠資源擁有著的授權。如果授權服務器頒發(fā)的access_token
的作用域與Client
要求的作用域不一樣,授權服務器要在響應中帶上scope
參數(shù)來告知Client
被授予的實際作用域。
如果Client
請求token
端點時沒有指定scope
參數(shù),授權服務器可以:
- 使用默認的授權作用域處理請求
- 或認為
scope
無效而拒絕此請求
服務提供商應該在其文檔中明確指出其支持的授權作用域或默認作用域。
4 獲得授權(Obtaining Authorization)
為了獲取access token
,Client
要從資源擁有者手中獲取授權。授權以授權許可的形式表示,Client
使用授權許可來獲取access token
。OAuth
定義了四種授權許可類型:授權碼,隱含授權,資源擁有者憑證和客戶端憑據(jù)。同樣只支持自定義的擴展授權類型。
4.1 授權代碼授予
授權碼類型的許可被用來獲取access token
和 refresh token
,對保密客戶端來說它是最佳的選擇。
因為授權碼授予是一個基于重定向的流程,Client
必須具有與用戶代理交互的能力,它也需要能夠接收從授權碼服務器發(fā)送的請求。
直白點就是Client要能夠將用戶代理重定向到授權端點,授權端點要能夠將用戶代理重定向到重定向端點。
授權碼授權流程:
+----------+| Resource || Owner || |+----------+^|(B)+----|-----+ Client Identifier +---------------+| -+----(A)-- & Redirection URI ---->| || User- | | Authorization || Agent -+----(B)-- User authenticates --->| Server || | | || -+----(C)-- Authorization Code ---<| |+-|----|---+ +---------------+| | ^ v(A) (C) | || | | |^ v | |+---------+ | || |>---(D)-- Authorization Code ---------' || Client | & Redirection URI || | || |<---(E)----- Access Token -------------------'+---------+ (w/ Optional Refresh Token)
圖中的A、B、C步驟由于要經(jīng)過用戶代理,所以被分為了兩部分。
A: Client
使用302響應將資源擁有者的用戶代理重定向到授權端點。
B: 授權服務器認證用戶,并確定資源擁有者是準許還是拒絕Client
的授權請求。
C: 假設資源擁有者準許,授權服務器將資源擁有者的用戶代理重定向到A
步驟中指定的重定向端點URI
,重定向端點URI
的路徑參數(shù)中將包含授權碼和Client
提供的state
。 資源擁有者要準許授權,也必須要通過用戶代理向授權服務器發(fā)送準許授權請求,也就是點擊同意授權按鈕時會發(fā)送這個請求,準許授權請求將返回302響應,用戶代理籍此響應重定向
D: Client
攜帶上一步中獲得的授權碼和重定向端點的URI
去獲取access token
。 在這個請求中,授權服務器需要認證Client
的身份。認證方法就是,Client
在請求時,將請求授權時指定的redirect_uri
(也就是A步驟指定的)也附加到請求參數(shù)中。
E: 授權服務器對Client
進行認證,驗證授權碼,確保redirect_uri
參數(shù)值域步驟C中的重定向端點URI
相同。如果這些驗證通過,授權服務器返回access token
,且選擇性的也返回refresh token
,這取決于授權服務器的策略。
4.1.1 授權請求
授權請求就是A步驟中302響應要發(fā)出的請求。Client
通過將以下參數(shù)以"application/x-www-form-urlencoded
"的形式添加到授權端點URI
后來構造這個請求,參數(shù)為:
response_type(REQUIRED)
:授權碼模式中固定為“code
”,它表示授權許可類型client_id(REQUIRED)
:向服務提供商注冊Client
后得到的client id
,它標識這個授權請求是對哪個Client
的授權redirect_uri(OPTIONAL)
:重定向端點URI
,如果Client
在向服務提供商注冊自身時只提供了一個重定向端點URI
,那么可以不用填;scope(OPTIONAL)
:Client
申請的權限范圍state(OPTIONAL BUT RECOMMENDED)
:Client
用于維護授權端點與重定向端點之間狀態(tài)的不透明值。此參數(shù)用于防止csrf
攻擊。
授權服務器在接收到授權請求時,必須保證所有的參數(shù)是有效且正確的。確認請求有效后,授權服務器將認證資源擁有者然后獲取它的授權決定。當資源擁有者決定后,授權服務器通過302響應將用戶代理重定向到redirect_uri
。
4.1.2 授權響應
授權服務器在獲得資源擁有著的授權后要做兩件事:頒發(fā)授權碼給Client
;將用戶代理重定向到重定向端點。因為重定向端點屬于Client
,所以這兩件事可以通過授權請求的302響應同時完成。
即授權響應可以將授權碼與重定向端點URI
結合。結合方式就是,將以下參數(shù)以"application/x-www-form-urlencoded
"的形式添加到重定向端點URI
,然后將結合體作為302響應的location
響應頭的值,參數(shù)為:
code(REQUIRED)
:授權服務器生成的授權碼,為了降低泄露它產生的風險,code
通常只有10分鐘的有效期,且只能被使用一次。如果一個code
被重復使用,授權服務器應該拒絕請求并且將之前基于此code
頒發(fā)的所有token
都撤銷。授權碼與client id
和重定向端點URI
綁定。state
:如果授權請求中有這個參數(shù),那么授權響應也必須包含它,且值與授權請求中的值保持一致。
Client必須忽略不認識的響應參數(shù)。
響應示例:
HTTP/1.1 302 FoundLocation: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
4.1.2.1 錯誤響應
如果授權請求是由于重定向URI
的缺失、無效或不匹配,或client id
的缺失或無效而被認定為是無效的。授權服務器要以某種方式告知資源擁有者,且不能自動重定向到無效的redirect_uri
。
如果授權請求是由于除上述兩個請求參數(shù)外的其它參數(shù)錯誤和資源擁有者拒絕授權而被認定為失敗,授權服務器應該將以下參數(shù)以"application/x-www-form-urlencoded
"的形式添到重定向端點URI
中來告知Client
請求授權的原因,參數(shù)為:
error(REQUIRED)
:錯誤代碼,其值可以為:
1.1invalid_request
:請求缺失必要參數(shù)、包含無效參數(shù)值、包含多個相同參數(shù)或包含其它畸形參數(shù)
1.2:unauthorized_client
:Client
無權使用此方法獲取授權代碼
1.3:access_denied
:資源擁有著拒絕授權
1.4:unsupported_response_type
:授權服務器不支持使用此返回獲取授權代碼(用戶代理不支持在Location
響應頭中包含Query
參數(shù)時使用此代碼)
1.5:invalid_scope
:授權請求的scope
值無效、位置、或畸形
1.6server_error
:授權服務器內部異常,阻止其完成請求(因為503HTTP狀態(tài)代碼不能通過302重定向來傳遞,所以需要此錯誤代碼)
1.7:temporarily_unavailable
:由于授權服務器過載或維護,無法處理授權請求(也是與f一樣的原因而需要此代碼)error_description(OPTIONAL)
:錯誤描述信息,不能包含ASCII中%x20-21 / %x23-5B / %x5D-7E
3.error_uri(OPTIONAL)
:關于錯誤信息解讀的網(wǎng)頁URI
state
:如果授權請求中有這個參數(shù),那么授權響應也必須包含它,且值與授權請求中的值保持一致。
錯誤響應示例:
HTTP/1.1 302 FoundLocation: https://client.example.com/cb?error=access_denied&state=xyz
4.1.3 Access Token請求
Client
通過攜帶以下參數(shù)向Token
端點發(fā)送請求,這些參數(shù)以UTF-8
編碼并包含在request body
中,且請求的Content-Type
為"application/x-www-form-urlencoded
",參數(shù)為:
grant_type(REQUIRED)
:授權碼授權中,只能設置為"authorization_code
".code(REQUIRED)
:獲取到的授權碼.redirect_uri
:如果授權請求中有此參數(shù),則此參數(shù)必填,且值與授權請求中的值相同client_id
:如果Client
還沒有與授權服務器進行認證,則必填。
雖然rfc6749說的是if the client is not authenticating with the authorization server as described in Section 3.2.1。但是client_id其實永遠都要填,因為授權碼與client id和重定向端點URI綁定
在請求token端點時,保密Client或其它被頒發(fā)憑據(jù)的客戶端必須與授權服務器進行身份認證,認證方法參考2.3.1。
請求示例:
POST /token HTTP/1.1Host: server.example.comAuthorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JWContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
4.1.4 Access Token響應
如果對token
端點的請求有效且資源擁有者已對Client
授權,那么授權服務器將給Client
頒發(fā)access token
(選擇性頒發(fā)refresh token
)具體響應在5.1;如果Client
認證失敗,則返回錯誤響應,具體響應在5.2。
響應示例:
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{"access_token":"2YotnFZFEjr1zCsicMWpAA","token_type":"example","expires_in":3600,"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA","example_parameter":"example_value"}
4.2 隱含授權
隱含授權類型用于直接獲取access token
(不支持獲取refresh token
),對操作已知的重定向URI
的公共客戶端來說是最佳的選擇。
隱含授權也是基于重定向的流程。不同于授權碼授權將授權請求和access token
請求分開,隱含授權的授權請求直接返回access token
。
因為access token
將附在重定向端點URI
上返回,所以access token
會暴露給資源擁有著和設備上的其它應用;因此隱含授權不包括Client
認證,且不依賴資源擁有者的存在和重定向端點的注冊。
隱含授權流程:
+----------+| Resource || Owner || |+----------+^|(B)+----|-----+ Client Identifier +---------------+| -+----(A)-- & Redirection URI --->| || User- | | Authorization || Agent -|----(B)-- User authenticates -->| Server || | | || |<---(C)--- Redirection URI ----<| || | with Access Token +---------------+| | in Fragment| | +---------------+| |----(D)--- Redirection URI ---->| Web-Hosted || | without Fragment | Client || | | Resource || (F) |<---(E)------- Script ---------<| || | +---------------++-|--------+| |(A) (G) Access Token| |^ v+---------+| || Client || |+---------+
A: Client
通過將資源擁有者的用戶代理重定向到授權端點開始授權流程;重定向請求要包含client id
、scope
、state
和redirect uri
等參數(shù)
B: 授權服務器認證資源擁有者,并確定資源擁有者是準許還是拒絕Client
的授權請求
C: 假設資源擁有者同意授權,授權服務器使用302響應將資源擁有者的用戶代理重定向回Client
,重定向URI
為A步驟中指定的redirect uri
,且重定向URI
中將包含access token
。
D: 資源擁有者的用戶代理遵循收到的302響應來請求相應的Client
資源(Client
接收此請求的方法不應該接收請求參數(shù)),請求參數(shù)應由用戶代理保存在本地。
就是說重定向端點接收請求時忽略參數(shù),而用戶代理保存參數(shù)的方法就是不管它,讓他在URI中待著。
E: Client
資源返回一個能夠處理完整重定向URI
和提取access token
的網(wǎng)頁(也就是內嵌js的HTML文檔)。
F: 用戶代理執(zhí)行Client
資源返回的腳本來提取access token
。
G: 用戶代理將access token
傳遞給Client
。
4.2.1 授權請求
隱含授予的授權請求與授權碼授予的授權請求大致一樣,不同之處在于response_type
的值必須為token
.
請求示例:
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1Host: server.example.com
4.2.2 授權響應
rfc6749將此節(jié)命名為access token response,個人覺得還是叫授權響應更好
隱含授予的授權請求的響應直接包含access token
,所以當資源擁有者同意授權后,授權服務器直接給Client
頒發(fā)access token
(絕對對不能頒發(fā)refresh token
),并將它以"application/x-www-form-urlencoded
"格式編碼然后附在重定向端點URI
后作為參數(shù)。響應要攜帶的所有參數(shù)為:
access_token(REQUIRED)
token_type(REQUIRED)
:具體值參照7.1expires_in(RECOMMENDED)
:表示access token
從頒發(fā)到過期的時間間隔,單位為秒。如果忽略它,那么授權服務器應該以其它方式指定過期時間,且應在服務提供商文檔中給出默認值。scope
:代表授權服務器頒發(fā)的access token
的作用域,如果授權的作用域與Client
請求的作用域相同,可以選擇性填寫;如果授權的作用域與Client
請求的作用域不同,則必須填寫state
:如果授權請求中包含此參數(shù),則必填,且值要與授權請求中的值保持一致。
成功響應示例:
HTTP/1.1 302 FoundLocation: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600
開發(fā)者應該注意:
某些用戶代理可能不支持在在302響應的Location響應頭中包含Query參數(shù),所以可能需要使用其它方法來重定向到重定向端點。比如,同意授權后的響應返回一個HTML頁面,頁面中包含一個“continue”按鈕,使用這個按鈕來重定向到重定向端點。
4.2.2.1 錯誤響應
隱含授權的錯誤響應與授權碼授權的錯誤響應一致。
錯誤響應示例:
HTTP/1.1 302 FoundLocation: https://client.example.com/cb#error=access_denied&state=xyz
資源擁有者密碼憑證授予
此授權類型在資源擁有者高度信任Client
的場景中適用(例如:Client
是操作系統(tǒng),或Client
是服務提供商旗下應用)。授權服務器應只在其它授權類型不可用時開啟此授權類型,且啟用此授權類型時要多加注意。
它適用于能夠獲取資源擁有者憑證(用戶名和密碼)的Client
。也適用于將現(xiàn)有的、使用如HTTP Basic
認證等直接認證規(guī)范的Client
遷移到OAuth
規(guī)范。遷移方法是通過將Client
存儲的憑證轉換為access token
。
授權流程:
+----------+| Resource || Owner || |+----------+v| Resource Owner(A) Password Credentials|v+---------+ +---------------+| |>--(B)---- Resource Owner ------->| || | Password Credentials | Authorization || Client | | Server || |<--(C)---- Access Token ---------<| || | (w/ Optional Refresh Token) | |+---------+ +---------------+
A: 資源擁有者向Client
提供其憑證,也就是用戶名和密碼。
B: Client
攜帶資源擁有者憑證向token
端點直接請求access token
。請求時,Client
要與授權服務器進行認證。
C: 授權服務器認證Client
,并且驗證資源擁有者憑證。如果憑據(jù)有效,則頒發(fā)access token
可以看出此授權類型只涉及token端點
4.3.1 授權請求和響應
Client
獲取資源擁有者的憑證的方法此規(guī)范不作要求。但Client
使用憑證獲取到access token
后必須丟棄憑證。
補充:
此授權類型的請求授權方式與其它類型不同。當用戶想要登錄Client時,Client提供的登錄表單就是授權請求,提交表單發(fā)出的請求就是授權響應。
4.3.2 Access Token請求
Client
應使用"application/x-www-form-urlencoded
"格式將參數(shù)放在請求體中來請求token
端點,也就是POST
請求,參數(shù)如下:
grant_type(REQUIRED)
:此授權類型下,必須設置為"password
"username(REQUIRED)
:資源擁有者用戶名password(REQUIRED)
:資源擁有者密碼scope(OPTIONAL)
:Client
想要的授權作用域
如果Client
是保密客戶端或者被頒發(fā)了憑據(jù),Client
就必須與授權服務器進行認證,認證方法參考.
請求示例:
POST /token HTTP/1.1Host: server.example.comAuthorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JWContent-Type: application/x-www-form-urlencodedgrant_type=password&username=johndoe&password=A3ddj3w
4.3.3 Access Token響應
如果請求有效且所有認證通過,授權服務器要頒發(fā)如5.1所述的access token
(選擇性頒發(fā)refresh token
)。
反之,授權服務器返回如5.2所述的錯誤響應。
成功響應示例:
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{"access_token":"2YotnFZFEjr1zCsicMWpAA","token_type":"example","expires_in":3600,"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA","example_parameter":"example_value"}
4.4 Client憑據(jù)授權
當Client
想要訪問在它自己掌握下的資源,或者那些已由資源擁有者授權給Client
訪問的資源時,Client
只需要使用其自身的憑證來獲取access_token
。
只有保密客戶端才能使用Client
憑據(jù)授權
授權流程:
+---------+ +---------------+| | | || |>--(A)- Client Authentication --->| Authorization || Client | | Server || |<--(B)---- Access Token ---------<| || | | |+---------+ +---------------+
A: Client
攜帶自身憑證請求token
端點以獲取access token
。
B: 授權服務器對Client
進行認證。如果驗證通過,則頒發(fā)access token
。
4.4.1 授權請求和響應
因為Client認證本身就可以用做權限授予,所以不需要額外的授權請求。
4.4.2 Access Token請求
Client
將請求參數(shù)以"application/x-www-form-urlencoded
"編碼并放在請求體中來請求token
端點以獲取access token
,參數(shù)為:
grant_type(REQUIRED)
:Client
憑據(jù)授權中,此參數(shù)值必須為"client_credentials
"scope(OPTIONAL)
:Client
請求授權的作用域
授權服務器必須與Client進行認證,認證方法參考2.3.1。
請求示例:
POST /token HTTP/1.1Host: server.example.comAuthorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JWContent-Type: application/x-www-form-urlencodedgrant_type=client_credentials
4.4.3 Access Token響應
如果對token
端點的請求有效且Client
認證通過,則頒發(fā)如5.1所述的access token
,但是不能頒發(fā)refresh token
。
否則,授權服務器返回如5.2所述的錯誤響應。
響應示例:
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{"access_token":"2YotnFZFEjr1zCsicMWpAA","token_type":"example","expires_in":3600,"example_parameter":"example_value"}
5 頒發(fā)Access Token
如果對token
端點的請求有效且Client
認證通過,則頒發(fā)如5.1所述的access token
,選擇性頒發(fā)refresh token
。
否則,授權服務器返回如5.2所述的錯誤響應。
5.1 成功響應
成功響應(200)包含以下響應參數(shù):
access_token(REQUIRED)
:授權服務器頒發(fā)的access token
token_type(REQUIRED)
:具體值參照7.1expires_in(RECOMMENDED)
:表示access token
從頒發(fā)到過期的時間間隔,單位為秒。如果忽略它,那么授權服務器應該以其它方式指定過期時間,且應在服務提供商文檔中給出默認值。refresh_token
:授權碼收授權和資源擁有者密碼憑證授權選填,Client
授權和隱含授權不填scope
:代表授權服務器頒發(fā)的access token
的作用域。在授權碼收錢中不要寫,因為在其授權請求與響應階段就已經(jīng)確定了作用域。對于其它授權類型,如果授權的作用域與Client
請求的作用域相同,可以選擇性填寫;如果授權的作用域與Client
請求的作用域不同,則必須填寫。
這些響應參數(shù)應該放在響應體中,且媒體類型為"application/json
"。
如果授權服務器的響應包含任何token
、憑證或者其它敏感信息時,必須將"Cache-Control
"響應頭設置為"no-store
",且將"Pragma
"響應頭設置為"no-cache
"。
成功響應示例:
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{"access_token":"2YotnFZFEjr1zCsicMWpAA","token_type":"example","expires_in":3600,"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA","example_parameter":"example_value"}
Client
必須忽略響應中不認識的參數(shù)。在服務提供商文檔中應該給出任何授權服務器返回的任何值得長度和格式。
5.2 錯誤響應
錯誤響應(400)包含以下參數(shù):
error(REQUIRED)
:錯誤代碼,其值可以為:
1.1:invalid_request
:請求缺失必要參數(shù)、包含無效參數(shù)值、包含多個相同參數(shù)或包含其它畸形參數(shù)
1.2:invalid_client
:Client認證失敗。The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the “Authorization” request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the “WWW-Authenticate” response header field matching the authentication scheme used by the client。 這個解釋還是用原文比較好理解。
1.3:invalid_grant
:提供的授權許可或refresh token
無效、過期或被撤銷,或者它們與redirect uri
不匹配。
1.4:unauthorized_client
:Client
無權使用此授權類型獲取授權代碼
1.5:unsupported_grant_type
:授權服務器不支持此授權類型
1.6:invalid_scope
:授權請求的scope
值無效、位置、或畸形error_description(OPTIONAL)
:錯誤描述信息,不能包含ASCII中%x20-21 / %x23-5B / %x5D-7Eerror_uri(OPTIONAL)
:關于錯誤信息解讀的網(wǎng)頁URI
這些響應參數(shù)應該放在響應體中,且媒體類型為"application/json
"。
錯誤響應示例:
HTTP/1.1 400 Bad RequestContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{"error":"invalid_request"}
6. 刷新Access Token
如果授權服務器給Client
頒發(fā)了refresh token
,Client
將請求參數(shù)以"application/x-www-form-urlencoded
"格式編碼并添加到請求體中來向token
端點發(fā)送刷新請求,請求參數(shù)為:
1.grant_type(REQUIRED)
:值必須是"refresh_token
"
2. refresh_token(REQUIRED)
:頒發(fā)給Client
的refresh token
3. scope(OPTIONAL)
:請求的授權作用域。scope
的值不得包含資源擁有者最初未授予Client
的作用域,若果省略scope
,那么默認其值為資源擁有者最初授予Client
的作用域。
因為refresh token
是長期憑證,所以它會被綁定到它被頒發(fā)的Client
。如果Client
是保密的或被頒發(fā)了憑證,Client
必須與授權服務器進行認證,認證方法如3.2.1所述。
請求示例:
POST /token HTTP/1.1Host: server.example.comAuthorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JWContent-Type: application/x-www-form-urlencodedgrant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
收到請求后,授權服務器必須:
- 認證保密
Client
和被頒發(fā)了憑證的Client
- 確保
refresh token
是被頒發(fā)為認證Client
的 - 驗證
refresh token
如果認證成功且驗證通過,授權服務器將頒發(fā)access token
給Client
。如果請求無效或沒通過驗證,授權服務器返回如5.2的錯誤響應。
授權服務器也要頒發(fā)新的refresh token
給Client
以替換舊的,然后撤銷Client
與舊refresh token
的綁定,并將新refresh token
與Client
綁定。新refresh token
的作用域要與舊的一樣。
7. 訪問受保護資源
Client
攜帶access token
以訪問受保護資源。資源服務器必須驗證access token
,確保它沒有過期,并且確保它的作用域包含要請求的資源。資源服務器驗證access token
的方法本規(guī)范不做要求,但是通常包含資源服務器與授權服務器的交互和協(xié)作。
Client
利用access token
向資源服務器進行認證的方法取決于授權服務器頒發(fā)的access token
的類型。
7.1 Access Token類型
Access token
類型為Client
提供了能夠成功利用access token
訪問受保護資源的必要信息,Client
不應在不知道access token
的類型下使用它。
具體的Access Token
類型用例,參照RFC6759。
我們一般使用的類型為Bearer
類型。
請求示例:
GET /resource/1 HTTP/1.1Host: example.comAuthorization: Bearer mF_9.B5f-4.1JqM
Authorization請求頭中,Bearer為access token類型,后面的字符串為access token
每個access token類型定義指定了授權服務器頒發(fā)給Client access token時要返回的額外參數(shù)(如果存在)。同樣也定義了Client訪問受保護資源時使用的認證方法。
7.2 錯誤響應
如果資源訪問請求失敗。資源服務器應該告知Client
失敗的原因。雖然錯誤的響應規(guī)范超出了本文檔的范圍,但是總的來說,一個錯誤響應選擇應該包含以下信息以與本規(guī)范其它錯誤響應對應:
error_description
error_uri
參考:
RFC-6749
是時候推出 OAuth 2.1 了