Junx
19 min readDec 29, 2017

OAuth原理與Laravel Passport實作(3)

OAuth 2.0內建四種授權認證流程(Grant Flow)

在OAuth 2.0中所定義的授權流程有四種,這四種不一定都要實作,主要是看你的業務需求而定

參考 Spec 的 1.3. Authorization Grant4. Obtaining Authorization

Authorization Code Grant

是授權認證流程中最嚴謹的,認證的流程主要是Client向Resource Owner請求Authorization Code然後Client拿到Authorization Code再向Authorization Server拿Access Token

PS: AuthorizationGrant內有Authorization Code

參考Spec 4.2.1. Authorization Request

假設角色:

  • 延續OAuth 角色互動關係的定義(系列1)

資料定義:

  • User-Agent通常是指Web Browser
  • Client Identifier是向Authorization Server申請的Client ID
  • Redirection URI代表Authorization Code要回傳的網址,本次情境為圖片編輯網
  • User authenticates代表Resource Owner授權動作
  • Authorization Code就是包含在Authorization Grant的一種字串代碼

步驟:

  • (A)圖片編輯網將小明的User-Agent轉址到Google OAuth認證授權頁,轉址採用Authorization Request請求,定義基本格式參數,如上圖就是Client Identifier與Redirection URI
  • (B)小明在Google OAuth認證授權頁授權權限給圖片編輯網
  • (C)Google OAuth認證授權頁在將小明User-Agent轉回Redirection URI並帶有Authorization Code,圖片編輯網就接收到Authorization Code
  • (D)圖片編輯網再向Google Auth出示Authorization Code與Redirection URI
  • (E)Google Auth認證Authorization Code與Redirection URI需與(A)的Redirection URI一樣後再回傳Access Token與Refresh Token(看業務邏輯)

請求流程:

  • (A)Authorization Request -> (C)Authorization Response
  • (D)Access Token Request -> (E)Access Token Response

適用場景:

  • 需要 User-Agent 通常是可以Redirection(轉址)
  • 可能需要核發Refresh Token
  • 適合 Confidential Clients (需要保密的客戶),通常是部署在Server上的服務

(A)Authorization Request(GET)

由Client發起給Resource Owner,Resource Owner執行授權將Resource Owner的User-Agent轉址到Authorization Endpoint,這個轉址過程就是Authorization Request

User-Agent -> Authorization Endpoint

Authorization Endpoint下一章節會提到,簡單來說Authorization Server有一個服務端是用來接收授權認證的接口

請求參數:

參考Spec 4.1. Authorization Code Grant

response_type要填code

GET /oauth/authorize?client_id=3&redirect_uri=http%3A%2F%2Fclient.oauth.test%2Ftoken&response_type=code&scope=Email&state=1234 HTTP/1.1Host: service.oauth.test

(C)Authorization Response

Authorization Endpoint確認授權完成,將帶有Authorization Grant的User-Agent轉址到Redirection Endpoint,這過程就是Authorization Response

Authorization Endpoint -> User-Agent -> Redirection Endpoint

Redirection Endpoint下一章節會提到,簡單來說Client有一個Redirection Endpoint服務接口專門在接收Authorization Grant

參數:

參考Spec 4.1.2. Authorization Response

Authorization Request發送後的Redirection Endpoint接口

Redirection Endpoint接口接收到Authorization Grant由於Authorization Request有帶state回來時也要判斷是否一樣,防止CSRF並且也會帶回來Authorization Code

GET /token?code=def50200927ae0c85cfb1a99a1394058463b4...&state=1234Host: client.oauth.test

Spec 4.1.2. Authorization Response是用302但是1.7. HTTP Redirections指出不強制使用302,依照各自實作細節決定

(D) Access Token Request(POST)

Client拿到Authorization Grant向Token Endpoint發出請求,這步驟為Access Token Request

Client -> Token Endpoint

Token Endpoint下一章節會提到,簡單來說Authorization Server有一個服務接口專門在接收Authorization Grant並發行Access Token

請求參數:

參考Spec 4.1.3. Access Token Request

Spec 4.1.3. Access Token Request中只提到client_id並未說明要帶client_secret,但是client_id說請參考3.2.1. Client Authentication又參考2.3.1. Client Password確實是要帶client_secret做Client做認證,Laravel這邊的做法也是需要帶client_secret,猜想可能是這個原因

