MVC 5 AddToRole requires logout before it works?
Asked Answered
C

4

13

I'm finding that if I add a user to a role in ASP Identity, it doesn't take effect until I log out and log back in. Is there something I need to do to refresh a user's roles without forcing a user to log off first?

Here's how I'm adding the user to the role.

var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var userId = HttpContext.Current.User.Identity.GetUserId();

userManager.AddToRole(userId, roleName);

Then, almost immediately, I redirect the user to this action method. I can tell in the database that I've been added to the correct role, but MVC still redirects me to the login page. However, if I log out, log back in, and attempt to go to this action method, it works just fine.

    [Authorize(Roles = RecoveryStandardRoles.ServiceProvider)]

    public partial class CertifyController : Controller

{
    #region Public Methods and Operators

    public virtual ActionResult CompanyProfile()
    {
        return this.View();
    }

    #endregion
}

Thank you for taking time to look at my question!

Cardiogram answered 10/12, 2013 at 12:58 Comment(1)
If you're checking if someone is in a role in your code, User.IsInRole(roleName) requires logout and login to reflect being added to the new role. UserManager.IsInRole(userID, roleName) does not.Inflection
F
9

MVC5 register new user, assign role and activate user with role WITHOUT logging off and back on by using :await UserManager.AddToRoleAsync(user.Id, "Role Name")

if (ModelState.IsValid)
{
    var user = new ApplicationUser() { UserName = model.Email, Email = model.Email,StandName = model.StandName,FirstName = model.FirstName,LastName = model.LastName,CellPhone = model.CellPhone,Supervisor = model.Supervisor};
    IdentityResult result = await UserManager.CreateAsync(user, model.Password);

    var roleStore = new RoleStore<IdentityRole>(context);
    var roleManager = new RoleManager<IdentityRole>(roleStore);

    var userStore = new UserStore<ApplicationUser>(context);
    var userManager = new UserManager<ApplicationUser>(userStore);


    if (result.Succeeded)
    {
         ***await UserManager.AddToRoleAsync(user.Id, "Users Tammy");***
         await SignInAsync(user, isPersistent: false);
Fur answered 15/11, 2014 at 6:54 Comment(2)
This answer helped me solve this issue. I had the line to add the user to a role after the line to sign in. Once I added the role before signing in, the role was added without requiring a sign out/in.Rem
Years after creating the question, this is the answer I use the most. The key is making sure you AddToRoleAsync before SignInAsyncCardiogram
C
8

@kevin-junghans your answer lead me to the correct answer. This code shows how to add a user to a role in MVC 5 and to have that role automatically take effect.

var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var userId = HttpContext.Current.User.Identity.GetUserId();

userManager.AddToRole(userId, roleName);

var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;

authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

var user = userManager.FindById(userId);
var identity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);

authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = false }, identity);
Cardiogram answered 10/12, 2013 at 14:19 Comment(1)
You are effectively logging the person out and back in again, which will work. The method CreateIdentity will add the roles as claims again, effectively adding the new role. The method I described in may answer does not require you to query the database again to fill these claims.Splenic
S
5

ASP.NET Identity uses claims to store the roles and uses the claims instead of doing a database query each time it needs to perform authorization. So the roles will not be in the claims until the person has been logged in again. You can read about using claims in ASP.NET Identity here. The articles shows how to add claims during the log-in process. But if you add a role to the current user you can update the claims using the method described in my answer to this QA without forcing the user to log in again. There is a claim for each role assigned to the user. Use ClaimTypes.Role when adding a new role.

Splenic answered 10/12, 2013 at 13:41 Comment(10)
That doesn't make sense because I don't have anything in my claims table. AddUserToRole adds a record to AspNetUserRoles and there's never anything in AspNetUserClaims. Can you explain that, please?Cardiogram
Also, the AuthenticationManager.SignIn line in the answer you referenced doesn't compile.Cardiogram
The claims I am referring to are not in the database table. They are in the CurrentPrincipal.Splenic
@KevinJunghans Anything wrong with UserManager<ApplicationUser>.AddClaim(userId, new Claim(ClaimTypes.Role, roleName)).Noctule
@SeanStenlund - The AddClaim method of the UserManager just puts the claims in the user store. In other words, it stores it in the table AspNetUserClaims in the Identity database. It does not store it in the ClaimsPrincipal so it is available during authorization without querying the database again. I have not figured out the reasoning for having the AspNetUserClaims table and have not seen a good example of its use.Splenic
@KevinJunghans I see. Maybe the Claims table and the ClaimsPrincipal will work together in later versions.Noctule
The problem I am having is when I, an administrator of the application (from my own computer) manually add a different user (who is already himself logged-in from his own computer!) to a role... This user may remain logged in for 14 days (the default) and doesn't see the new role until he decides to log out and back in again... I suppose I would need to add custom code to detect a "change in roles" by querying the database every time, and then perform the recommendations already given?Ligate
@Ligate - They added a more efficient way to handle this that is more event driven in ASP.NET Identity 2.0. What you want to use is the SecurityStamp. Take a look at this QA. #19487822Splenic
@KevinJunghans could you please post a code example that works in this situation specifically. We'd be very grateful since you obviously know alot about claims.Inflection
@Inflection - Would you please be more specific about what the code example you are looking for should cover.Splenic
A
1

After adding a role to the current user you can update the claims without forcing the user to log off and log in again.

Dim owinAuth = HttpContext.Current.GetOwinContext().Authentication
Dim authResult = Await owinAuth.AuthenticateAsync(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie)

authResult.Identity.AddClaim(New System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, "RoleName"))

Equivalent C# code for reference:

var owinAuth = HttpContext.Current.GetOwinContext().Authentication;

var authResult =
    await owinAuth.AuthenticateAsync(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);

authResult.Identity.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, roleName));
Amethyst answered 16/10, 2014 at 3:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.