OAuth2.0
OAuth1.0雖然在安全性上經過修補已經沒有問題了,但還存在其它的缺點,其中最主要的莫過於以下兩點:其一,簽名邏輯過於復雜,對開發者不夠友好;其二,授權流程太過單一,除了Web應用以外,對桌面、移動應用來說不夠友好。
為了彌補這些短板,OAuth2.0做了以下改變:
首先,去掉簽名,改用SSL(HTTPS)確保安全性,所有的token不再有對應的secret存在,這也直接導致OAuth2.0不兼容老版本。
其次,針對不同的情況使用不同的授權流程,和老版本只有一種授權流程相比,新版本提供了四種授權流程,可依據客觀情況選擇。
在詳細說明授權流程之前,我們需要先了解一下OAuth2.0中的角色:
OAuth1.0定義了三種角色:User、Service Provider、Consumer。而OAuth2.0則定義了四種角色:Resource Owner、Resource Server、Client、Authorization Server:
- Resource Owner:User
- Resource Server:Service Provider
- Client:Consumer
- Authorization Server:Service Provider
也就是說,OAuth2.0把原本OAuth1.0里的Service Provider角色分拆成Resource Server和Authorization Server兩個角色,在授權時交互的是Authorization Server,在請求資源時交互的是Resource Server,當然,有時候他們是合二為一的。
下面我們具體介紹一下OAuth2.0提供的四種授權流程:
Authorization Code
可用范圍:此類型可用於有服務端的應用,是最貼近老版本的方式。
+----------+ | 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)
Client向Authorization Server發出申請(/oauth/2.0/authorize):
response_type = code client_id redirect_uri scope state
Authorization Server在Resource Owner授權后給Client返回Authorization Code:
code state
Client向Authorization Server發出申請(/oauth/2.0/token):
grant_type = authorization_code code client_id client_secret redirect_uri
Authorization Server在Resource Owner授權后給Client返回Access Token:
access_token token_type expires_in refresh_token
說明:基本流程就是拿Authorization Code換Access Token。
Implicit Grant
可用范圍:此類型可用於沒有服務端的應用,比如Javascript應用。
采用Implicit Grant方式獲取Access Token的授權驗證流程又被稱為User-Agent Flow,適用於所有無Server端配合的應用(由於應用往往位於一個User Agent里,如瀏覽器里面,因此這類應用在某些平台下又被稱為Client-Side Application),如手機/桌面客戶端程序、瀏覽器插件等,以及基於JavaScript等腳本客戶端腳本語言實現的應用,他們的一個共同特點是,應用無法妥善保管其應用密鑰(App Secret Key),如果采取Authorization Code模式,則會存在泄漏其應用密鑰的可能性。其流程示意圖如下:

