OAuth 是一種第三方開放授權的協議,這個協議是用來制定第三方授權伺服器的規則。
透過第三方授權伺服器的授權可以簡化應用程式的註冊流程,例如,許多應用程式都需要註冊才能使用,那會造成每個應用程式都要註冊一遍並且不斷的填寫重複的資料。所以 OAuth 的目的就是要來簡化註冊的流程,讓使用者透過第三方的授權伺服器來授權應用程式取得使用者的資訊。
最常見的就是在應用程式上面會有使用 google 登入,這就是透過 google 的授權伺服器來授權應用程式存取使用者存在 google 的資料。
大致了解了 OAuth 的概念之後,我們要先來看過去還沒有 OAuth 的協議時,應用程式是如何取得第三方服務的資料以及會造成什麼問題。
以往當應用程式要拿取受保護的資源時,需要向伺服器出示使用者的帳號密碼,而此種方式會造成以下幾種問題 :
- 應用程式必須儲存使用者的帳號密碼,且為明碼儲存
- 應用程式擁有使用者帳號密碼相當於擁有近乎完整的存取權限
- 使用者想透過更改帳號密碼撤銷單一應用程式的存取權會造成其他應用程式一併被撤銷存取權
- 一旦其中一個應用程式被破解會導致使用該帳號密碼的資料都被破解
OAuth 將使用者和應用程式分離,中間引入一層認證層,應用程式就必須透過授權的方式才能存取受保護的資源。
角色與名詞定義
- Resource Owner (User) :
使用者,擁有帳號之人 - Client (App):
某個應用程式 - User Agent :
使用者代理,瀏覽器 - Access Token :
存取令牌,用以獲取受保護的資源 - Refresh Token :
更新授權的令牌,當存取令牌無效時,要使用更新授權令牌重新取得存取令牌及更新授權令牌 - Authorization Server :
授權伺服器,專門處理認證的伺服器,負責核發存取令牌和更新授權令牌 - Resource Server :
資源伺服器,存放使用者的資源
下面這張圖是一個簡單的 OAuth 存取流程
授權認證流程(Grant Flows)
OAuth 2.0 定義了四種授權認證的流程:
- Authorization Code Grant Flow (授權碼模式)
- Implicit Grant Flow (隱含授權模式)
- Resource Owner Password Credentials Grant Flow (密碼模式)
- Client Credentials Grant Flow (憑證模式)
每種授權方式都需要應用程式先向授權伺服器註冊,再發送 Request 時,授權伺服器才會知道是哪個應用程式發送 Request。
1. Authorization Code Grant Flow (授權碼模式)
需由使用者授權,相較其他授權較安全。應用程式不直接向使用者要求許可,而是透過授權伺服器轉址的方式在轉回應用程式時夾帶授權碼給應用程式,應用程式再使用授權碼取得存取令牌,進而獲取受保護的資料。
流程 :
-
應用程式會把使用者的瀏覽器導向到授權伺服器,
此時應用程式會傳送以下資料到授權伺服器- 回應的類別 : Code,表示要回傳授權碼
- 應用程式ID
- 請求的存取範圍 : 要存取哪些資料
- 內部狀態 : 應用程式會生成一個隨機字串並包含在請求中,用以對返回的值做檢查是否相同
- 重新導向的 URI : 告訴授權伺服器要導回來哪個網址
-
授權伺服器透過瀏覽器認證使用者(e.g, 使用者登帳號密碼),
確定使用者的許可或是駁回應用程式的申請 -
如果使用者許可了應用程式存取,授權伺服器會把瀏覽器導回先前指定的 URI,
並且回傳以下資料- 授權碼(Authroization Code)
必須短時效,建議10分鐘且只能使用一次,若重複使用則授權伺服器必須拒絕並且撤銷之前透過這個授權碼申請的存取令牌
- 許可的存取範圍(若與申請的不一樣才會回傳)
- 先前提供的內部狀態(若先前有提供才會回傳)
-
應用程式向授權伺服器要求存取令牌,並傳送以下資料來申請
- 先前取得的授權碼
- 重新導向的 URI(用以驗證是否和第三步導回的URI是同一個)
- 應用程式的認證資料(應用程式需要先跟授權伺服器註冊,註冊內容包含帳號密碼、重新導向的URL、名稱、網站、Logo等等)
-
授權伺服器會去認證應用程式、驗證授權碼、確認重新導向的URI是否一致,都符合則授權伺服器會回傳存取令牌、更新授權令牌(可選擇是否需要)
2. Implicit Grant Flow (隱含授權模式)
在隱含授權模式中,授權伺服器不會認證應用程式而會直接核發存取令牌,存取令牌會以 Fragment 的形式夾在重新導向的 URI 上回傳,因此存取令牌有外洩風險。也因為外洩風險高所以禁止申請更新授權令牌。
隱含授權模式適用於前端 Web 應用程式,也就是在瀏覽器內執行 JavaScript 的應用程式。這類應用程式會使用 JavaScript 存取伺服器資源(通常是 WebAPI),再更新頁面上的資訊。例如 :
Gmail依據使用者點選的收件匣的訊息,更新頁面上顯示的資訊,而不需要整頁重新導向。
流程 :
-
應用程式會把使用者的瀏覽器導向到授權伺服器,
此時應用程式會傳送以下資料到授權伺服器- 回應的類別 : token,表示要回傳存取令牌
- 應用程式ID
- 請求的存取範圍
- 內部狀態
- 重新導向的 URI (授權伺服器要導回來)
-
授權伺服器透過瀏覽器認證使用者,例如 : 使用者登帳號密碼,
確定使用者的許可或是駁回應用程式的申請 -
若使用者許可應用程式存取,授權伺服器會把瀏覽器導回先前指定的URI,
其中會包含存取令牌放在 Fragmet Component(html格式的片段組合)一起回傳 -
瀏覽器依據指定的URI進行導向,但是導向的請求過程中不會包含第三步所夾帶的 Fragment Component,也就是不會有存取令牌,瀏覽器會自己保留 Fragment,例如 :
- Fragment 夾帶存取令牌 : http://example.com/test#access_token=12345
- 未夾帶存取令牌 : http://example.com/test
-
第四步的導向會回傳一個網頁(通常是HTML + Script),瀏覽器會執行這個 Script 就可以解出存取令牌和回傳的參數。這步可以看出來存取令牌會放在瀏覽器,若是瀏覽器有漏洞則令牌可能會外洩。
-
瀏覽器再把存取令牌傳送給應用程式
隱含授權模式整個過程應用程式都不會參與,都是前端在操作,所以只能應用於一些安全性要求不高的場景,並且令牌的有效期限需非常短,通常是 Session 有效,瀏覽器關掉即失效
3. Resource Owner Password Credentials Grant Flow (密碼模式)
使用者自己的帳號密碼會被直接用來當作授權驗證,並傳給授權伺服器以取得存取令牌。而使用者在整個流程中只參與了輸入帳號密碼,無法控制授權流程,所以應用程式可能會取得比使用者預期更高的權限。
在 Spec 中有要求應用程式不能儲存帳號密碼,此帳號密碼只會使用一次以取得長時效的存取令牌或更新授權令牌。
密碼模式適用場景通常是使用者高度依賴的應用程式,例如 : 作業系統內建的應用程式或是官方應用程式,其餘皆不適用。
因牽涉到真實帳號密碼,所以授權伺服器必須可以防止被暴力破解。
流程 :
-
使用者向應用程式提供真正的帳號密碼
-
應用程式向授權伺服器申請存取令牌和更新授權令牌,此時應用程式也須向授權伺服器認證自己
-
授權伺服器認證了應用程式、驗證使用者的帳號密碼,如果都正確的話即核發存取令牌和更新授權令牌
4. Client Credentials Grant Flow (憑證模式)
應用程式可以使用應用程式的ID和密碼直接向授權伺服器申請存取令牌。
憑證模式適用於應用程式本身就是使用者,而應用程式屬於後端應用沒有前端。
流程 :
-
應用程式向授權伺服器認證自己,並要求申請存取令牌
-
授權伺服器認證了應用程式,如果正確即可核發存取令牌
參考
[1] 一次搞懂OAuth與SSO在幹什麼?
[2] OAuth 2.0 的四種方式
[3] OAuth 2.0 筆記 (1) 世界觀