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房间的钥匙给他」。

参考资料


<<:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 17 - 如何勾选元素

>>:  [Day16] 注册工具Postman – 安装、介绍Postman

[Day 8] SRE - 火炎焱燚之保卫战

火烧起来怎麽办? 当你听到手机有叮咚叮咚,一连串的alert表示服务已无法继续服务,几分钟後电话来了...

IT 铁人赛 k8s 入门30天 -- day20 k8s Logging Architecture

前言 参考来源: https://kubernetes.io/docs/concepts/clust...

[DAY 05] EC2 - 关於储存空间

EC2 的 EBS, EFS, Instance Store EC2 的部分还蛮多与复杂的,在此先纪...

Day14 互动式CSS按钮动画(上)

以下是以此图为例的互动式CSS按钮动画范例: 变深 HTML <div class="...

Day 3 跑一下 Tensorflow 范例

安装 tensorflow-gpu (如果自己电脑没有 GPU 就装 CPU 版) pip inst...