(E)Access Token Response

Authorization Server處理完Token Endpoint接收的Access Token Request請求,認證Authorization Code合法後將Access Token回傳給Client,這個回傳內容就是Access Token Response

Token Endpoint -> Client

參數:

參考Spec 5.1. Successful Response
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
"access_token":"eyJ0eXAiOiJKV1QImI6ImEwZWI2YjY0NWY1OGMzZWU....",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"def5020074acbe6e2d9c855e3873874ff1ff0b04.....",
}

Implicit Grant

在Authorization Code中Client會先取得Authorization Grant再透過Authorization Code向Authorization Server取Access Token,而Implicit Grant會跳過Client取Authorization Grant直接向Authorization Server取Access Token

此流程Access Token是經由Redirection URI傳遞所以存在資料洩漏的風險,所以Resource Owner與其使用的設備是看的到,可以透過事先設定Redirection URI來防止Access Token傳給非認證的網站

Implicit Grant禁止發行Refresh Token

參考Spec 4.2. Implicit Grant

假設角色:

  • 延續OAuth 角色互動關係的定義(系列1)

資料定義:

  • User-Agent通常是指可以Redirection(轉址)
  • Client Identifier是向Authorization Server申請的Client ID
  • Redirection URI代表Access Token要回傳的網址
  • User authenticates代表Resource Owner授權動作
  • Web-Hosted Client Resource代表執行應用程式的Server,如手機本身

步驟:

  • (A)圖片編輯網將小明的User-Agent轉址到Google OAuth認證授權頁,轉址採用Authorization Request請求,定義基本格式參數,如上圖就是Client Identifier與Redirection URI
  • (B)小明在Google OAuth認證授權頁授權權限給圖片編輯網
  • (C)Google OAuth認證授權頁在將小明User-Agent轉回Redirection URI並帶有Access Token,這時Access Token並非保存在Request而是以Fragment方式保存在User-Agent,也就是單純以#串在Redirection URI後面(稍後解釋)
  • (D)此時User-Agent經由Redirection URI會先跑到Web-Hosted Client Resource
  • (E)Web-Hosted Client Resource在將Redirection URI這段網址組成Script(網頁)回傳給User-Agent
  • (F)User-Agent得到Script後解出Access Token
  • (G)Client就拿到Access Token

Fragment就是指Url後面的#,這種Request是取不到資料的,所以才適合應用在JavaScript或是移動裝置應用程式上

http://client.oauth.test/token#access_token=eyJ0eXAiOiJKV1Q

(E)(F)這段組成Script部分我理解的不是很好,但就Laravel來說會在Redirection URI+’#’+Access Token,這是包含在headers一起回傳給Client

phpstorm示意圖

當Redirection URI回http://client.oauth.test/在Laravel中想用Request來取得完整URL在解析出#後的Access Token,這是沒辦法的,因為伺服器不會處理這種請求的參數,所以只能利用JavaScript這類去取得Browser上的URL再來解析取得Access Token

Laravel中配置Implicit Grant流程

切到oauth_service專案

cd oauth_service

到app/Providers/AuthServiceProvider.php加上

Passport::enableImplicitGrant();

response_type要填token

執行http://client.oauth.test/authorize回到/token時Request內並不會包Access Token,所以這個route只能做View的輸出讓JavaScript來幫我們解析Access Token

phpstorm示意圖

在resources/view新增一個implicit_token.blade.php內容為以下

<script>
console.log(location.hash.split('&'));
</script>

將/token route改成以下

Route::get('/token', function (Request $request) {
return view('implicit_token');
});

瀏覽http://client.oauth.test/authorize讓JavaScript取出Access Token

適用場景:

  • 需要 User-Agent 通常是指可以Redirection(轉址)
  • JavaScript應用程式
  • 移動裝置應用程式

請求流程:

  • (A)Authorization Request -> (C)(D)(E)Authorization Response

(A)Authorization Request(GET)

同Authorization Code的(A)Authorization Request

(C)(D)(E)Authorization Response

Authorization Endpoint確認授權完成,將Access Token以Fragment方式串在Redirection URI後面包在User-Agent回傳到Redirection URI,這部分就是Authorization Response

