Reputation: 5751
My single page application uses OidcTokenManager to connect to an IdentityServer3 STS using implicit flow. The client presents the IDS3 access token to a ASP.NET Core (WebApi) web service as a Bearer Token; the web service application is configured to use IDS3 middleware and restricts access to its methods using an Authorize attribute.
SPA client configuration:
function configureTokenManager() {
console.log("configureTokenManager()");
var config = {
authority: $config.authority,
client_id: "BNRegistry",
redirect_uri: $config.webRoot + "/#/authorised/",
post_logout_redirect_uri: $config.webRoot + "/#/",
response_type: "id_token token",
scope: "openid profile email BNApi",
silent_redirect_uri: $config.webRoot + "/#/renew/",
silent_renew: true,
filter_protocol_claims: false
};
return new OidcTokenManager(config);
};
Scope configuration in STS:
new Scope
{
Name = "BNApi",
DisplayName = "BN Api",
Enabled = true,
Type = ScopeType.Resource,
Claims = new List<ScopeClaim>
{
new ScopeClaim(Constants.ClaimTypes.Name),
new ScopeClaim(Constants.ClaimTypes.Role)
}
}
WebApi configuration:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = Configuration["Authority"],
RequiredScopes = new[] {"BNApi"},
NameClaimType = IdentityModel.JwtClaimTypes.Name,
RoleClaimType = IdentityModel.JwtClaimTypes.Role
});
WebApi method:
[Authorize]
public IActionResult Get()
{
...
}
This works as expected, rejecting an unauthenticated user with a 401. If I examine the claims for the user in the api controller method, (eg. User.Claims.ToList()
), it contains entries for any roles to which the user has been assigned.
However, if I examine the User.Identity.Name
property it is always null, and if I query User.IsInRole("Administrator")
it is always false, even when the user is assigned to that role. Further, if I add a role name to the Authorize
attribute ([Authorize(Role="Administrator")]
), users are rejected with a 401 whether or not they belong to the stated role.
How can I get IdentityServer3 to play nicely with ASP.NET Role authorisation?
Upvotes: 2
Views: 961
Reputation: 3765
Have you tried resetting the InboundClaimTypeMap
?
From the IdentityServer3 documentation page here:
When you inspect the claims on the about page, you will notice two things: some claims have odd long type names and there are more claims than you probably need in your application.
The long claim names come from Microsoft’s JWT handler trying to map some claim types to .NET's ClaimTypes class types.
Unfortunately this mapping ends up breaking the specific claim names you have defined as name
and role
, because their names get transformed and no longer map to what you were expecting. This results in the [Authorize(Roles = "")]
and User.IsInRole("")
not working as expected.
In your API Startup.cs
you should add the following:
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions()
{
...
});
EDIT: The below information was incorrect!. As pointed out by @Paul Taylor "the AlwaysInclude
property ensures that the relevant claim is always present in an identity token (which is used with the client, not the API). This is a resource scope so the property has no effect.". Thanks for helping me understand a little more about how IdentityServer works :-)
For the Name
and Role
claims to be included when accessing the API, you need to specifically mark them as alwaysInclude
in your ScopeClaim
list.
new Scope
{
Name = "BNApi",
DisplayName = "BN Api",
Enabled = true,
Type = ScopeType.Resource,
Claims = new List<ScopeClaim>
{
new ScopeClaim(Constants.ClaimTypes.Name, true), //<-- Add true here
new ScopeClaim(Constants.ClaimTypes.Role, true) // and here!
}
}
Upvotes: 1