Surely I'm missing something easy? Or was it really made this complicated?
If you use AddDefaultIdentity no UserRoleStore gets created? So you can't use roles at all. Why? If you switch back to AddIdentity then you have to deal reintegrating everything such as login paths, razor page authentication, etc..
Because we got way more objections to create a role table that people didn't use than we did kudos for doing it just in case people wanted roles. So we did what the majority wanted.
But those tables get created either way in a new template? Maybe Role based authorization is just bad for a new app and I need to change my thinking.
They shouldn't be created any more, or at least that was the intent. @HaoK ?
Yep, it all gets created. Start a new 2.1 app with individual auth and you end up with those in the CreateIdentity migration. If you wipe those out and do Add-Migration, you still end up with them. So the assumption is you can easily use them. You cannot.
The migration we create is always the same right now @ajcvickers maybe this is something we can consider making smarter in the future.
To turn roles back on, you need to uncollapse AddDefaultIdentity back to something like:
services.AddIdentity<User, Role>.AddEntityFrameworkStores().AddDefaultUI().AddDefaultTokenProviders()
Hi,
tried to use the new 2.1 syntax:
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
By using this, you can not define a policy for roles. E.g.:
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Administrator"));
});
You will always get false for: var hasAdministratorRole = (await AuthorizationService.AuthorizeAsync(User, "RequireAdministratorRole")).Succeeded;
If you use instead:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders()
Everything works as expected. Where is the difference? What am I doing wrong? How can you add role based auth in your app with the new syntax (like .AddRoles
You also need to replace the UserClaimsPrincipal factory with the one that's role aware.
See: https://github.com/aspnet/Identity/blob/dev/src/Core/UserClaimsPrincipalFactory.cs#L102
So something like
AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
This could be potentially a bug/improvement we can make in 2.2 to not require this step.
cc @ajcvickers @blowdart for thoughts?
Oh heck yes, if you're adding the roles tables we should have lit up the right claims factory by default.
The problem that I ran into immediately was this.
Created a new project, scaffolded Identity views (I have a template I'm working I have to work with, so need to customize those views). So these went into \Areas\Identity
Then, realized I couldn't use Roles with DefaultIdentity, so changed that, but then everything went weird (Razor Pages are a new concept for me, I did a site in 1.0, but couldn't upgrade it to 2.0 for other identity issues, that I finally just resolved in 2.1.
So then I realized, I have to go pull out all of the underneath config from the AddDefaultIdentity sugar to add authorization to those views (and actually move them out of the Identity area an into something more basic.
Anyways, it was an entire day of fighting with this because DefaultIdentity didn't do what I expected it to do. It was not a positive experience.
Yeah I hear you @natelaff this is probably something we should address in 2.2, @ajcvickers @blowdart agreed?
So what鈥檚 the path of least resistance here? Or at least the line of thinking? Are roles deprecated as a concept? Should I move to claims in general? Or are there no permission concepts happening in defaultidentity at all and it鈥檚 truely just a user store only?
If you want to use roles I guess you go back to the old:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders()
The idea behind the new AddDefaultIdentity was to just hide all the AddDefaults, the old AddIdentity apis should still continue to work as before
But as a more broad strokes question, clearly you intended to hide roles from default experience. Are claims that way as well? To bring any kind of permissions into this do you need to build out the config ourselves?
Yes at the end of the day the only thing roles did was end up adding a Role claim permission, we feel claims based authorization is the direction we want to encourage going forward, you can still accomplish the Role permission via the user claims api
Just add a claim of type "role" to the user's claims collection in the DB.
Yes, this needs fixing in 2.2
Yeah, the role claim doesn't seem to affect IsUserInRole or AuthorizeAttribute with a role name.
Yeah, the role claim doesn't seem to affect IsUserInRole or AuthorizeAttribute with a role name.
Perhaps your claims identity is not being created the way you need it to be: https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/
After wasting several (more) hours trying to decipher what is supposed to make this seemingly simple functionality work... This is what did it for me:
.AddRoleManager
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDbContext<SCPI_SiteIdentityDbContext>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("SCPI_SiteIdentityDbContextConnection")));
services.AddIdentity<SCPI_site_User, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddRoleManager<RoleManager<IdentityRole>>()
.AddDefaultUI()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<SCPI_SiteIdentityDbContext>();
....
namespace SCPI_Site.Views
{
public class AdminController : Controller
{
[Authorize(Roles = "Admin")] // Before the Add RoleManager Above this would always fail
public IActionResult Admin()
{
return View();
}
}
....
As would this from my _Layout.cshtml
<li>
@if (User.Identity.IsAuthenticated)
{
@if (User.IsInRole("Admin"))
{
<a asp-area="" asp-controller="Admin" asp-action="Admin">Admin</a>
}
}
</li>
Just wanted to use the built in framework at a light level (User in Role)->give them access to some functionality.
@C-BERBER I just tested this in 2.2 preview 1 and it's working.
Yes, it should work, that's why I posted :)
Wouldn't it make more sense to just support roles by merging them into the claims table?
@HaoK solution worked for me
Added the fixed link for better understanding https://github.com/aspnet/Identity/blob/master/src/Core/UserClaimsPrincipalFactory.cs
This factory AddScoped
Logout/Login to see the desire functionality. Now works also in UI, and in the Authorize attribute
@HaoK solution worked for me
Added the fixed link for better understanding https://github.com/aspnet/Identity/blob/master/src/Core/UserClaimsPrincipalFactory.csThis factory AddScoped
just adds the roles claims to the user object. Logout/Login to see the desire functionality. Now works also in UI, and in the Authorize attribute
It took me quite a lot of time to figure it out . For those who still cannot make it work , if you have signed in before using the older code , please logout and login again.
Most helpful comment
After wasting several (more) hours trying to decipher what is supposed to make this seemingly simple functionality work... This is what did it for me:
.AddRoleManager>()
....
....
As would this from my _Layout.cshtml
<li> @if (User.Identity.IsAuthenticated) { @if (User.IsInRole("Admin")) { <a asp-area="" asp-controller="Admin" asp-action="Admin">Admin</a> } } </li>Just wanted to use the built in framework at a light level (User in Role)->give them access to some functionality.