授权代码流如何在单页应用程序中工作?

Posted

技术标签:

【中文标题】授权代码流如何在单页应用程序中工作?【英文标题】:How authorization code flow works in single page applications? 【发布时间】:2021-08-13 08:42:08 【问题描述】:

您好,我正在探索一些与 azure Active Directory 相关的身份验证和授权流程。我在单页应用程序中使用了以前的誓言隐式流程。在花时间阅读微软文档之后,我理解了关于隐式流程的以下内容。

隐式流:

单页 javacript 应用程序使用隐式流从 azure 活动目录获取访问令牌。它直接调用令牌端点来获取令牌,因此这使得隐式流的安全性降低。

.Net Web 应用程序中的授权流程

每当我们使用带有授权代码流的 .Net core web mvc 应用程序时,首先会在浏览器中调用授权端点以获取代码。在浏览器中,我们可以看到对授权端点的请求。在请求 url 中,我将响应类型作为代码传递,然后是客户端 ID 并重定向 ui。这里第一次握手发生在浏览器和授权端点之间。此握手将代码返回到重定向 uri。下一部分,应用程序必须向令牌端点发出 POST 请求以获取访问令牌。第一步收到的代码我将发送令牌请求。在这个请求中,我还将包括客户端秘密,也包括重定向 uri。但是,每当我向授权端点发出第一个 GET 请求时,我都不会传递客户端密钥。这是因为在浏览器中暴露秘密并不好。因此,在第二个帖子请求中,我还将包括客户端秘密。获得访问令牌后,我会将其添加到 api 请求标头中以对 api 进行安全调用。 这是我所理解的关于 .Net 核心 Web 应用程序的授权代码流风格。现在我有了另一种关于单页应用程序的授权代码。

React Web App 中的授权代码流程

我有使用 MSAL 库的 SPA 反应应用程序。我已经从 github https://github.com/Azure-Samples/ms-identity-javascript-react-tutorial/tree/main/3-Authorization-II/1-call-api/SPA 克隆了示例应用程序。

每当我运行此应用程序并登录时,第一次调用都会如下所示

 https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https://login.microsoftonline.com/c5ffa990-7e0a-4bf6-6c04-79ab98e05931/oauth2/v2.0/authorize

我正在尝试理解这个请求。我将查询字符串附加到 url authorization_endpoint=https://login.microsoftonline.com/c5ffa990-7e0a-4bf6-6c04-79ab98e05931/oauth2/v2.0/authorize 所以这可用于从授权服务器返回代码。 下一个电话将立即发生https://login.microsoftonline.com/c5ffa990-7e0a-4bf6-6c04-79ab98e05931/oauth2/v2.0/token

要获取访问令牌并在 FormData 部分的请求中,我可以看到以下参数

client_d, redirect_uri,scope,code

在代码中,我看到了一些希望从授权端点收到的代码值。无论如何,这个 api 返回了我 access_token。

现在得出结论,在 .Net 核心 Web 应用程序和 React SPA 应用程序中,我都在使用授权代码流。

    在 .Net 核心授权代码流中,每当尝试获取访问令牌时,我都会使用客户端机密。所有这些都以安全的方式发生在服务器端。在反应中,我也在使用授权代码流,但我没有在任何地方使用客户端秘密。

    在 react 应用程序中,我也发出两个请求,一个用于授权端点获取代码,另一个用于获取令牌。我可以在浏览器本身中看到所有这些,但我怎么能认为这是安全的?

    在 .Net Web 应用程序和 React 应用程序中,这两个应用程序都使用授权代码流,但其行为独立取决于应用程序的类型。

    在网上浏览了几份文件和视频后,我总结自己是

当授权代码流与 .Net core MVC 等服务器端 Web 应用程序一起使用时,它利用 client_secrete 获取访问令牌,此调用将发生在服务器端,因此客户端密钥不会通过浏览器暴露给用户

当授权流程使用没有服务器端支持的 SPA 应用程序时,首先它会调用以获取授权码,然后它会在没有 client_secrete 的情况下发出获取访问令牌的 post 请求。没有客户端秘密的授权码授予的唯一方法是安全的是通过使用“状态”参数并将重定向 URL 限制为受信任的客户端。

所以我的结论是,当我们使用带有授权代码流的服务器端 Web 应用程序时,我们可以使用客户端机密,但在 SPA 的情况下,我们不会使用 client_secrete。

我已经理解了上述概念并解释了我所理解的内容,并且我列出了在 web 应用程序和 spa 应用程序中实现 2 种授权代码流之后遇到的困惑。有人可以帮助我如果我的理解是否正确,如果我的理解是错误的,我到底在哪里理解错了?任何人都可以在这方面帮助我。任何帮助将不胜感激。谢谢

