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\'
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
The reason is that your are making request with default scope scope=api%3A%2F%2{client_id}%2F.default
which doesn't include scope claim in the access_token you should use specific scope that you are registered for your ASP.NET Core 3.1 API project when you have exposing that API in the Azure Portal.
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.
Just add DefaultMapInboundClaims to your API service config
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
}