【发布时间】:2021-01-19 10:01:28
【问题描述】:
我有一个类似以下示例的 ASP.NET Core 3.1 项目:Sign-in a user with the Microsoft Identity Platform in a WPF Desktop application and call an ASP.NET Core Web API。
我正在使用 Identity web 1.0 版和 Azure AD,单租户应用程序。
我已经编辑了添加 appRoles 的清单,因为我只请求应用程序令牌,而不是用户令牌:
[... more json ...]
"appId": "<guid>",
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Accesses the application.",
"displayName": "access_as_application",
"id": "<unique guid>",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "access_as_application"
}
],
"oauth2AllowUrlPathMatching": false,
[... more json ...]
我还启用了idtyp 访问令牌声明,以指定这是一个应用程序令牌。:
[... more json ...]
"optionalClaims": {
"idToken": [],
"accessToken": [
{
"name": "idtyp",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"saml2Token": []
[... more json ...]
以下请求是通过 Postman 发出的。请注意/.default 与范围的使用,在与client credentials grant flow 相关的文档中提到。
POST /{tenant_id}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
scope=api%3A%2F%2{client_id}%2F.default
&client_id={client_id}
&grant_type=client_credentials
&client_secret={secret_key}
请求返回一个access_token,可以用jwt.ms查看,看起来像这样,出于安全原因,实际数据已被占位符替换。:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "[...]",
"kid": "[...]"
}.{
"aud": "api://<client_id>",
"iss": "https://sts.windows.net/<tenant_id>/",
"iat": 1601803439,
"nbf": 1601803439,
"exp": 1601807339,
"aio": "[...]==",
"appid": "<app id>",
"appidacr": "1",
"idp": "https://sts.windows.net/<tenant_id>/",
"idtyp": "app",
"oid": "<guid>",
"rh": "[..].",
"roles": [
"access_as_application"
],
"sub": "<guid>",
"tid": "<guid>",
"uti": "[...]",
"ver": "1.0"
}
我注意到上面的令牌不包括scp。这似乎是正确的,因为这是应用程序令牌而不是用户令牌。取而代之的是,它包含“角色”,作为应用程序令牌。
access_token 现在可以在 Postman Get 中用作承载:
GET /api/myapi
Host: https://localhost:5001
Authorization: Bearer {access_token}
对此请求的响应是500 internal error。 IE。出了点问题。 access_token 看起来像一个正确的应用程序令牌,因此错误似乎出在 ASP.NET Core 3.1 控制器端。
ASP.NET Core 3.1。托管自定义 API 的项目,有一个 startup.cs,其中包括以下代码:
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
// This is added for the sole purpose to highlight the origin of the exception.
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
if (context.Principal.Claims.All(x => x.Type != ClaimConstants.Scope)
&& context.Principal.Claims.All(y => y.Type != ClaimConstants.Scp)
&& context.Principal.Claims.All(y => y.Type != ClaimConstants.Roles))
{
// This where the exception originates from:
throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token.");
}
};
});
该项目的appsettings.json 包括:
"AzureAD": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain.onmicrosoft.com",
"ClientId": "<client_id>",
"TenantId": "<tenant_id>",
"Audience": "api://<client_id>"
},
...控制器看起来像这样:
[Authorize]
[Route("api/[controller]")]
public class MyApiController : Controller
{
[HttpGet]
public async Task<string> Get()
{
return "Hello world!";
}
}
500 internal error的根本原因是抛出了这个异常:IDW10201: Neither scope or roles claim was found in the bearer token.异常。
更新:
(有关更多详细信息,请参阅下面的答案)。
“Implementing Authorization in your Applications with Microsoft identity platform - june 2020”上的这个视频表明缺少的部分是这个标志JwtSecurityTokenHandler.DefaultMapInboundClaims = false;,需要在startup.cs 中设置 - 例如:
public void ConfigureServices(IServiceCollection services)
{
// By default, the claims mapping will map clain names in the old format to accommodate older SAML applications.
//'http://schemas.microsodt.com/ws/2008/06/identity/clains/role' instead of 'roles'
// This flag ensures that the ClaimsIdentity claims collection will be build from the claims in the token
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
[...more code...]
【问题讨论】:
-
使用像 wireshark 或 fiddler 这样的嗅探器,比较工作的 Postman 和不工作的 c#。首先检查正在使用的 TLS 版本。如果它们相同,则比较第一个请求中的标头。使 c# 看起来像 Postman 结果。
-
不确定这有什么帮助?邮递员不工作。我只使用邮递员。问题不在电线上。问题似乎是 access_token 缺少一些信息。
-
检查 TLS 版本。您可能会收到错误,因为您使用的是 TLS 1.0/1.1。业界 5 年前因为安全问题决定淘汰 TLS 1.0/1.1。今年 6 月,微软推出了一项安全更新,以禁用服务器上的 TLS 1.//1.1。所以客户端现在必须请求 TLS 1.2/1.3。由于您没有指定 TLS 版本,因此它默认为您使用的 Net 版本和您正在使用的 Windows 版本。还要确保您使用的是最新的 API。旧的 APi 可能正在使用旧版本的 TLS。
-
我可以确认通信使用的是 TLS 1.2,但我仍然不明白为什么这很重要,因为 Postman 和我的 ASP.NET Core 3.1 api 之间的通信都在我的本地 Windows 上运行10 bld 19042.541,工作得很好。问题不在网络上。
标签: c# asp.net-core oauth-2.0 azure-active-directory microsoft-identity-platform