I have a controller that I want to restrict only to a specific role, let's say admin
. After setting a user with the admin
role, I can validate that he's on that role using the IsInRoleAsync
method (which returns true). When setting the attribute with [Authorize(Roles = "admin")]
I get a 404 with that very same user . I'm using bearer tokens (I don't think that is relevant but anyway) and here's what I've done to try debugging:
Controller w/o [Authorize]
: the resource is returned. [OK]
Controller with [Authorize]
: the resource is returned only when I use the Authentication: Bearer [access token]
[OK]
Controller with [Authorize(Roles = "admin")]
: even after logging in with the user that has the role set, I get the 404 [NOK]
I don't know if I'm missing some configuration, but here's my ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOpenIddict();
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddOpenIddict(opt =>
{
opt.AddEntityFrameworkCoreStores<ApplicationDbContext>();
opt.AddMvcBinders();
opt.EnableTokenEndpoint("/api/token");
opt.AllowPasswordFlow();
opt.DisableHttpsRequirement(); //for dev only!
opt.UseJsonWebTokens();
opt.AddEphemeralSigningKey();
opt.AllowRefreshTokenFlow();
opt.SetAccessTokenLifetime(TimeSpan.FromMinutes(5));
});
services.AddAuthentication(options =>
{
options.DefaultScheme = OAuthValidationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = OAuthValidationConstants.Schemes.Bearer;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddJwtBearer(options =>
{
options.Authority = "http://localhost:44337/";
options.Audience = "resource_server";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role
};
});
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
// User settings
options.User.RequireUniqueEmail = true;
// Add application services.
options.ClaimsIdentity.UserNameClaimType= OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
services.AddSingleton(typeof(RoleManager<ApplicationUser>));
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
You likely get a 404 response because Identity - which is automatically configured as the default authentication, sign-in/sign-out and challenge/forbidden scheme by
services.AddIdentity()
- tries to redirect you to the "access denied page" (Account/AccessDenied
by default), that probably doesn't exist in your application.Try to override the default challenge/forbidden scheme to see if it fixes your issue:
To fix your second issue, make sure the JWT claims mapping feature is disabled. If it's not, the JWT handler will "convert" all your
role
claims toClaimTypes.Role
, which won't work as you configured it to userole
as the role claim used byClaimsPrincipal.IsInRole(...)
(RoleClaimType = OpenIdConnectConstants.Claims.Role
).