IDW10201:在不记名令牌中找不到范围或角色声明
IDW10201: Neither scope or roles claim was found in the bearer token
我有一个 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}
请求 returns 一个 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 核心 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”的视频表明缺少的部分是需要在 startup.cs
中设置的标志 JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
- 例如:
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...]
原因是您正在使用默认范围 scope=api%3A%2F%2{client_id}%2F.default
发出请求,其中不包括 access_token 中的范围声明,您应该使用您为 ASP.NET 注册的特定范围] 核心 3.1 API 项目,当您在 Azure 门户中公开该 API 时。
视频“Implementing Authorization in your Applications with Microsoft identity platform - june 2020”概述了缺少的部分是需要在 startup.cs
中设置的标志 JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
- 例如:
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
// By default, the claims mapping will map claim 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;
// Notice that this part is different in the video,
// however in this context the following seems to be
// the correct way of setting the RoleClaimType:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
// The claim in the Jwt token where App roles are available.
options.TokenValidationParameters.RoleClaimType = "roles";
});
[... more code ...]
}
选项 1
也可以像这样在startup.cs
中为整个应用程序设置授权:
services.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("roles", "access_as_application")
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
备选方案 2
也可以使用这样的策略:
services.AddAuthorization(config =>
{
config.AddPolicy("Role", policy =>
policy.RequireClaim("roles", "access_as_application"));
});
现在可以将此策略用于这样的控制器请求:
[HttpGet]
[Authorize(Policy = "Role")]
public async Task<string> Get()
{
return "Hello world!";
}
文档中有更多内容:Policy based role checks。
只需将 DefaultMapInboundClaims 添加到您的 API 服务配置中
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
}
如果您计划不使用内置范围或角色,这可能会有所帮助。您可以使用下面的 Azure B2C 示例启用“访问控制列表”身份验证。这里有一些官方文档的链接。
将以下内容添加到您的 AD 配置中:
"AllowWebApiToBeAuthorizedByACL": true
示例:
"AzureAdB2C": {
"Instance": "https://xxx.b2clogin.com/",
"ClientId": "xxxx",
"Domain": "xxx.onmicrosoft.com",
"SignUpSignInPolicyId": "xxx",
"AllowWebApiToBeAuthorizedByACL": true
},
ACL/Access-control 列表的含义: ACL:https://en.wikipedia.org/wiki/Access-control_list
当我收到此错误“IDW10202”时,这是因为控制器中的这行代码。
HttpContext.ValidateAppRole("MyAppRole");
(这是 Google 返回的唯一结果,因此将此评论放在这里是为了任何人的利益。如果有点偏离主题,我们深表歉意。)