Authorization Endpoint ->User-Agent

參數:同Authorization Code的(E)Access Token Response

Resource Owner Password Credentials Grant

該流程將Resource Owner 的帳號密碼當成Authorization Grant讓Client直接向Authorization Server做認證取Access Token,此流程非常信任Client才適用,像是第三方所開發的應用程式,如內建或官方應用程式,非必要請勿將此流程開發給第三方使用

參考Spec 4.3. Resource Owner Password Credentials Grant

假設角色:

  • 延續OAuth 角色互動關係的定義(系列1)

資料定義:

  • Resource Owner Password Credentials指的是Resource Owner帳密

步驟:

  • (A)圖片編輯網想存取小明的Google雲端圖片所以請小明授權Google OAuth的存取權限,所以小明提供他在Google雲端上的帳密
  • (B)圖片編輯網向Google OAuth提交小明Google雲端上的帳密
  • (C)Google OAuth認證圖片編輯網與小明的帳號為合法則回傳Access Token與Refresh Token(可選)

OAuth原理與Laravel Passport實作(2)中有有執行過以下,這個指令已建置Resource Owner Password Credentials流程的Client ID與secret

php artisan passport:install

每一種Grant Flow都有對應的Client ID與secret,拿Implicit Grant來當作Resource Owner Password Credentials的Client ID與secret是不合法的

更改/token route

grant_type要更改為password

username/password小明的帳密

Authorization更改成Resource Owner Password Credentials的Client ID與secret

瀏覽http://client.oauth.test/token

適用場景:

  • 內建或官方應用程式,需非常信任Client

請求流程:

  • (A)Authorization Request and Response -> (B)Access Token Request
  • (B)Access Token Request -> (C)Access Token Response

(A)Authorization Request and Response

Resource Owner向Client取得帳密,這個段就是Authorization Request

如何向Resource Owner取帳密,屬於自行實作細節

(B)Access Token Request(POST)

Client拿到Resource Owner帳密後向Token Endpoint請求Access Token,這段就是Access Token Request

Client -> Token Endpoint

請求參數:

參考Spec 4.3.2. Access Token Request

(C)Access Token Response

Token Endpoint認證合法後回傳Access Token與Refresh Token(可選),這個回傳就是Access Token Response

Token Endpoint -> Client

參數:同Authorization Code的(E)Access Token Response

Client Credentials Grant

將Client的Client ID與secret當作Authorization Grant向Authorization請求Access Token,意思就是Client自己去請求Access Token時或Protected Resources屬於Client情況

參考Spec 4.4. Client Credentials Grant

假設角色:

  • 延續OAuth 角色互動關係的定義(系列1)

資料定義:

  • Client Authentication指的是Client ID與secret

步驟:

  • (A)圖片編輯網想存取自己放在Google雲端上的圖片,利用之前向Google OAuth註冊時所拿到的帳密向Google OAuth授權存取
  • (B)Google OAuth認證Client ID與secret合法後回傳Access Token與Refresh Token(不建議)

OAuth原理與Laravel Passport實作(2)中有有執行過以下,這個指令已建置Resource Owner Password Credentials流程的Client ID與secret

php artisan passport:install

每一種Grant Flow都有對應的Client ID與secret,拿Implicit Grant來當作Client Credentials的Client ID與secret是不合法的

更改/token route

grant_type要更改為client_credentials

Authorization更改成Client Credentials的Client ID與secret

瀏覽http://client.oauth.test/token

適用場景:

  • Resource Owner就是Client
  • 部署在Server上的應用程式

請求流程:

(A)Access Token Request ->(B)Access Token Response

Authorization Request and Response

因為不存在Resource Owner且Client ID與secret就是Authorization Grant所以不需要此流程

(A)Access Token Request(POST)

Client發送Client ID與secret向Token Endpoint請求Access Token,這動作就是Access Token Request

Client -> Token Endpoint

請求參數:

參考Spec 4.4.2. Access Token Request

(B)Access Token Response

Token Endpoint認證Client ID與secret合法後回傳Access Token與Refresh Token(不建議),回傳這個動作就是Access Token Response

Token Endpoint -> Client

參數:同Authorization Code的(E)Access Token Response

下一篇OAuth原理與Laravel Passport實作(4)