I have read through countless tutorials, articles and questions.
I cannot figure out why [Authorize(Roles = "SuperAdmin")]
or any other role is not working. Everyone gets 403 regardless of the role specified:
//[Authorize(Roles = "SuperAdmin")] - Commented out to debug roles
public async Task<IActionResult> Index()
{
var userID = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByIdAsync(userID);
var roles = await _userManager.GetRolesAsync(user);
return View();
}
When I debug the user, I can see that they have the role:
But they still get a 403 on any controller that authorizes roles.
Here are the relevant bits of my program.cs:
builder.Services.AddIdentity<TMSUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddClaimsPrincipalFactory<MyUserClaimsPrincipalFactory>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddTokenProvider<DataProtectorTokenProvider<TMSUser>>(TokenOptions.DefaultProvider);
builder.Services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = new PathString("/Home/HandleError/401");
options.LoginPath = new PathString("/Home/Portal");
});
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddControllersWithViews();
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.AddTransient<ITools, Tools>();
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
builder.Services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/Home/HandleError/{0}");
app.UseHsts();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<TMSContext>();
context.Database.EnsureCreated();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
I have changed my code countless times over the last few months trying to get this to work, but now I’m just begging for help. This is very frustrating! What have I missed? Could something else in my code be preventing this from working properly?
Thank you!
4
Answers
I finally came up with a solution for this. It should not have been this complicated! I've used roles in other versions .net without this problem.
I created a custom Authorize Filter and I'm just hitting the db (which I assume all the helpers do as well so not really a big deal):
Part of this I got from this link
First, I created a model with my roles:
Then a custom Authorize Filter:
And I use it like this:
I do like this approach. Its going to allow me to do some other things I wanted to do on a per controller/user level - I just can't believe it was this complicated!
Hope this helps somebody. If there's any glaring issues with this, please let me know. I'm officially balled.
check what you are getting with
User.FindAll(ClaimTypes.Role).ToList()
after that check "SuperAdmin" coming in list or not?, if your issue not resolved than post your
GetRolesAsync
function.its seems like you have to add the
services.AddDefaultIdentity<TMSUser>()
so the complete code would look like
The solution is much simpler than described. If you add both roles by .AddRoles() and custom claims by .AddClaimsPrincipalFactory(); you have to inherit from UserClaimsPrincipalFactory<TUser, TRole> not UserClaimsPrincipalFactory<TUser> which is a default one.
So your code should look: