OAuth2.0 協議入門指南

本文希望以應用場景的角度出發,幫助大家快隨了解OAuth協議流程,更為清楚明白的介紹在各種情況使用什么授權模式更為合適。
OAuth2 官網
原文地址
本系列相關文章:
OpenID Connect 協議入門指南
SAML2.0入門指南

1. 協議中各種角色:應用、API和用戶

第三方應用:客戶端
客戶端,即嘗試去獲得用戶賬號信息的應用,用戶需要先對此操作授權。

API: 資源服務器
即資源服務器,提供用來獲得用戶信息的API。

授權服務器
授權服務器用來提供接口,讓用戶同意或者拒接訪問請求。在某些情況下,授權服務器和API資源服務器會是同一個,但是在大多數情況下,二者是獨立的。

用戶:資源的擁有者
資源的擁有者,當前的請求正在嘗試獲得他們賬戶的部分信息。

2. 創建App

在開始OAuth流程之前,你首選需要注冊一個新應用到服務器。通常在注冊的過程中,需要提供應用的基本信息,比如應用名稱、網站信息、logo等等。此外,你還需要注冊一個重定向URI,用以將用戶通過瀏覽器或者移動客戶端重定向回Web服務器,這個URI很重要,在后面的我們會具體講到

2.1 重定向URI

服務器只會把用戶重定向回注冊過的URI以避免一些安全攻擊。任何HTTP的從定向請求都必須使用TLS保護,所以要求使用HTTPS。這樣的要求是為了防止token在傳輸的過程中被截獲。對于原生APP,重定向的URI可以被注冊為一個自定義的URL scheme,比如

demoapp://redirect

2.2 Client ID & Secret

當注冊完畢之后,一般會返回給用戶一個客戶端ID(Client ID)和一個客戶端密鑰(Client Secret)。這個ID一般是公開的信息,用來構造登錄URL,或者被包含在頁面的JS代碼中。這個密鑰(Secret) 必須是保密的。如果一個已經部署的應用不能保護密鑰,比如一個簡單的JS網頁,則不應該使用客戶端密鑰,同時理論上服務器也不應該向這類應用頒發密鑰。

3. 授權:得到授權碼

OAuth 2流程的第一步是獲得用戶的授權。對于基于瀏覽器應用或是移動應用,這一步通常是在服務器顯示給用戶的接口上完成。

OAuth 2提供了多種授權模式(grant types),根據不同的情況而使用。

  • 授權碼模式:適用于Web應用、瀏覽器應用或是移動APP;
  • 口令模式:適用于使用用戶名和口令登錄的模式;
  • 應用訪問模式:適用于應用訪問;
  • 默認模式:之前被推薦在沒有Secret情況下使用,現在被沒有客戶端密鑰的授權碼模式取代。

每一個模式的使用細節將在下面的章節中說明。

3.1 Web服務

Web服務是最常見的應用類型。用戶所使用的Web App程序運行在服務器端,其源碼不會公開暴露,這就代表著這類應用在授權的過程中可以使用客戶端密鑰(Client Secret),以避免某些攻擊手段。

3.1.1 授權

創建登錄鏈接,將用戶重定向到授權服務器:

https://oauth2server.com/auth?response_type=code&
 client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
  • code: 代表服務器希望收到授權碼;
  • client_id: 注冊應用時頒發的ID;
  • redirect_uri: 指明當用戶授權完成之后返回的地址;
  • scope: 一個或多個值,用來指明希望獲得用戶賬戶哪部分權限;
  • state:由應用生成的一個隨機字符串,會在稍后的過程中去驗證。

通過以上的鏈接,用戶將會看到如下的界面


image.png

當用戶點擊“Allow”之后,授權服務器會把用戶重定向回應用網站,并在鏈接中帶上授權碼:

https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
  • code: 服務器返回的授權碼;
  • state: 返回請求授權碼過程中發送的state;

當應有接收到這個請求時,要首先驗證state是否就是應用剛才發送的值。在發送state之后,可以把state保存到Session以便于后續的比較。這樣做的目的是防止應用接受任意偽造的授權碼。

3.1.2 換取Token

然后使用授權碼到資源服務器換取Token:

POST https://api.oauth2server.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_HERE&
  redirect_uri=REDIRECT_URI&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET
  • grant_type=authorization_code - 代表授權模式為驗證碼;
  • code=AUTH_CODE_HERE - 驗證碼;
  • redirect_uri=REDIRECT_URI - 重定向URI;
  • **client_id=CLIENT_ID **- 注冊應用時頒發的ID;
  • client_secret=CLIENT_SECRET - 客戶端密鑰,因為當前請求時服務器之間傳輸的,并沒有暴露給用戶。

API服務器會返回授權碼和其有效期:

{
  "access_token":"RsT5OjbzRn430zqMLgV3Ia",
  "expires_in":3600
}

或者是授權失敗的提示:

{
  "error":"invalid_request"
}

安全性提示:服務器要求應用必須提前注冊從定向URI

3.2 純頁面應用

純頁面應用,也稱為瀏覽器應用,其所有運行代碼都會加載到本地的瀏覽器上。因此其代碼會暴露給使用者的瀏覽器,不能保護客戶端密鑰的安全,所以客戶端密鑰不能在這種情況下使用,除此之外其和Web應用沒有太大區別。

以前,曾經推薦使用默認模式(implicit type),該種模式將會直接返回token,并不使用授權碼來換取token。但是到了本文編寫之時,業界已經開始轉為推薦使用沒有Scret的授權碼流程,這樣這樣協議流程更為安全,具體內容請見引文: Redhat, Deutsche Telekom, Smart Health IT.

3.2.1 授權

創建登錄鏈接,將用戶發送至授權服務器:

https://oauth2server.com/auth?response_type=code&
client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
  • code - 指明使用授權碼模式;
  • client_id - 注冊時頒發的client ID;
  • redirect_uri - 重定向URI,代表當授權完成之后,返回的路徑;
  • scope - 一個或多個值,代表所希望訪問當前用戶的那部分信息;
  • state - 由于后續步驟驗證實用的隨機字符串。
    之后,同樣用戶會顯示授權頁面:
    image.png

    當用戶點擊“Allow”,授權服務器依據重定向URI將用戶返回到應用網站:
https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
  • code - 授權服務器返回的授權碼;
  • state - 登錄請求中發出的隨機碼;

應有服務器收到該請求之后,應該先核對state的值,以避免接受偽造的授權碼;

3.2.2 換取Token

POST https://api.oauth2server.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_HERE&
  redirect_uri=REDIRECT_URI&
  client_id=CLIENT_ID
  • grant_type=authorization_code - 代表授權模式為驗證碼;
  • code=AUTH_CODE_HERE - 驗證碼;
  • redirect_uri=REDIRECT_URI - 重定向URI;
  • **client_id=CLIENT_ID **- 注冊應用時頒發的ID;

3.3 移動APP

以前,曾經推薦使用默認模式(implicit type),該種模式將會直接返回token,并不使用授權碼來換取token。但是到了本文編寫之時,業界已經開始轉為推薦使用沒有Scret的授權碼流程,這樣這樣協議流程更為安全,

類似于純頁面應用,移動應用不能包含客戶端密鑰,所以使用的是沒有客戶端密鑰的Oauth流程。此外,對于移動APP,還有一些特別地方需要注意。

3.3.1 授權

創建一個登陸按鈕,將用戶跳轉到服務器在手機上原生APP,或是通過手機的瀏覽器跳轉到服務器Web頁面、無論是IOS還是Android,都可以通過URL Scheme協議啟動本地APP;

3.3.1.1 使用服務器的APP

如果用戶安裝了服務器的原生APP,比如微博APP或是微信APP,可以通過URL來直接啟動APP:

fbauth2://authorize?response_type=code&client_id=CLIENT_ID
  &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
  • code - 指明使用授權碼模式;
  • client_id - 注冊時頒發的client ID;
  • redirect_uri - 重定向URI,代表當授權完成之后,返回的路徑;
  • scope - 一個或多個值,代表所希望訪問當前用戶的那部分信息;
  • state - 由于后續步驟驗證實用的隨機字符串。

對于還要支持PKCE extension的服務器,還需要創建一個隨機字符串("code verifier")保存本地,然后再URL中追加如下參數:

  • code_challenge=XXXXXXX - 過base64編碼后的"code verifier"的Hash值;
  • code_challenge_method=S256 - 指明Hash算法種類,這里是sha256;

需要說明的是,這里的URL用的協議App提前向操作系統定義好的。

3.3.1.2 使用手機Web瀏覽器