【问题讨论】:

我强烈推荐观看此视频:youtube.com/watch?v=996OiexHze0。 你也可以看看这篇来自 MS 的文章:Sign in users and call the Microsoft Graph API from a JavaScript single-page app (SPA) using auth code flow 谢谢高拉夫。本教程非常棒,我对 .net 网络应用程序的理解是正确的,与 youtube 中的解释相同。但是对于 POST 请求发生的反应 SPA 应用程序呢?他在后端以更安全的方式(反向通道)说,但在我的反应应用程序中,我没有任何后端服务,所以 POST 请求恰好在哪里获取访问令牌? 嗨,Thomas,我已经完成了该应用程序,并且我也在使用类似的应用程序。在您提供的上述链接中,我们没有在任何地方使用 client_secrete。根据授权代码流程,一旦我们获得授权代码,我们就会调用令牌端点以及代码和 client_secrete。这就是授权代码流的工作方式。但是在考虑 SPA 应用程序时,即使在您共享的链接中,我们也不会在任何地方使用 client_secrete。当涉及到服务器端 .net 核心网络应用程序和 SPA 时,这就是我坚持理解的地方。 嗨 Gaurav 和 Thomas,我已经更新了我的问题,所以我最终得出的结论是,当我使用像 .net core mvc 这样的服务器端 Web 应用程序时,我可以使用客户端秘密来获取访问令牌,但如果SPA 我们需要在 post 请求中发送 client_secrete。如果我的所有理解都是正确的,请告诉我。非常感谢 【参考方案1】:

Authcode flow 是一个 OAuth 2.0 工作流程,您可以在任何类型的客户端(Web/移动/SPA)中使用它。 客户端应使用 MSAL 库与 AAD/B2C 进行通信,PKCE 用于通过 S256 加密的代码交换证明密钥 (code_challenge) 保护授权代码授权。

Authcode Grant Flow 规范:

如果您使用的是 B2C,您的入口端点是:

https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/oauth2/v2.0/authorize?
client_id=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6
&response_type=code
&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob
&response_mode=query
&scope=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6%20offline_access
&state=arbitrary_data_you_can_receive_in_the_response
&code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
&code_challenge_method=S256

这将显示 SignIn-SignUp-Social 登录表单。只需使用您在 B2C 中注册的 App ClientId 导航到此 URL。

您还可以查看自定义政策 starter pack,以根据您的需求(声明)调整您的工作流程。

如果您将 response_type=code 更改为 response_type=id_token,您将获得一个令牌,该令牌可用于在所有登录过程之后针对您的受限资源 (API) 进行身份验证。 或者您可以使用对令牌端点的第二次调用来获取它。

令牌端点:

POST https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/oauth2/v2.0/token HTTP/1.1

Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&client_id=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6&scope=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6 offline_access&code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...&redirect_uri=urn:ietf:wg:oauth:2.0:oob&code_verifier=ThisIsntRandomButItNeedsToBe43CharactersLong 

code=XXXXXXXXXXXXX参数是第一次GET请求返回的access_code。

【讨论】:

您好 Junama 感谢您花时间回答。我知道授权代码流程是如何工作的,但是当我进去时,我开始收到很多问题,我在上面发布了。我真的很想有人可以帮助我消除对上述问题的困惑谢谢 @Mr Perfect 这里没有client_secret。只需使用 GET 请求中的参数来获取 access_code,然后在 /token 端点中使用此代码来获取最终令牌以进行身份​​验证。 是的,这是正确的,所以我想在这里理解的是,当我们将身份验证代码流与服务器 Web 应用程序一起使用时,我们可以使用客户端秘密,而当我们使用 SPA 时,我们仍然可以使用身份验证代码没有客户秘密的流动?我的理解对吗? 您的客户端密码是 access_code,不管它是 Web、SPA 还是移动应用程序。更新了答案。

以上是关于授权代码流如何在单页应用程序中工作?的主要内容,如果未能解决你的问题,请参考以下文章

异步编程如何在单线程编程模型中工作?

在单页 Clojure Web 应用程序中使用 Friend 进行身份验证和授权

如何在单页应用程序 (SPA) 中实现 ReCaptcha

如何让 Richfaces 组件在按需加载的弹出窗口中工作?

如何在单页应用程序 (SPA) 的特定页面中隐藏对讲聊天小部件?

如何在单页移动 Web 应用程序中实现我自己的历史堆栈?