Search code examples
c#asp.net-mvcasp.net-identityowin

User.IsInRole() does not work right after role assignment, but does after re-login


In a ASP.NET MVC 5 application I'm using Unity container to create OWIN/Identity objects and resolve all the dependencies.

The problem is when I register as a new user and assign him a role like this

userManager.AddToRole(user.Id, "NewUser");
...
await userManager.UpdateAsync(user);

it actually creates a record in AspNetUserRoles table, but right after that if I check his role with User.IsInRole("NewUser") I get false, unless I log out and then log in again, then it is true.

I guess the problem could be with Identity objects (UserManager, RoleManager, etc.) lifetime management in Unity context.

UnityConfig.cs

public static void RegisterTypes(IUnityContainer container)
{
    // DbContext
    container.RegisterType<DbContext, AppEntitiesDbContext>();
    container.RegisterType<AppIdentityDbContext>();

    // Identity
    container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
                new InjectionConstructor(typeof(AppIdentityDbContext)));

    container.RegisterType<IAuthenticationManager>(
                new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));

    container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>(
                new InjectionConstructor(typeof(AppIdentityDbContext)));

     container.RegisterType<ApplicationUserManager>();
     container.RegisterType<ApplicationSignInManager>();
     container.RegisterType<ApplicationRoleManager>();
}

IdentityConfig.cs (I use <add key="owin:AppStartup" value="MyApp.IdentityConfig" /> in Web.config)

public class IdentityConfig
{
    public void Configuration(IAppBuilder app)
    {
        var container = UnityConfig.GetConfiguredContainer();

        app.CreatePerOwinContext(() => container.Resolve<AppIdentityDbContext>());
        app.CreatePerOwinContext(() => container.Resolve<ApplicationUserManager>());
        app.CreatePerOwinContext(() => container.Resolve<ApplicationSignInManager>());
        app.CreatePerOwinContext(() => container.Resolve<ApplicationRoleManager>());

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login")
        });
    }
}

Solution

  • That's because using anything from the User object (IPrincipal) is looking at the identity token of the user for the current HTTP request, not the persisted values of the user.

    When you log in that token gets created from the roles and other claims. If you change the user's roles in the database the token needs to be recreated and set as the user's new identity.

    When you change a part of the user's identity. Just invalidate the old token and re-issue an new one by signing them out/back in.

    private async Task SignInAsync(User user, bool isPersistent)
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
        }