對於應用而言,其流程只有一步,即直接獲取Access Token。
+----------+ | 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 | | | +---------+
Client向Authorization Server發出申請(/oauth/2.0/authorize):
response_type = token client_id redirect_uri scope state
Authorization Server在Resource Owner授權后給Client返回Access Token:
access_token token_type expires_in scope state
說明:沒有服務端的應用,其信息只能保存在客戶端,如果使用Authorization Code授權方式的話,無法保證client_secret的安全。BTW:不返回Refresh Token。
請求數據包格式
為了獲取Access Token,應用需要將用戶瀏覽器(或手機/桌面應用中的瀏覽器組件)到百度OAuth2.0授權服務的“https://openapi.baidu.com/oauth/2.0/authorize”地址上,並帶上以下參數:
-
- client_id:必須參數。注冊應用時獲得的API Key。
- response_type:必須參數。此值固定為“token”。
- redirect_uri:授權后要回調的URI,即接受Access Token的URI。如果用戶在授權過程中取消授權,會回調該URI,並在URI末尾附上error=access_denied參數。對於無Web Server的應用,其值可以是“oob”,否則redirect_uri所在域名必須與開發者注冊應用時所提供的網站根域名列表或應用站點地址(如果根域名列表沒填寫)的域名相匹配。
-
- scope:非必須參數。以空格分隔的權限列表,若不傳遞此參數,代表請求用戶的默認權限。關於權限的具體信息請參考“權限列表”。
- state:非必須參數。用於保持請求和回調的狀態,授權服務器在回調時(重定向用戶瀏覽器到“redirect_uri”時),會在Query Parameter中原樣回傳該參數。
- display:非必須參數。登錄和授權頁面的展現樣式,默認為“page”,具體參數定義可以參考“自定義授權頁面樣式”一節。
例如:“client_id”為“Va5yQRHlA4Fq4eR3LT0vuXV4”的應用要請求某個用戶的默認權限和email訪問權限,並在授權后需跳轉到“http://www.example.com/oauth_redirect”,同時希望在彈出窗口中展現用戶登錄授權界面,則應用需要重定向用戶瀏覽器到如下URL:
https://openapi.baidu.com/oauth/2.0/authorize? response_type=token& client_id=Va5yQRHlA4Fq4eR3LT0vuXV4& redirect_uri=http%3A%2F%2Fwww.example.com%2Foauth_redirect& scope=email& display=popup& state=xxx
響應數據包格式
若用戶登錄並接受授權,授權服務將重定向用戶瀏覽器到“redirect_uri”,並在Fragment中追加如下參數:
-
- access_token:要獲取的Access Token;
- expires_in:Access Token的有效期,以秒為單位;請參考“Access Token生命周期方案”
- scope:Access Token最終的訪問范圍,即用戶實際授予的權限列表(用戶在授權頁面時,有可能會取消掉某些請求的權限),關於權限的具體信息參考“權限列表”一節;
- state:如果請求獲取Access Token時帶有state參數,則將該參數原樣返回。
- session_key:基於http調用Open API時所需要的Session Key,其有效期與Access Token一致;
- session_secret:基於http調用Open API時計算參數簽名用的簽名密鑰。
例如:
HTTP/1.1 302 Found Location: http://www.example.com/oauth_redirect#access_token=1.a6b7dbd428f731035f771b8d15063f61.86400.1292922000-2346678-124328&expires_in=86400&scope=basic%20email&session_key=ANXxSNjwQDugf8615OnqeikRMu2bKaXCdlLxn&session_secret=248APxvxjCZ0VEC43EYrvxqaK4oZExMB&state=xxx
若請求錯誤或用戶拒絕授權,授權服務將重定向用戶瀏覽器到“redirect_uri”,並在Fragment中追加如下參數:
-
- error:錯誤碼;關於錯誤碼的詳細信息請參考“百度OAuth2.0錯誤響應”一節。
- error_description:錯誤描述信息,用來幫助理解和解決發生的錯誤;
- state:如果請求獲取Access Token時帶有state參數,則將該參數原樣返回。
例如:
HTTP/1.1 302 Found Location: http://www.example.com/oauth_redirect#error=access_denied&error_description=user%20didnot%20allow%20your%20authorization%20request&state=xxx
開發者需要注意的事項
-
- 默認情況下,Access Token的有效期為永久(一個月的Access Token + 10年的Refresh Token)。關於Token方案的詳細信息,請參考“Access Token生命周期方案”一節。
- 采用Implicit Grant方式獲取Access Token時,不會返回Refresh Token,即使該應用申請了1個月Access Token + 10年的Refresh Token這種token生命周期方案。
- 獲取Access Token時所返回的session_key和session_secret參數不是OAuth2.0協議標准規定的返回參數,而是百度OAuth2.0服務擴展加入的,目的是使得開發者可以基於http調用百度的Open API,因為基於https調用Open API雖然更為簡單,但畢竟響應速度更差(比基於http的要差一倍時間左右)。
Resource Owner Password Credentials
可用范圍:不管有無服務端,此類型都可用。
+----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+
Clien向Authorization Server發出申請(/oauth/2.0/token):
grant_type = password username password scope
AuthorizationServer給Client返回AccessToken:
access_token token_type expires_in refresh_token
說明:因為涉及用戶名和密碼,所以此授權類型僅適用於可信賴的應用。
Client Credentials
可用范圍:不管有無服務端,此類型都可用。
+---------+ +---------------+ | | | | | |>--(A)- Client Authentication --->| Authorization | | Client | | Server | | |<--(B)---- Access Token ---------<| | | | | | +---------+ +---------------+
Client向Authorization Server發出申請(/oauth/2.0/token):
grant_type = client_credentials client_id client_secret scope
Authorization Server給Client返回Access Token:
access_token token_type expires_in
說明:此授權類型僅適用於獲取與用戶無關的公共信息。BTW:不返回Refresh Token。
…
流程中涉及兩種Token,分別是Access Token和Refresh Token。通常,Access Token的有效期比較短,而Refresh Token的有效期比較長,如此一來,當Access Token失效的時候,就需要用Refresh Token刷新出有效的Access Token:
+--------+ +---------------+ | |--(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 -----------| | +--------+ & Optional Refresh Token +---------------+
Client向Authorization Server發出申請(/oauth/2.0/token):
grant_type = refresh_token refresh_token client_id client_secret scope
Authorization Server給Client返回Access Token:
access_token expires_in refresh_token scope
…
不過並不是所有人都對OAuth2.0投贊成票,有空可以看看:OAuth 2.0對Web有害嗎?