Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User.IsInRole returns nothing in ASP.NET Core (Repository Pattern implemented)

I have an ASP.NET Core (Full .NET Framework) application with the following configuration:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>(p => {
        p.Password.RequireDigit = true;
        p.Password.RequireNonAlphanumeric = false;
        p.Password.RequireUppercase = true;
        p.Password.RequiredLength = 5;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

    services.AddMvc();

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
    services.AddTransient<IDbFactory, DbFactory>();
    services.AddTransient<IUnitOfWork, UnitOfWork>();

    services.AddTransient<IUserRepository, UserRepository>();
    services.AddTransient<IUserService, UserService>();
}

The ApplicationUser extends from IdentityUser and ApplicationDbContext extends IdentityDbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base()
    {
    }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public virtual void Commit()
    {
        base.SaveChanges();
    }

    protected override void OnConfiguring(DbContextOptionsBuilder builder)
    {
        base.OnConfiguring(builder);

        builder.UseSqlServer("connection string here");
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);

        // Configure model
        // Identity
        new Configuration.Identity.ApplicationUserConfiguration(builder.Entity<ApplicationUser>());
        new Configuration.Identity.ApplicationUserProfileConfiguration(builder.Entity<ApplicationUserProfile>());
        new Configuration.Identity.RoleConfiguration(builder.Entity<IdentityRole>());
        new Configuration.Identity.RoleClaimConfiguration(builder.Entity<IdentityRoleClaim<string>>());
        new Configuration.Identity.ApplicationUserRoleConfiguration(builder.Entity<IdentityUserRole<string>>());
        new Configuration.Identity.ApplicationUserClaimConfiguration(builder.Entity<IdentityUserClaim<string>>());
        new Configuration.Identity.ApplicationUserLoginConfiguration(builder.Entity<IdentityUserLogin<string>>());
        new Configuration.Identity.ApplicationUserTokenConfiguration(builder.Entity<IdentityUserToken<string>>());
    }
}

Here is my demo data:

Role table

Role table

User table

User table

UserRole table

UserRole table

In my Login Action i have the following:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            if (User.IsInRole("Admin"))
            {
                return RedirectToAction("Index", "Home", new { area = "Admin" });
            }
            return RedirectToAction("Index", "Home");
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning(2, "User account locked out.");
            return View("Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return View(model);
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

What i want to achieve is to redirect the user to a certain area after login.

The current problem i'm facing is that the function User.IsInRole("Admin") returns false and in debug mode, if i look at the usermanager, the current user doesn't have the roles loaded (Count = 0).

Any thoughts would be appreciated.

Update 1

Ignore the Role Id cause is wrong. In fact the user is mapped with the correct value.

like image 624
Cristian Szpisjak Avatar asked Oct 20 '16 10:10

Cristian Szpisjak


People also ask

What does IsInRole () will return?

If the IsInRole method finds the specified role in the cached list, it returns true .


1 Answers

If anyone (as me) is struggling with this in .Net Core 2.1, this link may help.

In short, if you are using AddDefaultIdentity like this:

services.AddDefaultIdentity<ApplicationUser>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

Then Roles won't work as they are not implemented in DefaultIdentity.

What worked for me is replacing it with:

services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddRoleManager<RoleManager<IdentityRole>>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultUI()
            .AddDefaultTokenProviders();

Also, if you signed in before above fix, logout and login again, so identity claims are refreshed. Now it should work.

like image 101
Piotrek Avatar answered Oct 13 '22 11:10

Piotrek