2

I am trying to learn more about Identity Server. I am currently struggling to get Role Based Authorisation working. Here are the steps I have followed:

1) Download the sample solution: https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/6_AspNetIdentity

2) Run the solution, which starts:

a) Identity Server project

b) MVC project

c) API project

3) Browse to the MVC project and apply migrations. 4) Register a new user: Bert@Bert.com 5) Browse to: CallApiUsingUserAccessToken in the MVC project. The API is reached as expected because the user is authorised.

Say I now wanted to change IdentityContoller from this:

[Authorize] 
public class IdentityController : ControllerBase 

to this:

[Authorize(Roles="Admin")] 
public class IdentityController : ControllerBase 

and Home Controller (https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/6_AspNetIdentity/src/MvcClient/Controllers/HomeController.cs) from this:

public async Task<IActionResult> CallApiUsingUserAccessToken()

to this:

[Authorize(Roles="Admin")] 
public async Task<IActionResult> CallApiUsingUserAccessToken()

What changes would I have to make to the configuration?

I have tried a few suggestions this afternoon. For example, in the startup of the MVCClient I tried adding:

options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";

Please assume that I have correctly added the roles to the identity database (and associated the roles with the users).

w0051977
  • 15,099
  • 32
  • 152
  • 329

1 Answers1

5

What you are looking for is the AddProfileService() method where you can add your custom implementation of the IProfileService interface where you can customize the claims to add to the access token.

Here's an example using Identity which adds the role claim to the token

public class ProfileService : IProfileService
{
    protected UserManager<ApplicationUser> _userManager;

    public ProfileService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        var roles = await _userManager.GetRolesAsync(user);
        var claims = new List<Claim>
        {
            new Claim(JwtClaimTypes.Role, roles.Any() ? roles.First() : "Standard")
        };

        context.IssuedClaims.AddRange(claims);
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var user = await _userManager.GetUserAsync(context.Subject);
        context.IsActive = (user != null) && user.LockoutEnabled;
    }
}

Then in the startup, tell idp to use your class

var builder = services.AddIdentityServer(options =>
                {
                    options.Events.RaiseErrorEvents = true;
                    options.Events.RaiseInformationEvents = true;
                    options.Events.RaiseFailureEvents = true;
                    options.Events.RaiseSuccessEvents = true;
                })
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddAspNetIdentity<ApplicationUser>()
                .AddProfileService<ProfileService>();
Marcus Höglund
  • 16,172
  • 11
  • 47
  • 69
  • Does this class and configuration need adding to both the MVC project and the API project? Thanks. – w0051977 Nov 07 '18 at 18:18
  • @w0051977 you only need to add it to the Identity Server project where you store the users. The consumers, which logon via the idp, will get the role from the token and then you will be able to use the role-based authorization on the controllers – Marcus Höglund Nov 07 '18 at 18:22
  • @w0051977 in this class https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/6_AspNetIdentity/src/IdentityServerWithAspNetIdentity/Startup.cs – Marcus Höglund Nov 07 '18 at 18:25
  • There is an error in the Profile Service class: inconsistent accessibility UserManager . Any ideas? – w0051977 Nov 07 '18 at 18:26
  • @w0051977 something with accessibility..Check that both ApplicationUser and UserManager are public mayby? Or the service itself.. – Marcus Höglund Nov 07 '18 at 18:30
  • thanks. It was just the AlpplicationUser class that was not public. I will test your code now. Please standbye. – w0051977 Nov 07 '18 at 18:32
  • That change has made absolutely no difference whatsoever. Could you provide some more detail? Thanks. – w0051977 Nov 07 '18 at 18:37
  • Thanks. Perhaps you could download the project and make the changes yourself? Thanks again. – w0051977 Nov 07 '18 at 18:49
  • @w0051977 if you set a break point in the GetProfileDataAsync method in ProfileService and then login via the mvc client, does it hit? – Marcus Höglund Nov 07 '18 at 18:58
  • No it does not. – w0051977 Nov 07 '18 at 19:01
  • @w0051977 try this https://stackoverflow.com/a/48350508 on the client spec in the idp server – Marcus Höglund Nov 07 '18 at 19:14
  • Tried it - makes no difference. Does it work for you? – w0051977 Nov 07 '18 at 19:18
  • @w0051977 in the mvc-client, last in this file https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/6_AspNetIdentity/src/IdentityServerWithAspNetIdentity/Config.cs, add AlwaysSendClientClaims = true, AlwaysIncludeUserClaimsInIdToken = true – Marcus Höglund Nov 07 '18 at 19:22
  • 1
    @w0051977 here's a better walk-through http://hamidmosalla.com/2017/12/07/policy-based-authorization-using-asp-net-core-2-identityserver4/ – Marcus Höglund Nov 07 '18 at 19:23
  • I tried your first suggestion with no luck. Your second link seems to suggest hardcoding the client credentials into the app. I will try it though. Did you try downloading the app and making your changes? – w0051977 Nov 07 '18 at 19:29
  • @w0051977 Ok, I cloned the same repo. added migration, ran the seed, added the ProfileService and appended it to the AddIdentityServer() in the startup, added AlwaysIncludeUserClaimsInIdToken = true, AlwaysSendClientClaims = true in the mvc client. Then started the IdentityServerWithAspNetIdentity and MvcClient from VS2017. When i then press secure in the MvcClient and logon to the IDP, accept all consents and then the break point in the ProfileService is hit.. – Marcus Höglund Nov 07 '18 at 20:08
  • Thanks again for trying. In that case I will try again. Did the role based authroisation work? – w0051977 Nov 07 '18 at 20:21
  • @w0051977 I stop when I saw that the breakpoint in ProfileService was hit because you said it didn't got hit at all. If it goes through the ProfileSErvice it will add your claims to the token. But, you need to add the roles to the users, the default setup of the project did not have any roles connected to the identity users. – Marcus Höglund Nov 08 '18 at 06:44
  • @Marcus Höglund, I plan to try this again this morning. Is there anything I need to do to get the authorised (Roles="Admin") to work at the web api level or should it work as it is? – w0051977 Nov 08 '18 at 06:56
  • @w0051977 ok, you need to add the Role claim to the token in the ProfileService, Try it hardcoded first as the users in Identitity dont have any roles.. new Claim(JwtClaimTypes.Role, "Admin") – Marcus Höglund Nov 08 '18 at 06:57