I've implemented Identity Scaffold on my project and Authentication works like a charm, I watched some tutorials of how to implement Roles with Identity and I'm having a hard time with the Role Policies labels on the controllers.
They all tell me I'm not authorized.
This is my startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityCore<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI();
services.AddAuthorization(options =>
{
options.AddPolicy("AdminAccess", policy => policy.RequireRole("Admin"));
options.AddPolicy("ManagerAccess", policy =>
policy.RequireAssertion(context =>
context.User.IsInRole("Admin")
|| context.User.IsInRole("Manager")));
options.AddPolicy("UserAccess", policy =>
policy.RequireAssertion(context =>
context.User.IsInRole("Admin")
|| context.User.IsInRole("Manager")
|| context.User.IsInRole("User")));
});
services.AddTransient<IEmailSender, EmailSender>(i =>
new EmailSender(
Configuration["EmailSender:Host"],
Configuration.GetValue<int>("EmailSender:Port"),
Configuration.GetValue<bool>("EmailSender:EnableSSL"),
Configuration["EmailSender:UserName"],
Configuration["EmialSender:Password"]
)
);
services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.AddControllersWithViews();
services.AddRazorPages().AddRazorRuntimeCompilation();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// 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();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Charts}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
I have a PolicyController.cs
public class PolicyController : Controller
{
public IActionResult Index() => View();
[Authorize(Policy = "UserAccess")]
public IActionResult UserPage() => View();
[Authorize(Policy = "ManagerAccess")]
public IActionResult ManagerPage() => View();
//[Authorize(Policy = "AdminAccess")]
public IActionResult AdminPage()
{
// This returns FALSE
if (User.IsInRole("Admin"))
ViewBag.Message = "You Admin";
ViewBag.Message = "No Admin";
return View();
}
}
I also have a view where I create roles and link them to a user, I even lookup at my DB and I see the RoleID with the UserID created on the aspnet-user-roles table but I'm not able to get on those test views with my created roles which are exactly typed as in the startup.cs
[HttpPost]
public async Task<IActionResult> UpdateUserRole(UpdateUserRoleViewModel vm)
{
var user = await _userManager.FindByEmailAsync(vm.UserEmail);
if (vm.Delete)
await _userManager.RemoveFromRoleAsync(user, vm.Role);
else
await _userManager.AddToRoleAsync(user, vm.Role);
return RedirectToAction("Index");
}
What am I doing wrong?
services.AddIdentity<IdentityUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); services.AddIdentityCore<ApplicationUser>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultUI();
Perhaps the issue is related to the above code, from the code, it seems that you want to add custom user data to the AspNetUsers table. So, you will create a ApplicationUser.cs
class and inheriting from IdentityUser
, code like this:
public class ApplicationUser: IdentityUser
{
//custom user data.
public string CustomTag { get; set; }
}
Then, in the Startup.ConfigureServices
, we could replace IdentityUser
with ApplicationUser
, and use the following code to configure the Identity:
services.AddIdentity<ApplicationUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
//there is no need to add the following code:
//services.AddIdentityCore<ApplicationUser>()
// .AddEntityFrameworkStores<ApplicationDbContext>()
// .AddDefaultUI();
[Note] By using the above code, for all of the Scaffolding Identity razor pages, might be you also have to replace the IdentityUser
with ApplicationUser
.
If you don't want to add custom user data to the AspNetUsers table via the ApplicationUser class, try to remove the following code:
//services.AddIdentityCore<ApplicationUser>()
// .AddEntityFrameworkStores<ApplicationDbContext>()
// .AddDefaultUI();
Besides, if still not working, please recheck the database, whether you are using the correct database, and check the user's roles from the AspNetUsers
, AspNetRoles
and AspNetUserRoles
table.