Why isn't [Authorize(Roles = "Admin")] working in MVC 5 RTM with ASP.NET Identity?

19,069

Solution 1

And the answer is the UserManager's DbContext must have lazy loading enabled in order for user roles to manifest in the application in the usual, expected, way. As it turns out, not all of my code was "out of the box." I had customized my DbContext ever so slightly. Hopefully in the future Microsoft will sidestep this integration bug by ensuring the collection is loaded with something like userDbContext.Users.Include(o => o.Roles).SingleOrDefault(...).

  • DO: ApplicationDbContext.Configuration.LazyLoadingEnabled = true;
  • DO NOT: ApplicationDbContext.Configuration.LazyLoadingEnabled = false;

Note that if ApplicationDbContext.Configuration.LazyLoadingEnabled is not set in your code then it defaults to true. So leaving off that line is as good as setting it to true.

Etc.

Here's my guess at what is going on when lazy loading is disabled, the Roles property of the IdentityUser / ApplicationUser object is null or empty when the UserManager or UserStore accesses it because that collection was not manually loaded. The code then carries on like no roles have been assigned to the user when in fact that collection simply was never loaded.

Ah, the aroma of silent failure. Had the code only made some noise when things didn't look right.

Solution 2

The user may need to be re-authenticated to receive new claims that include membership in the Admin role. Since MVC 5 uses ASP.NET Identity out of the box, and by default in MVC 5, ASP.NET Identity stores claims like roles in the user's cookies, that information can become stale (hence the database says one thing but the user's cookies say something else). Re-authenticating a user will refresh their claims, including user role claims, to match the current state of the database.

For example:

If a user signs in before being assigned to the Admin role in the database that user will be granted claims but they will not include their assignment to the Admin role. If later, they are added to the Admin role, the claims stored in their cookies are not automatically updated. Instead only the database has been update, the application has to re-authenticate them before their old claims will be replaced with the new claims that include membership in the Admin role. Having the user manually sign out and back in, is the most obvious way re-authenticate that user.

Here's an article on Using Claims in ASP.NET Identity

Share:
19,069

Related videos on Youtube

Jeremy Cook
Author by

Jeremy Cook

Husband and da-da, passionate about helping others via the software I write.

Updated on September 16, 2022

Comments

  • Jeremy Cook
    Jeremy Cook about 1 year

    Does [Authorize(Roles = "Admin")] work out of the box in MVC 5 RTM with ASP.NET Identity?

    I've had no luck. Note that [Authorize] and [Authorize(Users = "AdminUser")] work just fine, and the AspNetUserRoles and AspNetRoles tables are populated as I would expect them to be, establishing a relationship between the AdminUser user and the Admin role. This issue seems specific to roles.

    • Jeremy Cook
      Jeremy Cook almost 10 years
      Authorize does work out of the box, but setting Roles does not work as expect. When my user, AdminUser, is assigned to the role Admin and I apply the [Authorize(**Roles** = "Admin")] attribute I receive access denied which is not expected. When I apply the [Authorize(**Users**= "AdminUser")] attribute I am permitted access as is expected. As far as my seeding code goes, my tables look exactly as they should with a relationship between the AdminUser user and the Admin role in the AspNetUserRoles table. Does using Roles = "..." work for you with a fresh MVC 5 site?
  • Jeremy Cook
    Jeremy Cook almost 10 years
    Then I'd guess your problem is different from mine. You may want to follow the path I took to get to figure out a solution. I created a new web application and compared absolutely everything in it with the one I had been working on for a few days. One way to do this, assuming you are already using source control, is to replace all pertinent files in your project with those from the new project and use your favorite source control tool to view what has changed.
  • fabspro
    fabspro over 9 years
    This was driving me crazy. Did not think to log out and log back in, because when the role was denying access it was showing the login page. I was logging in on that page without logging out first. Gah!
  • Raphael Teubner
    Raphael Teubner over 9 years
    Perfect I just added your code in my MyAppDbContext which Inherit of the IdentityDbContext<ApplicationUser> and it worked fine :). public class MyDbContext: IdentityDbContext<ApplicationUser> { new public DbSet<ApplicationRole> Roles { get; set;} public MyDbContext() : base("DefaultConnection") { this.Configuration.LazyLoadingEnabled = true; } }
  • Jeremy Cook
    Jeremy Cook almost 9 years
    Care to explain why this helped you?