Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IDW10201: Neither scope or roles claim was found in the bearer token

I have a ASP.NET Core 3.1 project like this sample: Sign-in a user with the Microsoft Identity Platform in a WPF Desktop application and call an ASP.NET Core Web API.

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...]

like image 845
1iveowl Avatar asked Oct 04 '20 10:10

1iveowl


People also ask

What is scope JWT?

Scope is a mechanism in OAuth 2.0 to limit an application's access to a user's account. An application can request one or more scopes, this information is then presented to the user in the consent screen, and the access token issued to the application will be limited to the scopes granted.

What is bearer token in Web API?

Bearer token. A particular type of access token, with the property that anyone can use the token. In other words, a client doesn't need a cryptographic key or other secret to use a bearer token. For that reason, bearer tokens should only be used over a HTTPS, and should have relatively short expiration times.

What is scope in Web API?

Scopes enable you to group a set of REST resources, methods, or both, and SOAP operations for an API. A scope consists of a name, description, and zero or more resources, methods, or operations. An API can have zero or more scopes. You can define a set of policies and configure its properties for each individual scope.


2 Answers

This might help if you are planning on not using build in scopes or roles. You can enable "access-control list" authentication using my example for Azure B2C below. Here are some links to the official documentation.

https://github.com/AzureAD/microsoft-identity-web/wiki/web-apis#user-content-web-apis-called-by-daemon-apps-using-client-credential-flow

https://docs.microsoft.com/en-us/dotnet/api/microsoft.identity.web.microsoftidentityoptions.allowwebapitobeauthorizedbyacl?view=azure-dotnet-preview

Add the following to your AD configuartion: "AllowWebApiToBeAuthorizedByACL": true

Example:

"AzureAdB2C": {
    "Instance": "https://xxx.b2clogin.com/",
    "ClientId": "xxxx",
    "Domain": "xxx.onmicrosoft.com",
    "SignUpSignInPolicyId": "xxx",
    "AllowWebApiToBeAuthorizedByACL": true
  },

For what ACL/Access-control list means: ACL: https://en.wikipedia.org/wiki/Access-control_list

like image 73
Endre86 Avatar answered Sep 18 '22 15:09

Endre86


When I received this error, "IDW10202", it was because of this line of code in the Controller.

HttpContext.ValidateAppRole("MyAppRole");

(This was the only result returned by Google, so placing this comment here for anyone's benefit. Apologies if a bit off topic.)

like image 29
RGH Avatar answered Sep 21 '22 15:09

RGH