IDW10201:在承载令牌中未找到范围或角色声明 [英] IDW10201: Neither scope or roles claim was found in the bearer token
问题描述
我有一个类似此示例的ASP.NET Core 3.1项目: 身份网站
1.0版和Azure AD单租户应用程序.
由于我只请求一个应用程序令牌,而不是一个用户令牌,所以我已经编辑了添加 appRoles
的清单:
<代码> [...更多json ...]"appId":< guid>","appRoles":[{"allowedMemberTypes":[应用"],描述":访问该应用"."displayName":"access_as_application","id":< unique guid>","isEnabled":是的,"lang":null,"origin":"Application",值":"access_as_application"}],"oauth2AllowUrlPathMatching":否,[...更多json ...]
我还启用了 idtyp
访问令牌声明,以指定这是一个应用程序令牌.
<代码> [...更多json ...]"optionalClaims":{"idToken":[],"accessToken":[{名称":"idtyp","source":null,"essential":否,"additionalProperties":[]}],"saml2Token":[][...更多json ...]
以下请求是由邮递员提出的.请注意将 请求返回一个 我注意到上面的令牌不包含 对此请求的响应是 ASP.NET Core 3.1.托管自定义API的项目有一个 该项目的 ...,控制器如下所示: 更新: (有关更多详细信息,请参见下面的答案). "使用Microsoft身份平台在应用程序中实施授权的视频-2020年6月/.default
与范围一起使用,该范围在文档中相对于 POST/{tenant_id}/oauth2/v2.0/token HTTP/1.1主机:login.microsoftonline.com内容类型:application/x-www-form-urlencodedscope = 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":"[...]",孩子":"[...]"}.{"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":"[..]",角色":["access_as_application"],"sub":< guid>","tid":< guid>","uti":"[...]","ver":"1.0"}
scp
.这似乎是正确的,因为这是应用程序令牌而不是用户令牌.相反,它包括"roles"作为应用程序令牌的适当名称. access_token
现在可以用作Postman Get中的载体:
GET/api/myapi主机:https://localhost:5001授权:承载{access_token}
500内部错误
.IE.出了点问题. access_token
看起来像是一个适当的应用程序令牌,因此错误似乎出在ASP.NET Core 3.1控制器端. startup.cs
,其中包含以下代码:
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);//添加此代码仅是为了突出显示异常的来源.services.Configure< JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme,options =>{var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;options.Events.OnTokenValidated =异步上下文=>{如果(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)){//这是异常发生的地方:抛出新的UnauthorizedAccessException(在承载令牌中找不到作用域或角色声明.");}};});
appsettings.json
包括:
"AzureAD":{实例":"https://login.microsoftonline.com/",域":"mydomain.onmicrosoft.com","ClientId":< client_id>","TenantId":< tenant_id>",受众":"api://< client_id>"},
<代码> [授权][Route("api/[controller]"))]公共类MyApiController:控制器{[HttpGet]公共异步Task< string>得到(){返回"Hello world!";}}
500内部错误
的根本原因是引发了此异常: IDW10201:在承载令牌中未找到范围或角色声明.
异常.> JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
,需要在 startup.cs
中设置-例如:
public void ConfigureServices(IServiceCollection服务){//默认情况下,声明映射将以旧格式映射clain名称,以适应旧的SAML应用程序.//'http://schemas.microsodt.com/ws/2008/06/identity/clains/role'而不是'roles'//此标志确保ClaimsIdentity声明集合将根据令牌中的声明进行构建JwtSecurityTokenHandler.DefaultMapInboundClaims = false;[...更多代码...]
视频"实施使用Microsoft身份平台在您的应用程序中进行授权-2020年6月"概述缺少的部分是此标志 JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
,需要在 startup.cs
中设置-例如:
public void ConfigureServices(IServiceCollection服务){services.AddMicrosoftIdentityWebApiAuthentication(Configuration);//默认情况下,声明映射将以旧格式映射声明名称,以适应较早的SAML应用程序.//'http://schemas.microsodt.com/ws/2008/06/identity/clains/role'而不是'roles'//此标志确保ClaimsIdentity声明集合将根据令牌中的声明进行构建JwtSecurityTokenHandler.DefaultMapInboundClaims = false;//请注意,该部分在视频中有所不同,//但是在这种情况下,以下内容似乎是//设置RoleClaimType的正确方法:services.Configure< JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme,options =>{//Jwt令牌中具有应用角色的声明.options.TokenValidationParameters.RoleClaimType =角色";});[...更多代码...]}
替代1
还可以在 startup.cs
中为整个应用设置授权,例如:
services.AddControllers(options =>{var policy = new AuthorizationPolicyBuilder().RequireClaim(角色","access_as_application").建造();options.Filters.Add(new AuthorizeFilter(policy));});
替代2
也可以使用以下策略:
services.AddAuthorization(config =>{config.AddPolicy("Role",policy =>policy.RequireClaim(角色","access_as_application")));});
现在此策略可用于类似这样的控制器请求:
<代码> [HttpGet][Authorize(Policy ="Role"))公共异步Task< string>得到(){返回"Hello world!";}
I'm using Identity web
version 1.0 and Azure AD, single-tenant application.
I've edited the manifest adding appRoles
since I'm requesting an application token only, and not a user token:
[... 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 ...]
I've also enabled the idtyp
access token claim, to specify that this is an application token.:
[... more json ...]
"optionalClaims": {
"idToken": [],
"accessToken": [
{
"name": "idtyp",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"saml2Token": []
[... more json ...]
The following request is made with Postman. Please notice the use of /.default
with the scope, which is mentioned in the documentation in relation to the 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}
The request returns an access_token
which can be viewed with jwt.ms and looks like this, where actual data have been replaced by placeholders for security reasons.:
{
"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"
}
I notice that the token above does not include scp
. This seem correct as this is an application token and not a user token. Instead it includes `"roles"´ as appropiate for an application token.
The access_token
can now be used as bearer in a Postman Get:
GET /api/myapi
Host: https://localhost:5001
Authorization: Bearer {access_token}
The reponse to this request is 500 internal error
. I.e. something is wrong. The access_token
looks like a corrent application token, so the error seems to be on the ASP.NET Core 3.1 controller side.
The ASP.NET Core 3.1. project hosting the custom API, has a startup.cs
which includes the following code:
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.");
}
};
});
The appsettings.json
for the project includes:
"AzureAD": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain.onmicrosoft.com",
"ClientId": "<client_id>",
"TenantId": "<tenant_id>",
"Audience": "api://<client_id>"
},
... and the controller looks like this:
[Authorize]
[Route("api/[controller]")]
public class MyApiController : Controller
{
[HttpGet]
public async Task<string> Get()
{
return "Hello world!";
}
}
The underlying cause of the 500 internal error
is that this exception is thrown: IDW10201: Neither scope or roles claim was found in the bearer token.
exception.
UPDATE:
(Please see the answer below for even more details).
This video on "Implementing Authorization in your Applications with Microsoft identity platform - june 2020" suggests that the missing piece is this flag JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
which need to be set in startup.cs
- e.g:
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...]
The video "Implementing Authorization in your Applications with Microsoft identity platform - june 2020" outlines that the missing piece is this flag JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
which need to be set in startup.cs
- e.g:
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 ...]
}
Alternative 1
It is also possible to set authorization for the whole app like this in startup.cs
:
services.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("roles", "access_as_application")
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
Alternative 2
It is also possible to use a policy like this:
services.AddAuthorization(config =>
{
config.AddPolicy("Role", policy =>
policy.RequireClaim("roles", "access_as_application"));
});
Now this policy can be used on a controller request like this:
[HttpGet]
[Authorize(Policy = "Role")]
public async Task<string> Get()
{
return "Hello world!";
}
More in the documentation: Policy based role checks.
这篇关于IDW10201:在承载令牌中未找到范围或角色声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!