如果服務器在手機上沒有一個原生的APP,也可以通過手機瀏覽器上來走標準的Web認證流程。這里需要注意的是,不要使用應用內置的WebView,因為這樣就不無法保證用戶正在登陸網站是不是偽造的。

一般是使用移動端的原生瀏覽器,對于IOS 9+版本的用戶,可以使用“SafariViewController”在應用中啟動嵌入瀏覽器,這個API會顯示地址欄讓用戶判斷是否打開正確的網頁,同時還提供和Safari瀏覽器共享cookie的功能。該API能保證應用被注入和修改,所以可以被認為是安全的。對于Android開發者,可以考慮使用Chrome Custom Tabs來提供類似的功能。

https://facebook.com/dialog/oauth?response_type=code&client_id=CLIENT_ID
 &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx

參數的意義和使用服務器原生APP的情況是一樣的。比如用戶登錄FaceBook,就會在手機上看到如下界面:

everyday-city-auth.png

3.3.2 換取Token

當用戶點擊"Approve"之后,用戶將會被重定向到應用,同樣可以使用URL Scheme:

fb00000000://authorize#code=AUTHORIZATION_CODE&state=1234zyx

應用收到該請求之后,要先比較state的值是否滿足預期,然后用授權碼來換取Token。

換取Token的過程和在Web中請求類似,只不過沒有加入Client Secret。如果服務器需要支持PKCE,則還需要添加更多的參數:

POST https://api.oauth2server.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_HERE&
  redirect_uri=REDIRECT_URI&
  client_id=CLIENT_ID&
  code_verifier=VERIFIER_STRING
  • grant_type=authorization_code - 標識這是授權碼模式;
  • code=AUTH_CODE_HERE - 授權碼;
  • redirect_uri=REDIRECT_URI -重定向URL;
  • client_id=CLIENT_ID - Client ID;
  • code_verifier=VERIFIER_STRING - 之前步驟中,生成的隨機字符串

如果服務器支持PKCE,則授權服務器需要能“Code-Challenge”中識別code,也就是提供再次對code_verifier進行Hash,并且比較計算出的Hash值和之前的code_challenge是否一致,以此代替Client Secret,保證安全性。

3.4 其他授權模式

3.4.1 口令模式

這種模式直接使用用戶名和口令來取回Token。很顯然,這要求應用去收集用戶的口令,所以只建議當前的APP是專門為服務器設計的情況下使用。比如Twitter的App使用該模式登錄。

POST https://api.oauth2server.com/token
  grant_type=password&
  username=USERNAME&
  password=PASSWORD&
  client_id=CLIENT_ID
  • grant_type=password - 指明使用口令模式;
  • username=USERNAME - 用戶名;
  • password=PASSWORD - 口令;
  • client_id=CLIENT_ID - client ID;

資源服務器返回的Token和其他模式是一樣的。值得注意的是,因為口令模式多用于移動端和桌面應用,使用Secret并不合適

3.4.2 應用訪問模式

在一些情況下,是應用去訪問服務區獲取資源,而不是某個用戶。比如應用去獲取一些服務在應用配置信息,這些信息不屬于某個用戶的。在這種情形下,應用也需要被授權,得到Token去訪問數據,這就是應用訪問模式的意義。

OAuth提供了client_credentials模式來結局這個問題:

POST https://api.oauth2server.com/token
    grant_type=client_credentials&
    client_id=CLIENT_ID&
    client_secret=CLIENT_SECRET

資源服務器返回形式和其他模式是相同的。

4. 創建已經授權的請求

最后就是使用Token獲取資源,如何表面一個請求是已經唄授權?就是Http的頭中加入Token:

curl -H "Authorization: Bearer RsT5OjbzRn430zqMLgV3Ia" \
https://api.oauth2server.com/1/me

另外還要確保是使用HTTPS通信,這樣才能確保信息安全。

5. 與OAuth 1.0版本的差異

最后說明下兩個版本的OAuth協議有什么不同點。

5.1 認證和簽名

OAuth 1.0 流程中有大量密碼學上的簽名操作,以保證數據完整性;OAuth 2中有HTTPS來保護數據。

5.2 用戶體驗

和OAuth 1.0相比,OAuth 2在移動端的體驗更好。

5.3 性能

和OAuth 1.0相比,OAuth 2性能更好,減少了流程的步驟。

其他參考

Learn more about creating OAuth 2.0 Servers
PKCE Extension
Recommendations for Native Apps
More information is available on OAuth.net
Some content adapted from hueniverse.com.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容