title: OpenIddict Roles/Policy returns 403 Forbidden
body: <p>Good morning.  I am having an issue with Authorize in controllers and using Roles and / or Policy set up.  It is always returning a 403 forbidden, with the following in the logs:</p>
  <pre><code>info: OpenIddict.Server.OpenIddictServerDispatcher[0]
    The request address matched a server endpoint: Token.
  info: OpenIddict.Server.OpenIddictServerDispatcher[0]
    The token request was successfully extracted: {
      &quot;grant_type&quot;: &quot;password&quot;,
      &quot;username&quot;: &quot;Administrator@MRM2Inc.com&quot;,
      &quot;password&quot;: &quot;[redacted]&quot;
    }.
  info: OpenIddict.Server.OpenIddictServerDispatcher[0]
    The token request was successfully validated.
  info: OpenIddict.Server.OpenIddictServerDispatcher[0]
    The response was successfully returned as a JSON document: {
      &quot;access_token&quot;: &quot;[redacted]&quot;,
      &quot;token_type&quot;: &quot;Bearer&quot;,
      &quot;expires_in&quot;: 3600
    }.
  info: OpenIddict.Validation.OpenIddictValidationDispatcher[0]
    The response was successfully returned as a challenge response: {
      &quot;error&quot;: &quot;insufficient_access&quot;,
      &quot;error_description&quot;: &quot;The user represented by the token is not allowed to perform the requested action.&quot;,
      &quot;error_uri&quot;: &quot;https://documentation.openiddict.com/errors/ID2095&quot;
    }.
  info: OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandler[13]
    AuthenticationScheme: OpenIddict.Validation.AspNetCore was forbidden.
  </code></pre>
  <p>If I remove the Roles = or Policy = from the Authorize Tag it works.  I have the project setup as follows:</p>
  <p>Startup.cs</p>
  <pre><code> public void ConfigureServices(IServiceCollection services)
      {
          services.AddControllers();
  
          services.AddDbContext&lt;IdentDbContext&gt;(options =&gt;
          {
              options.UseSqlServer(
                  Configuration.GetConnectionString(&quot;IdentityDB&quot;));
  
              options.UseOpenIddict();
          });
          
          // Add the Identity Services we are going to be using the Application Users and the Application Roles
          services.AddIdentity&lt;ApplicationUsers, ApplicationRoles&gt;(config =&gt;
          {
              config.SignIn.RequireConfirmedEmail = true;
              config.SignIn.RequireConfirmedAccount = true;
              config.User.RequireUniqueEmail = true;
              config.Lockout.MaxFailedAccessAttempts = 3;
          }).AddEntityFrameworkStores&lt;IdentDbContext&gt;()
          .AddUserStore&lt;ApplicationUserStore&gt;()
          .AddRoleStore&lt;ApplicationRoleStore&gt;()
          .AddRoleManager&lt;ApplicationRoleManager&gt;()
          .AddUserManager&lt;ApplicationUserManager&gt;()
          .AddErrorDescriber&lt;ApplicationIdentityErrorDescriber&gt;()
          .AddDefaultTokenProviders()
          .AddDefaultUI();
  
          services.AddDataLibrary();
  
          // Configure Identity to use the same JWT claims as OpenIddict instead
          // of the legacy WS-Federation claims it uses by default (ClaimTypes),
          // which saves you from doing the mapping in your authorization controller.
          services.Configure&lt;IdentityOptions&gt;(options =&gt;
          {
              options.ClaimsIdentity.UserNameClaimType = Claims.Name;
              options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
              options.ClaimsIdentity.RoleClaimType = Claims.Role;
          });
  
          services.AddAuthentication(options =&gt;
          {
              options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
          });
  
          // Add in the email
          var emailConfig = Configuration.GetSection(&quot;EmailConfiguration&quot;).Get&lt;EmailConfiguration&gt;();
          services.AddSingleton(emailConfig);
          services.AddEmailLibrary();
  
          services.AddAuthorization(option =&gt;
          {
              option.AddPolicy(&quot;SiteAdmin&quot;, policy =&gt; policy.RequireClaim(&quot;Site Administrator&quot;));
          });
  
          services.AddOpenIddict()
              // Register the OpenIddict core components.
              .AddCore(options =&gt;
              {
                  // Configure OpenIddict to use the Entity Framework Core stores and models.
                  // Note: call ReplaceDefaultEntities() to replace the default entities.
                  options.UseEntityFrameworkCore()
                  .UseDbContext&lt;IdentDbContext&gt;()
                  /*.ReplaceDefaultEntities&lt;Guid&gt;()*/;
              })
              // Register the OpenIddict server components.
              .AddServer(options =&gt;
              {
                  // Enable the token endpoint.  What other endpoints?
                  options.SetLogoutEndpointUris(&quot;/api/LogoutPost&quot;)
                  .SetTokenEndpointUris(&quot;/Token&quot;);
  
                  // Enable the client credentials flow.  Which flow do I need?
                  options.AllowPasswordFlow();
  
                  options.AcceptAnonymousClients();
  
                  options.DisableAccessTokenEncryption();
  
                  // Register the signing and encryption credentials.
                  options.AddDevelopmentEncryptionCertificate()
                        .AddDevelopmentSigningCertificate();
  
                  // Register the ASP.NET Core host and configure the ASP.NET Core options.
                  options.UseAspNetCore()
                         .EnableLogoutEndpointPassthrough()
                         .EnableTokenEndpointPassthrough();
              })
              // Register the OpenIddict validation components.
              .AddValidation(options =&gt;
              {
                  // Import the configuration from the local OpenIddict server instance.
                  options.UseLocalServer();
  
                  // Register the ASP.NET Core host.
                  options.UseAspNetCore();
              });            
  
          // Register the Swagger generator, defining 1 or more Swagger documents
          services.AddSwaggerGen(swagger =&gt; 
          {
              swagger.AddSecurityDefinition(&quot;Bearer&quot;, new OpenApiSecurityScheme()
              {
                  Name = &quot;Authorization&quot;,
                  Type = SecuritySchemeType.Http,
                  Scheme = &quot;Bearer&quot;,
                  BearerFormat = &quot;JWT&quot;,
                  In = ParameterLocation.Header,
                  Description = &quot;JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer'[space] and then your token in the text input below.\r\n\r\nExample: \&quot;Bearer 12345abcdef\&quot;&quot;
              });
              swagger.AddSecurityRequirement(new OpenApiSecurityRequirement
              {
                  {
                      new OpenApiSecurityScheme
                      {
                          Reference = new OpenApiReference
                          {
                              Type = ReferenceType.SecurityScheme,
                              Id = &quot;Bearer&quot;
                          }
                      },
                      new string[] {}
                  }
              });
              swagger.OperationFilter&lt;SwaggerDefaultValues&gt;();
              swagger.OperationFilter&lt;AuthenticationRequirementOperationFilter&gt;();
  
              // Set the comments path for the Swagger JSON and UI.
              var xmlFile = $&quot;{Assembly.GetExecutingAssembly().GetName().Name}.xml&quot;;
              var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
              swagger.IncludeXmlComments(xmlPath);
          });
          services.AddApiVersioning();
          services.AddVersionedApiExplorer(options =&gt;
          {
              options.GroupNameFormat = &quot;'v'VVVV&quot;;
              options.DefaultApiVersion = ApiVersion.Parse(&quot;0.6.alpha&quot;);
              options.AssumeDefaultVersionWhenUnspecified = true;
          });
          services.AddTransient&lt;IConfigureOptions&lt;SwaggerGenOptions&gt;, ConfigureSwaggerOptions&gt;();
      }
  
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
          else
          {
              app.UseExceptionHandler(&quot;/Error&quot;);
              // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
              app.UseHsts();
          }
  
          app.UseHttpsRedirection();
          app.UseStaticFiles();
  
          // Enable middleware to serve generated Swagger as a JSON endpoint.
          app.UseSwagger();
  
          // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
          // specifying the Swagger JSON endpoint.
          app.UseSwaggerUI(c =&gt;
          {               
              c.DisplayOperationId();
              var versionDescription = provider.ApiVersionDescriptions;
              foreach (var description in provider.ApiVersionDescriptions.OrderByDescending(_ =&gt; _.ApiVersion))
              {
                  c.SwaggerEndpoint($&quot;/swagger/{description.GroupName}/swagger.json&quot;, $&quot;MRM2 Identity API {description.GroupName}&quot;);
              }
          });
  
          app.UseRouting();
  
          app.UseAuthentication();
  
          app.UseAuthorization();
  
          app.UseEndpoints(endpoints =&gt;
          {
              endpoints.MapControllers();
          });
      }
  </code></pre>
  <p>AuthorizationController.cs</p>
  <pre><code>/// &lt;summary&gt;
  /// Controls the Authorization aspects of the API
  /// &lt;/summary&gt;
  [Route(&quot;api/[controller]/[action]&quot;)]
  [ApiController]
  [ApiVersion(&quot;0.8.alpha&quot;)]
  [Produces(MediaTypeNames.Application.Json)]
  public class AuthorizationController : ControllerBase
  {
      private readonly IConfiguration _configuration;
      private readonly IdentDbContext _context;
      private readonly ApplicationUserManager _userManager;
      private readonly ApplicationRoleManager _roleManager;
      private readonly IOpenIddictApplicationManager _applicationManager;
      private readonly IOpenIddictAuthorizationManager _authorizationManager;
      private readonly IOpenIddictScopeManager _scopeManager;
      private readonly SignInManager&lt;ApplicationUsers&gt; _signInManager;
      private HttpClient _client;
  
      public AuthorizationController(IConfiguration configuration, IdentDbContext context, ApplicationUserManager userManager, 
          ApplicationRoleManager roleManager, IOpenIddictApplicationManager applicationManager, IOpenIddictAuthorizationManager authorizationManager,
          IOpenIddictScopeManager scopeManager, SignInManager&lt;ApplicationUsers&gt; signInManager)
      {            
          _configuration = configuration;
          _context = context;
          _userManager = userManager;
          _roleManager = roleManager;
          _applicationManager = applicationManager;
          _authorizationManager = authorizationManager;
          _scopeManager = scopeManager;
          _signInManager = signInManager;
      }
  
      [HttpPost(&quot;/token&quot;), Produces(&quot;application/json&quot;)]
      public async Task&lt;IActionResult&gt; Exchange()
      {
          var request = HttpContext.GetOpenIddictServerRequest() ??
              throw new InvalidOperationException(&quot;The OpenID Connect request cannot be retrieved.&quot;);
  
          ClaimsPrincipal claimsPrincipal;
  
          if (request.IsPasswordGrantType())
          {
              var user = await _userManager.FindByNameAsync(request.Username);
              var roleList = await _userManager.GetRolesListAsync(user);
              var databaseList = await _userManager.GetDatabasesAsync(user);
              string symKey = _configuration[&quot;Jwt:Symmetrical:Key&quot;];
              string jwtSub = _configuration[&quot;Jwt:Subject&quot;];
              string issuer = _configuration[&quot;Jwt:Issuer&quot;];
              string audience = _configuration[&quot;Jwt:Audience&quot;];
  
              var claims = new List&lt;Claim&gt;
              {
                  new Claim(JwtRegisteredClaimNames.Sub, jwtSub, issuer),
                  new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(), issuer),
                  new Claim(ClaimTypes.Name, user.UserName, issuer)
              };
  
              foreach (var role in roleList)
              {
                  claims.Add(new Claim(ClaimTypes.Role, role.Name));
              }
  
              foreach (var database in databaseList)
              {
                  claims.Add(new Claim(type: &quot;DatabaseName&quot;, database));
              }
              var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
              identity.AddClaim(OpenIddictConstants.Claims.Name, user.UserName, OpenIddictConstants.Destinations.AccessToken);
              identity.AddClaim(OpenIddictConstants.Claims.Subject, jwtSub, OpenIddictConstants.Destinations.AccessToken);
              identity.AddClaim(OpenIddictConstants.Claims.Audience, audience, OpenIddictConstants.Destinations.AccessToken);
              foreach (var cl in claims)
              {
                  identity.AddClaim(cl.Type, cl.Value);       
              }
  
              claimsPrincipal = new ClaimsPrincipal(identity);
  
              // Set the list of scopes granted to the client application.
              claimsPrincipal.SetScopes(new[]
              {
                  Scopes.OpenId,
                  Scopes.Email,
                  Scopes.Profile,
                  Scopes.Roles
              }.Intersect(request.GetScopes()));
  
              foreach (var claim in claimsPrincipal.Claims)
              {
                  claim.SetDestinations(GetDestinations(claim, claimsPrincipal));
              }
              return SignIn(claimsPrincipal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
          }
  
          if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType())
          {
              // Retrieve the claims principal stored in the authorization code/device code/refresh token.
              var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
  
              // Retrieve the user profile corresponding to the authorization code/refresh token.
              // Note: if you want to automatically invalidate the authorization code/refresh token
              // when the user password/roles change, use the following line instead:
              // var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
              var user = await _userManager.GetUserAsync(principal);
              if (user == null)
              {
                  return Forbid(
                      authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                      properties: new AuthenticationProperties(new Dictionary&lt;string, string&gt;
                      {
                          [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                          [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = &quot;The token is no longer valid.&quot;
                      }));
              }
  
              // Ensure the user is still allowed to sign in.
              if (!await _signInManager.CanSignInAsync(user))
              {
                  return Forbid(
                      authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                      properties: new AuthenticationProperties(new Dictionary&lt;string, string&gt;
                      {
                          [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                          [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = &quot;The user is no longer allowed to sign in.&quot;
                      }));
              }
  
              foreach (var claim in principal.Claims)
              {
                  claim.SetDestinations(GetDestinations(claim, principal));
              }
  
              // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
              return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
          }
  
          throw new InvalidOperationException(&quot;The specified grant type is not supported.&quot;);
      }
  
      private IEnumerable&lt;string&gt; GetDestinations(Claim claim, ClaimsPrincipal principal)
      {
          // Note: by default, claims are NOT automatically included in the access and identity tokens.
          // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
          // whether they should be included in access tokens, in identity tokens or in both.
  
          switch (claim.Type)
          {
              case Claims.Name:
                  yield return Destinations.AccessToken;
  
                  if (principal.HasScope(Scopes.Profile))
                      yield return Destinations.IdentityToken;
  
                  yield break;
  
              case Claims.Email:
                  yield return Destinations.AccessToken;
  
                  if (principal.HasScope(Scopes.Email))
                      yield return Destinations.IdentityToken;
  
                  yield break;
  
              case Claims.Role:
                  yield return Destinations.AccessToken;
  
                  if (principal.HasScope(Scopes.Roles))
                      yield return Destinations.IdentityToken;
  
                  yield break;
  
              // Never include the security stamp in the access and identity tokens, as it's a secret value.
              case &quot;AspNet.Identity.SecurityStamp&quot;: yield break;
  
              default:
                  yield return Destinations.AccessToken;
                  yield break;
          }
      }
      
  }
  </code></pre>
  <p>RolesController.cs</p>
  <pre><code>/// &lt;summary&gt;
  /// Controls the actions for roles within the API
  /// &lt;/summary&gt;
  /// &lt;response code=&quot;401&quot;&gt;If the user did not login correctly or 
  /// does not have the correct permissions&lt;/response&gt;
  [Route(&quot;api/[controller]&quot;)]
  [ApiController]
  [ApiVersion(&quot;0.8.alpha&quot;)]
  [Produces(MediaTypeNames.Application.Json)]
  [Consumes(MediaTypeNames.Application.Json)]
  [ProducesResponseType(StatusCodes.Status401Unauthorized)]
  [Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme, Roles = &quot;Site Administrator&quot;)]  //If I change this to Policy = &quot;SiteAdmin&quot; still does not work.  If I remove the Roles completely it works.
  public class RolesController : ControllerBase
  {
      private readonly ApplicationRoleManager _roleManager;
      private readonly ILogger&lt;RolesController&gt; _logger;
      private readonly IApplicationDatabaseData _databaseData;
  
      public RolesController(ApplicationRoleManager roleManager, ILogger&lt;RolesController&gt; logger, IApplicationDatabaseData databaseData)
      {
          _roleManager = roleManager;
          _logger = logger;
          _databaseData = databaseData;
      }
  
      /// &lt;summary&gt;
      /// Gets a List of all the Roles
      /// &lt;/summary&gt;
      /// &lt;returns&gt;A list of ApplicationRoles&lt;/returns&gt;
      /// &lt;response code=&quot;200&quot;&gt;Returns the list&lt;/response&gt;
      /// &lt;response code=&quot;404&quot;&gt;If the list is empty&lt;/response&gt;
      [HttpGet(&quot;ListRoles&quot;, Name = nameof(ListRolesAsync))]
      [Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme/*, Roles = &quot;Site Administrator&quot;*/)]  //Currently commented out until Roles work.
      [ProducesResponseType(StatusCodes.Status200OK)]
      [ProducesResponseType(StatusCodes.Status404NotFound)]
      public async Task&lt;ActionResult&lt;IList&lt;ApplicationRoles&gt;&gt;&gt; ListRolesAsync()
      {
          var roles = await _roleManager.GetAllRoles();
          if (!roles.Any())
          {
              return NotFound();
          }
          else
          {
              var output = roles;
              return Ok(output);
          }            
      }        
  }
  </code></pre>
  <p>What I noticed that when stepping through this in debugging except for the Claims.Name when going through GetDestinations all claims hit the default switch case.  So I am not sure where in the startup or authorizationcontoller I went wrong.  But I am pretty sure my issue is within there.
  What have I missed to get my Roles and / or Policy to work correctly within the Controllers?</p>