title: How to add roles to claims in IdentityServer4?
body: <p>I am new to IdentityServer and I have been struggling with this issue all day. So much so that I'm almost about to give up on this. I know this question has been asked over and over again and I have tried many different solutions but none seem to work. Hopefully you can help me push me in the right direction with this.</p>
  
  <p>First I installed the IdentityServer4 templates by running <code>dotnet new -i identityserver4.templates</code> and created a new project with the is4aspid template by running <code>dotnet new is4aspid -o IdentityServer</code>.</p>
  
  <p>After that i created a new IdentityServer database and ran the migrations. By that time I had a the default Identity database structure.</p>
  
  <p>In Config.cs I changed <code>MVC client</code> to the following:</p>
  
  <pre><code>new Client
  {
      ClientId = "mvc",
      ClientName = "MVC Client",
  
      AllowedGrantTypes = GrantTypes.Implicit,
      ClientSecrets = { new Secret("47C2A9E1-6A76-3A19-F3C0-S37763QB36D9".Sha256()) },
  
      RedirectUris = { "https://localhost:44307/signin-oidc" },
      FrontChannelLogoutUri = "https://localhost:44307/signout-oidc",
      PostLogoutRedirectUris = { "https://localhost:44307/signout-callback-oidc" },
  
      AllowOfflineAccess = true,
      AllowedScopes = { "openid", "profile", "api1", JwtClaimTypes.Role }                
  },
  </code></pre>
  
  <p>And changed the <code>GetApis</code> method to this:</p>
  
  <pre><code>public static IEnumerable&lt;ApiResource&gt; GetApis()
  {
      return new ApiResource[]
      {
          new ApiResource("api1", "My API #1", new List&lt;string&gt;() { "role" })
      };
  }
  </code></pre>
  
  <p>There where of course no users in the database yet so i added a registration form and registered two dummy users, one with the username <code>admin@example.com</code> and one with the username <code>subscriber@example.com</code>.</p>
  
  <p>To assign the roles to these user I created the following method in Startup.cs.</p>
  
  <pre><code>private async Task CreateUserRoles(IServiceProvider serviceProvider) {
      var RoleManager = serviceProvider.GetRequiredService&lt;RoleManager&lt;IdentityRole&gt;&gt;();
      var UserManager = serviceProvider.GetRequiredService&lt;UserManager&lt;ApplicationUser&gt;&gt;();
  
      IdentityResult adminRoleResult;
      IdentityResult subscriberRoleResult;
  
      bool adminRoleExists = await RoleManager.RoleExistsAsync("Admin");
      bool subscriberRoleExists = await RoleManager.RoleExistsAsync("Subscriber");
  
      if (!adminRoleExists) {
          adminRoleResult = await RoleManager.CreateAsync(new IdentityRole("Admin"));
      }
  
      if(!subscriberRoleExists) {
          subscriberRoleResult = await RoleManager.CreateAsync(new IdentityRole("Subscriber"));
      }
  
      ApplicationUser userToMakeAdmin = await UserManager.FindByNameAsync("admin@example.com");
      await UserManager.AddToRoleAsync(userToMakeAdmin, "Admin");
  
      ApplicationUser userToMakeSubscriber = await UserManager.FindByNameAsync("subscriber@example.com");
      await UserManager.AddToRoleAsync(userToMakeSubscriber, "Subscriber");
  }
  </code></pre>
  
  <p>In the <code>Configure</code> method of the same class I add the the parameter <code>IServiceProvider services</code> and called the above method like so: <code>CreateUserRoles(services).Wait();</code>. By this time my database did have two roles in it.</p>
  
  <p>Next I created a new solution (within the same project) and in the Startup.cs file of that solution I added the following in the <code>ConfigureServices</code> method.</p>
  
  <pre><code>    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
  
      services.AddAuthentication(options =&gt;
      {
          options.DefaultScheme = "Cookies";
          options.DefaultChallengeScheme = "oidc";
      })
          .AddCookie("Cookies")
          .AddOpenIdConnect("oidc", options =&gt; {
              options.SaveTokens = true;
              options.ClientId = "mvc";
              options.ClientSecret = "32D7A7W0-0ALN-2Q44-A1H4-A37990NN83BP";
              options.RequireHttpsMetadata = false;
              options.Authority = "http://localhost:5000/";
              options.ClaimActions.MapJsonKey("role", "role");
          });
  </code></pre>
  
  <p>After that I added <code>app.UseAuthentication();</code> in the <code>Configure</code> method of the same class.</p>
  
  <p>Then I created a new page with the following if statements.</p>
  
  <pre><code>if(User.Identity.IsAuthenticated) {
   &lt;div&gt;Yes, user is authenticated&lt;/div&gt;
  } 
  
  if(User.IsInRole("ADMIN")) {
   &lt;div&gt;Yes, user is admin&lt;/div&gt;
  }
  </code></pre>
  
  <p>I logged in with <code>admin@example.com</code> but the second if statement returns <code>False</code>. I inspected all the claims by looping over them like so.</p>
  
  <pre><code>@foreach (var claim in User.Claims) {
      &lt;dt&gt;@claim.Type&lt;/dt&gt;
      &lt;dd&gt;@claim.Value&lt;/dd&gt;
  }
  </code></pre>
  
  <p>But there was no role claim to be found, only sid, sub, idp, preferred_username and name. </p>
  
  <p>I tried to get the role in there so that the second if statement returns True but after trying and trying I have not yet been able to make it work. Can someone see what I have to do in order to make this work? I am an absolute beginner in IdentityServer4 and trying my best to understand it. Any help will be appreciated. Thanks in advance!</p>
  
  <p><strong>EDIT 1:</strong></p>
  
  <p>Thanks to <a href="https://github.com/IdentityServer/IdentityServer4/issues/1786" rel="noreferrer">this question</a> and <a href="https://stackoverflow.com/questions/53976553/identityserver4-role-based-authorization-for-web-api-with-asp-net-core-identity/53977681#53977681">this question</a> I got the feeling that I'm on the right track. I have made some modifications but I still can not get it to work. I just tried the following.</p>
  
  <ol>
  <li>Created a new ProfileService class in my IdentityServer project with the following content.</li>
  </ol>
  
  <pre><code>public class MyProfileService : IProfileService {
   public MyProfileService() { }
   public Task GetProfileDataAsync(ProfileDataRequestContext context) {
    var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
    List&lt;string&gt; list = context.RequestedClaimTypes.ToList();
    context.IssuedClaims.AddRange(roleClaims);
    return Task.CompletedTask;
   }
  
   public Task IsActiveAsync(IsActiveContext context) {
    return Task.CompletedTask;
   }
  }
  </code></pre>
  
  <p>Next I registered this class in the ConfigureServices method by adding the line <code>services.AddTransient&lt;IProfileService, MyProfileService&gt;();</code>. After that I added a new a new line to the GetIdentityResources method, which looks like this now.</p>
  
  <pre><code>public static IEnumerable&lt;IdentityResource&gt; GetIdentityResources()
  {
      return new IdentityResource[]
      {
          new IdentityResources.OpenId(),
          new IdentityResources.Profile(),
          new IdentityResource("roles", new[] { "role" })
  };
  }
  </code></pre>
  
  <p>I also added the roles to my Mvc client like so: <code>AllowedScopes = { "openid", "profile", "api1", "roles" }</code>.</p>
  
  <p>Next I switched over to the other project and added the following lines in the .AddOpenIdConnect oidc. </p>
  
  <pre><code>options.ClaimActions.MapJsonKey("role", "role", "role");
  options.TokenValidationParameters.RoleClaimType = "role";
  </code></pre>
  
  <p>But still, I cannot get it to work like I want it to. Anyone knows what I am missing?</p>