ASP.NET Core 3.1 MVC Access Denied role based authorization – Conflict with custom UserClaimsPrincipalFactory

I have read a lot of answers on stackoverflow for similar problems but still cannot figure out what I am doing wrong.

My AspNetUsers, AspNetRoles and AspNetUserRoles tables have been seeded correctly.

This is my ConfigureServices:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services.AddIdentity<ApplicationUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false)
            .AddRoles<IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddClaimsPrincipalFactory<CustomUserClaimsPrincipalFactory>();
        services.AddControllersWithViews();
        services.AddMvc();
        services.AddRazorPages();
        services.AddAuthorization(options => {
            options.AddPolicy("ManagerOnly", policy => policy.RequireRole("Manager"));
            options.FallbackPolicy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
        });
        services.Configure<IdentityOptions>(options =>
        {
            ...
        });

        services.ConfigureApplicationCookie(options =>
        {
            ...
        });

        services.Configure<PasswordHasherOptions>(option =>
        {
            ...
        });
    }

I am using the default identity management that Visual Studio scaffolds when we select “individual user accounts” as authentication method.

When I put
[Authorize(Policy = "ManagerOnly"]
on any Action, I get the Access Denied result even when logged in from the correct user account which is in the Manager role.

EDIT:

I have verified that the problem disappears when I remove .AddClaimsPrincipalFactory<CustomUserClaimsPrincipalFactory>();

Following is my CustomUserClaimsPrincipalFactory:

public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
    public CustomUserClaimsPrincipalFactory(
    UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor)
    {
    }

    protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
    {
        var identity = await base.GenerateClaimsAsync(user);
        identity.AddClaim(new Claim("FullName", user.FullName ?? "Unnamed"));
        return identity;
    }
}

Any idea why this would conflict with Roles?

EDIT 2:

Solution for noobs like me:

public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public CustomUserClaimsPrincipalFactory(
    UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
    {
    }

Per my understanding, role manager needs to be injected in the custom claims factory so that roles defined in the database can be loaded when we override the base claims generation.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I think your dependencies that you inject to CustomUserClaimsPrincipalFactory are somehow different or not resolved properly thus not connecting to the DB.

Please make sure the role manager is properly setup in your startup.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x