20

I am using ASP.NET MVC 5.1 with Owin and Claims authentication.

After the user changes its email I need to update the users claims, so I tried in the controller:

  ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
  Claim claim = identity.FindFirst(ClaimTypes.Email);
  identity.RemoveClaim(claim);
  identity.AddClaim(new Claim(ClaimTypes.Email, newEmail));

  IOwinContext context = new OwinContext();

  context.Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
  context.Authentication.SignIn(identity);

The Claim is changed but when I refresh the page the email claims is the original again ...

It seems the cookie is not being updated. Any idea what I am doing wrong?

And is it possible to get the value of "IsPersistent" from the identity so when I sign it again I will have the same value?

Thank You,

Miguel

JumpingJezza
  • 5,498
  • 11
  • 67
  • 106
Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • 1
    I found an answer to this [here](http://stackoverflow.com/questions/19349011/how-to-change-authentication-cookies-after-changing-username-of-current-user-wit) that worked for me. – Chris Morgan Mar 08 '14 at 01:34

3 Answers3

27

I had this same problem, so just wanted to summarise my findings here. As Chris says, the basis of the answer is indeed here: How to change authentication cookies after changing UserName of current user with asp.net identity but I found that thread a bit hard to follow, and that question isn't really a direct duplicate.

To begin, get the AuthenticationManager from the current OWIN context. Once you have that, you can get the value of "isPersistent" (and other properties from the original SignIn call), by calling the AuthenticateAsync method. Then to update the claims of the current user identity you just need to replace the value of the AuthenticationResponseGrant property like this:

var identity = (ClaimsIdentity)User.Identity;

// Call AddClaim, AddClaims or RemoveClaim on the user identity.

IOwinContext context = Request.GetOwinContext();

var authenticationContext = 
    await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);

if (authenticationContext != null)
{
    authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(
        identity,
        authenticationContext.Properties);
}

It is the final setting of the AuthenticationResponseGrant property that actually updates the cookie.

Hope this helps other readers.

Community
  • 1
  • 1
Adrian Edwards
  • 559
  • 5
  • 8
  • How do you get AuthenticationManager and AuthenticationResult from OWIN Context? – Maxim V. Pavlov May 20 '15 at 14:37
  • @maxim, With reference to the above sample code, `context.Authentication` (i.e.,`Request.GetOwinContext().Authentication`) will get you the current AuthenticationManager. After that call `authenticationManager.AuthenticateAsync()` to get the AuthenticationResult. – Adrian Edwards May 21 '15 at 07:25
  • +1 for "It is the final setting of the AuthenticationResponseGrant property that actually updates the cookie." I injected `IAuthenticationManager` directly into my class, set the `AuthenticationResponseGrant` property, and it worked great. Thanks! – emragins Sep 04 '15 at 23:22
  • 1
    +1 for the straight forward explanation. This was messing with my head for hours! Thank you! One little bit of feedback; at the very end of your code, I think you meant to use `authenticationContext.Properties` instead of `authenticationResult.Properties` as `authenticationResult` doesn't exist in that context. – Gio Dec 23 '15 at 06:44
  • Worked for me but I had to change `DefaultAuthenticationTypes.ExternalCookie` to `DefaultAuthenticationTypes.ApplicationCookie`. Thanks! – biddano Apr 27 '20 at 23:42
7

SORRY, this is an ASP.NET CORE solution I also challenged the problem with claims, but the answer was easy to find.

To refresh your cookie, you can rely on the RefreshSignInAsync() function of the SignInManager;

private readonly UserManager<ApplicationUser> _userManager;
    private readonly ApplicationDbContext _context;
    private readonly SignInManager<ApplicationUser> _signInManager;

    public ApiClubController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, ApplicationDbContext context)
    {
        _userManager = userManager;
        _context = context;
        _signInManager = signInManager;
    }

Inside your function:

//GET CURRENT USER
        var usr = await GetCurrentUserAsync();
        //OLD CLAIM
        var myClaims = await _userManager.GetClaimsAsync(usr);
        var oldClaim = myClaims.Where(o => o.Type.Equals("Club")).FirstOrDefault();
        if (oldClaim != null)
        {
            await _userManager.RemoveClaimAsync(usr, oldClaim);
        }

        //CREATE CLUB CLAIM
        var clubClaim = new Claim("Club", "" + id);
        await _userManager.AddClaimAsync(usr, clubClaim);

        //RESET USER COOKIE
        await _signInManager.RefreshSignInAsync(usr);

        //RETURN
        return Ok(company);;

NOTE: I'm using an API here, because I'm mixing up a lot with angular. If you update your identity with your API, you need to refresh your page in order to view things based on your claim

Officer Jonez
  • 253
  • 2
  • 8
4

This works for me. Not sure if it is the best way but the updated claim is in the DB and in subsequent controllers.

var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
var c = identity.Claims.FirstOrDefault(r => r.Type == "tId");
await UserManager.RemoveClaimAsync(user.Id, c);
await UserManager.AddClaimAsync(user.Id, new Claim("tId", "9032C945-DC5C-4FC9-BE7C-8EDC83A72E58"));

AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, identity);
Shiloh
  • 1,816
  • 4
  • 24
  • 29
  • This works for me finally. I think I was blocked by the list of Claims. Maybe it should use the method ``UserManager.GetClaimsAsync`` in order to avoid exception Enumerator foreach during ``RemoveClaimAsync``. Holp this will be help to others. – Yang C Jun 09 '15 at 14:16
  • This is the only way I could get claims to update. Thanks! – FirstVertex Sep 22 '16 at 15:31