iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
Software Development

用Keycloak學習身份驗證與授權系列 第 16

Day15 - 【概念篇】OAuth flows: 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)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

Authorization Code是在RFC6749第一個提到的流程,所以有時又被視爲 標準流程(Standard Flow)

它與前兩個流程很不一樣,分成 前端通訊(frontchannel)後端通訊(Backchannel) 。不過,其實反倒是前兩個是所有模式裡的怪胎,在隱含模式下,後端通信並在前端通訊;在密碼模式下,根本不存在前端通信,資源擁有者需要高度信任客戶端(說穿了在前端通信下,資源擁有者也是高度信賴瀏覽器或代理(User-Agent))。

我們在「Flow這一小段路上路前注意事項」將整個部分拆分成資源擁有者/取用者、前端客戶端(瀏覽器/User-Agent)、後端客戶端、驗證/授權伺服器、資源伺服器。在Code模式下,前四者會參予授權流程。 前端通訊 指的會是 前端客戶端(瀏覽器/User-Agent)授權伺服器(Authorization Server) 交換訊息的過程; 後端通訊 指的會是 後端客戶端(Client)授權伺服器(Authorization Server) 交換訊息的過程。

透過OAuth.Tools和Curl來理解Code模式

透過OAuth.Tools完成前端通訊

接著同樣開起OAuth.Tools的頁面。選擇 Demo: Code Flow ,並記得將環境改成所設置的Keycloak-quick-start。(你可能會注意到Redirect URI不同了。這次Callback不直接取得存取權杖,而是取得特殊密碼。將這個密碼交給真的的客戶端,在與授權伺服器取得存取權杖)

接著主要檢查 clientid 是否正確爲oauth_tools;scope是否爲openid profile roles即可。另外,因爲在設定時我們將所有可以啓用的都啓用了,所以這邊還需要多給與一個client_secret。這與Client Credentials模式有關,現在只需要簡單按下Re-import client from environment(如果有的話)。

同樣按下Run會進入到登入畫面登入。

登入結束後瀏覽器同樣會導向所指定的Redirect URI。通常也就是在發送一個Request給Web伺服器,這個伺服器就是提供真正服務的客戶端。

其中最重要的部分是透過query參數傳遞的code。這個 code 會給實際的客戶端向授權伺服器換取存取權杖。

但由於使用Redirect URI傳遞出去給的客戶端,在OAuth.Tools,但授權伺服器卻是架在私人網域環境下的,故客戶端無法與授權伺服器通訊。所以接著要使用Curl來模擬後端通訊。

雖然有很多技術可以使自架的Keycloak允許在網際網路上被找到,其中包含ngrok等服務。一部分是我不太想再多花篇幅說明,另一部分是整個Environment的設定還需要再調整。

現在可以複製畫面上的命令。

不過爲了使結果更好看一些,會在使用到Python。在你的Terminal環境下輸入以下內容(<paste-command>改爲複製的內容)

python -m json.tool <(<paste-command>)

以這次的例子會下以下命令

