title: Role Based Authorization for Web API with IdentityServer4
body: <p>I am using IdentityServer4 (v2.2.1) with .Net Core 2.0 and Asp.Net Core Identity.
  I have three projects in my Solution. </p>
  
  <ol>
  <li>IdentityServer</li>
  <li>Web API</li>
  <li>MVC Web Application </li>
  </ol>
  
  <p>I am trying to implement Role Based Authorization on my Web API so that any client will pass an Access Token to Web API to access resources.</p>
  
  <p>Currently i can implement Roles bases authorization on MVC Application controller but i cannot pass/Configure the same for WEB API Controller.</p>
  
  <p>Below are the Identity Server Files:
  Config.cs</p>
  
  <pre><code>public static IEnumerable&lt;ApiResource&gt; GetApiResources()
      {
          return new List&lt;ApiResource&gt;
          {
              //SCOPE - Resource to be protected by IDS 
              new ApiResource("TCSAPI", "TCS API")
              {
                  UserClaims = { "role" }
              }
          };
      }
  
  
   public static IEnumerable&lt;Client&gt; GetClients()
      {
          return new List&lt;Client&gt;
          {
  new Client
              {
                  ClientId = "TCSIdentity",
                  ClientName = "TCS Mvc Client Application .",
                  AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                  RequireConsent = false,
                  ClientSecrets =
                  {
                      new Secret("secret".Sha256())
                  },
  
                  RedirectUris = { "http://localhost:5002/signin-oidc" },
                  PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
  
                  AlwaysSendClientClaims= true,
                  AlwaysIncludeUserClaimsInIdToken = true,
  
                  AllowedScopes =
                  {
                      IdentityServerConstants.StandardScopes.OpenId,
                      IdentityServerConstants.StandardScopes.Email,
                      IdentityServerConstants.StandardScopes.Profile,
                      IdentityServerConstants.StandardScopes.OfflineAccess,
                      "TCSAPI",
                      "office",
                      "role",
                  },
                  AllowOfflineAccess = true
              }
    };
      }
  
  public static IEnumerable&lt;IdentityResource&gt; GetIdentityResources()
      {
          return new IdentityResource[]
          {
              new IdentityResources.OpenId(),
              new IdentityResources.Profile(),
              new IdentityResources.Email(),
              new IdentityResource
              {
                  Name = "role",
                  DisplayName="User Role",
                  Description="The application can see your role.",
                  UserClaims = new[]{JwtClaimTypes.Role,ClaimTypes.Role},
                  ShowInDiscoveryDocument = true,
                  Required=true,
                  Emphasize = true
              }
          };
      }
  </code></pre>
  
  <p>Startup.cs</p>
  
  <pre><code> public void ConfigureServices(IServiceCollection services)
      {
          services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
              options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  
          services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;()
              .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()
              .AddDefaultTokenProviders();
  
          // Add application services.
          services.AddTransient&lt;IEmailSender, EmailSender&gt;();
  
          services.AddMvc();
  
          // configure identity server with in-memory stores, keys, clients and scopes
          services.AddIdentityServer()
              .AddDeveloperSigningCredential()
              .AddInMemoryPersistedGrants()
              .AddInMemoryIdentityResources(Config.GetIdentityResources())
              .AddInMemoryApiResources(Config.GetApiResources())
              .AddInMemoryClients(Config.GetClients())
              .AddAspNetIdentity&lt;ApplicationUser&gt;();
      }
  </code></pre>
  
  <p>MVC WEB APP (Roles Base Authorization works well for MVC WEB APP):</p>
  
  <p>RoleClaimAction.cs 
  using this file to Add roles to Identity.</p>
  
  <pre><code>internal class RoleClaimAction : ClaimAction
  {
      public RoleClaimAction()
          : base("role", ClaimValueTypes.String)
      {
      }
  
      public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
      {
          var tokens = userData.SelectTokens("role");
          IEnumerable&lt;string&gt; roles;
  
          foreach (var token in tokens)
          {
              if (token is JArray)
              {
                  var jarray = token as JArray;
                  roles = jarray.Values&lt;string&gt;();
              }
              else
                  roles = new string[] { token.Value&lt;string&gt;() };
  
              foreach (var role in roles)
              {
                  Claim claim = new Claim("role", role, ValueType, issuer);
                  if (!identity.HasClaim(c =&gt; c.Subject == claim.Subject
                                           &amp;&amp; c.Value == claim.Value))
                  {
                      identity.AddClaim(claim);
                  }
              }
          }
      }
  } 
  </code></pre>
  
  <p>MVC WEB APP/Startup.cs</p>
  
  <pre><code> public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvc();
  
          JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
          services.AddCors();
          services.AddAuthentication(options =&gt;
          {
              options.DefaultScheme = "Cookies";
              options.DefaultChallengeScheme = "oidc";
          })
              .AddCookie("Cookies")
              .AddOpenIdConnect("oidc", options =&gt;
              {
                  options.SignInScheme = "Cookies";
                  options.Authority = "http://localhost:5000";
                  options.RequireHttpsMetadata = false;
  
                  options.ClientId = "TCSIdentity";
  
                  //HYBRID FLOW
                  options.ClientSecret = "secret";
  
                  options.ClaimActions.Add(new RoleClaimAction()); // &lt;-- 
  
                  options.ResponseType = "code id_token token";
                  options.GetClaimsFromUserInfoEndpoint = true;
                  options.Scope.Add("TCSAPI");
                  options.Scope.Add("offline_access");
                  //END HYBRID FLOW
                  options.SaveTokens = true;
                  options.Scope.Add("role");
  
                  options.TokenValidationParameters.NameClaimType = "name";
                  options.TokenValidationParameters.RoleClaimType = "role";
              });
  
      }
  </code></pre>
  
  <p>MVC WEB APP/HomeController.cs
  this action methos works well with role base authorization but when when i try to pass Token to Web Api to access anythings with Role Base Authorization it cannot authorize. e.g. 
  var content = await client.GetStringAsync("<a href="http://localhost:5001/user" rel="noreferrer">http://localhost:5001/user</a>");</p>
  
  <pre><code>[Authorize(Roles = "User")]
      [Route("user")]
      public async Task&lt;IActionResult&gt; UserAccess()
      {
  
          var tokenClient = new TokenClient("http://localhost:5000/connect/token", "RoleApi", "secret");
          var tokenResponse = await tokenClient.RequestClientCredentialsAsync("TCSAPI");
  
          var client = new HttpClient();
          client.SetBearerToken(tokenResponse.AccessToken);
          var content = await client.GetStringAsync("http://localhost:5001/user");
  
          ViewBag.Json = JArray.Parse(content).ToString();
          return View("json");
      }
      [Authorize(Roles = "Admin")]
      [Route("admin")]
      public async Task&lt;IActionResult&gt; AdminAccess()
      {
          var accessToken = await HttpContext.GetTokenAsync("id_token");
  
          var client = new HttpClient();
          client.SetBearerToken(accessToken);
          var content = await client.GetStringAsync("http://localhost:5001/admin");
          ViewBag.Json = JArray.Parse(content).ToString();
          return View("json");
      }
  </code></pre>
  
  <p>WEBAPI/Startup.cs</p>
  
  <pre><code> public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvcCore()
              .AddAuthorization()
              .AddJsonFormatters();
          services.AddAuthentication("Bearer")
              .AddIdentityServerAuthentication(options =&gt;
              {
                  options.Authority = "http://localhost:5000";
                  options.RequireHttpsMetadata = false;
                  options.ApiName = "TCSAPI";
              });
  
          services.AddCors(options =&gt;
          {
              options.AddPolicy("default", policy =&gt;
              {
                  policy.WithOrigins("http://localhost:5002")
                  .AllowAnyHeader()
                  .AllowAnyMethod();
              });
          });
      }
  </code></pre>
  
  <p>WEB API/TestController.cs</p>
  
  <pre><code>[Route("admin")]
      [Authorize(Roles = "Admin")]
      public IActionResult AdminAccess()
      {
          return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
      }
      [Route("user")]
      [Authorize(Roles = "User")]
      public IActionResult UserAccess()
      {
          return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
      }
      [AllowAnonymous]
      [Route("public")]
      public IActionResult PublicAccess()
      {
          return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
      }
  </code></pre>