我有一个 SPA,用户将使用授权代码流使用 Microsoft Entra 登录。客户端是公开的,因此我按照Microsoft 誓言代码流程文档将我的应用程序注册为 SPA 客户端。假设我在这个项目中没有 API,因为我想使用公共流,并且不需要 API,因为我们使用 PKCE 而不是客户端密钥。
获取代币:
我正在生成 PKCE(挑战和验证者),然后从
请求授权代码https://login.microsoftonline.com/[my tenant id]/oauth2/v2.0/authorize
正在发送
这给了我 GET 请求 URL:
https://login.microsoftonline.com/[my tenant id]/oauth2/v2.0/authorize?client_id=[my client id]&response_type=code&redirect_uri=[url-encoded redirect uri]&response_mode=query&scope=.default&code_challenge=[generated challenge]&code_challenge_method=S256
到目前为止一切顺利。我在给定的 URL 处收到授权码。现在我想用获得的身份验证代码交换令牌,在令牌端点发送 POST 请求:
this.http.post(
'https://login.microsoftonline.com/[my tenant id]/oauth2/v2.0/token',
{
client_id: '[my client id]',
scope: '.default',
code: '[received auth code]',
redirect_uri: 'https://[my domain]/ms-auth-response',
grant_type: 'authorization_code',
code_verifier: '[code verifier matching the challenge],
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
});
现在我面临的问题是登录端点令人惊讶地返回 CORS 错误,指出跨源请求是
文档中有关于此类错误的通知:
但是我的客户注册为 SPA 并且清单是正确的:
现在,如果我从 POSTMAN 发送代码和验证程序,手动将“origin”标头设置到我的域 - 微软端点将返回令牌。
没有原始标头,正如预期的那样,“为‘单页应用程序’客户端类型发行的令牌只能通过跨源请求兑换。”返回错误。
所以 MS 告诉我 - 你不能使用跨源请求,但你必须使用跨源请求?
我试图从 microsoft github 甚至 MSAL 库中查看一些示例,但 npm 甚至不会安装这些模块,因为它们要么已被弃用,要么正在使用一些旧的节点版本(14-15 左右)。
我使用隐式grand和代码流集成了许多OAuth2 SSO,包括谷歌,但我从未遇到过像这里这样的问题。为什么它可以从原点设置为我的域的邮递员工作,但不能从浏览器中的域本身工作?我已经检查了 chrome 工具,并且发送到令牌端点的所有标头都已正确设置(来源、引用等)
有人遇到过类似的问题吗?我不想改变流程或将confidentail web api客户端注册为整个过程中的中间方,因为不需要它。
关于如何处理这些 CORS 错误的一些建议,或者也许还有其他关于使用 Microsoft 登录进行 oauth 的信息?
正如评论中CBroe所指出的,错误是由于请求编码错误引起的。我假设 Angular 的 http 客户端会通过查看 headers 来做到这一点(这就是 Postman 正在做的事情 - 这就是为什么它在那里工作得很好)。使用
URLSearchParams
显式编码对象立即解决了问题:
const encodedBody = new URLSearchParams({
client_id: '[my client id]',
scope: '.default',
code: '[received auth code]',
redirect_uri: 'https://[my domain]/ms-auth-response',
grant_type: 'authorization_code',
code_verifier: '[code verifier matching the challenge],
});
this.http.post(
'https://login.microsoftonline.com/[my tenant id]/oauth2/v2.0/token',
encodedBody.toString(),
{
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded',
}
}
);