python -m json.tool <(curl -Ss -X POST \
http://localhost:8080/auth/realms/quick-start/protocol/openid-connect/token \
-H 'Authorization: Basic b2F1dGhfdG9vbHM6N2QwODcwMTMtNDA1OS00Y2RjLTgxNGMtZTkyNWMxNTk2ZDI0' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code&redirect_uri=https%3A%2F%2Foauth.tools%2Fcallback%2Fcode&code=107f2c47-7b45-4d85-9e01-d6814c4249a3.ea2ac574-62f9-4134-a32c-0a83a2101e54.8ff37bae-a560-423c-926e-9637816fee51')

如此一來會到以下結果

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4VXh6WGR4UWpFNDNIZGdYbXJkUjBQZWxXN1ZoZWowbGRkR2NhN0VubXpZIn0.eyJleHAiOjE2MzE4MzI0MjIsImlhdCI6MTYzMTgzMjEyMiwiYXV0aF90aW1lIjoxNjMxODMxMjYwLCJqdGkiOiJjODVjMTlmYy0yYjNkLTQ1YTUtODhmMi0zODk4YmE3NTM1ZDciLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvcXVpY2stc3RhcnQiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMzQ1YjFiY2EtOTgwNS00YjRiLWEwZjgtZGEyYzcwMTc2YzU5IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoib2F1dGhfdG9vbHMiLCJzZXNzaW9uX3N0YXRlIjoiZWEyYWM1NzQtNjJmOS00MTM0LWEzMmMtMGE4M2EyMTAxZTU0IiwiYWNyIjoiMCIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwczovL29hdXRoLnRvb2xzIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLXF1aWNrLXN0YXJ0Iiwib2ZmbGluZV9hY2Nlc3MiLCJxdWljay1zdGFydC1leGFtcGxlLXJvbGUxIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiZWEyYWM1NzQtNjJmOS00MTM0LWEzMmMtMGE4M2EyMTAxZTU0Iiwid2Vic2l0ZSI6Imh0dHBzOi8vYm9iLmlkIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImdlbmRlciI6Im1hbiIsIm5hbWUiOiJCb2IgTGVlIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYm9iIiwiZ2l2ZW5fbmFtZSI6IkJvYiIsImZhbWlseV9uYW1lIjoiTGVlIiwiZW1haWwiOiJib2JAZmFrZS5lbWFpbCJ9.KmP3mkz8x9n1lpdxV4OPrRK7qNmR25j78forgGN_NdBFFOzXfC8lISPBCWOi4zMC8hPWq8SHlaHTtEcpTusn3ISxyt3wRPmArGTSUfIJF0MEB_VGk-ElG5STIJRF_lPE00y07cRbMWpxiX4SM0mFUM4dQJ-mPDMKbFnsglQlmQ_ATnTZYXvPR4i0zprdMxsoVvoNjb-T67N_lEAIx1-u86fCTRpwdDhM011jiUfrwKfCcMKit8WsXssahsACGkrpTsf3kSi-ur6Rra55cTsQlmuq3QLSdYfN7FIH9rqy373MhmEOLxSFoj05XKl5J-Y9j40QJNlR_LxGhdvtDy8JAw",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0NjUwNDBkYi1lNGJkLTRiYTYtOWM2Ny02ZWYxZGJmMmUxOWYifQ.eyJleHAiOjE2MzE4MzM5MjIsImlhdCI6MTYzMTgzMjEyMiwianRpIjoiYzJjMjVhZmYtZDgzYS00YmNjLTliNjEtN2Y3ODVlOGE3ODVjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3F1aWNrLXN0YXJ0IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL3F1aWNrLXN0YXJ0Iiwic3ViIjoiMzQ1YjFiY2EtOTgwNS00YjRiLWEwZjgtZGEyYzcwMTc2YzU5IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Im9hdXRoX3Rvb2xzIiwic2Vzc2lvbl9zdGF0ZSI6ImVhMmFjNTc0LTYyZjktNDEzNC1hMzJjLTBhODNhMjEwMWU1NCIsInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiJlYTJhYzU3NC02MmY5LTQxMzQtYTMyYy0wYTgzYTIxMDFlNTQifQ.sgbYimIA_TuV65eVywUQlAsJwG0c0x6y1b4gd7l6lss",
    "token_type": "Bearer",
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4VXh6WGR4UWpFNDNIZGdYbXJkUjBQZWxXN1ZoZWowbGRkR2NhN0VubXpZIn0.eyJleHAiOjE2MzE4MzI0MjIsImlhdCI6MTYzMTgzMjEyMiwiYXV0aF90aW1lIjoxNjMxODMxMjYwLCJqdGkiOiIyMDY2MWU3YS1kYzRiLTRhZDctOTk3Ny00NjVhNGE5MzQwNzgiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvcXVpY2stc3RhcnQiLCJhdWQiOiJvYXV0aF90b29scyIsInN1YiI6IjM0NWIxYmNhLTk4MDUtNGI0Yi1hMGY4LWRhMmM3MDE3NmM1OSIsInR5cCI6IklEIiwiYXpwIjoib2F1dGhfdG9vbHMiLCJzZXNzaW9uX3N0YXRlIjoiZWEyYWM1NzQtNjJmOS00MTM0LWEzMmMtMGE4M2EyMTAxZTU0IiwiYXRfaGFzaCI6Ilp1VUhXOVRtbG5HVkdHSU11bDVOOVEiLCJhY3IiOiIwIiwic2lkIjoiZWEyYWM1NzQtNjJmOS00MTM0LWEzMmMtMGE4M2EyMTAxZTU0Iiwid2Vic2l0ZSI6Imh0dHBzOi8vYm9iLmlkIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImdlbmRlciI6Im1hbiIsIm5hbWUiOiJCb2IgTGVlIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYm9iIiwiZ2l2ZW5fbmFtZSI6IkJvYiIsImZhbWlseV9uYW1lIjoiTGVlIiwiZW1haWwiOiJib2JAZmFrZS5lbWFpbCJ9.YI-VWA2wzLGBF9o_w-QzA--lodfB0T618JZnb7PJW-9wszJg2k5Dw_YJnE-4TojsMTRY5PSc9RNJ5wUDVk70_chHJnFq2iZc1MpTRNbK3dLywm63hAkXnhj1chmcXvn7AcJwfZNVw9177eg4R0lt8KycfgzFPCbr0iOrSlw7-9adbEGjWlAuuZMJloiW_9Fpyje-JTmVISyx33JsKR34p2aVSS1ybns7ccY6kpNLdw2Nc6WrYKSLE3V1SjrnbRbooHiZ6xQYTBY5Zl5epWRXH55f3Yl4mPE7161XFuD6cct2QpETfxS21IPVmsRDmmcNzA1kr-4nf9j5FLXOCurFBQ",
    "not-before-policy": 1631743594,
    "session_state": "ea2ac574-62f9-4134-a32c-0a83a2101e54",
    "scope": "openid email profile"
}

拿到存取權杖~並且之後的過程,除了使用存取權杖取得資源外,還有可能透過更新權杖(refresh_token)更新存取權杖,也就是之後的過程就不再需要資源擁有者參予。

結語

這個模式開始很像跟之前提過 特殊密碼 (深入OAuth 2.0) 很像。在資源擁有者驗證身份登入後,向系統申請一個特殊密碼--code,這個密碼有時效性,且只能使用一次。並告訴授權伺服器,要是有人在規定時間內,使用這個特殊密碼來要鑰匙,就給他特定房間的鑰匙。

接著,把這個特殊密碼給與向要授權的對象。回到豪宅管家的比喻,現在你變成這個豪宅的擁有者,並留給管家一個特殊密語 -- 阿里巴巴,芝麻開門 。並告訴管家:「在接下來的24小時內,第一個跟你說這句話的人,就把A房間的鑰匙給他」。

參考資料


上一篇
Day14 - 【概念篇】OAuth flows: Implicit (Legacy)
下一篇
Day16 - 【概念篇】OAuth flows: Refresh Token
系列文
用Keycloak學習身份驗證與授權40

尚未有邦友留言

立即登入留言