Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update Identity User Claims in Web API

I'm currently trying to update the user's email/username from a mobile app to a Web API project. I'm currently using oauth and token authentication. When updating the identity user, the user becomes unauthenticated because the username and access token are no longer valid. From what I have read, I must update the identity claims. This is what I have tried so far:

var identity = new ClaimsIdentity(User.Identity);

if (result)
{
    var identityUser =  await UserManager.FindByNameAsync(User.Identity.Name);

    identityUser.Email = AntiXssEncoder.HtmlEncode(value.Email, true);
    identityUser.UserName = AntiXssEncoder.HtmlEncode(value.Email, true);

    var identityResult = await UserManager.UpdateAsync(identityUser);

    if(identityResult.Succeeded)
    {
        var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;

        await UserManager.RemoveClaimAsync(identityUser.Id, identity.FindFirst(ClaimTypes.Name));
        await UserManager.AddClaimAsync(identityUser.Id, new Claim(ClaimTypes.Name, value.Email));

        identity.RemoveClaim(identity.FindFirst(ClaimTypes.Name));
        identity.AddClaim(new Claim(ClaimTypes.Name, value.Email));

        authenticationManager.AuthenticationResponseGrant =
                    new AuthenticationResponseGrant(
                    new ClaimsPrincipal(identity),
                    new AuthenticationProperties { IsPersistent = false });
     }
  return Ok();
}

However, it still shows the previous email when using User.Identity.Name and the claims for the user within the authenticationManager have not been updated either. I'm not sure what else to do as there isn't much documentation on this for Web API. Any help is greatly appreciated.

like image 290
Humpy Avatar asked Feb 20 '18 05:02

Humpy


People also ask

How do I add a user claim?

Just add the claims to the user object before you call CreateAsync on the user manager. var identityUser = new IdentityUser { UserName = username, Email = email, // etc... Claims = { new IdentityUserClaim { ClaimType = "SomeClaimType", ClaimValue = "SomeClaimValue"} } }; var identityResult = await _userManager.

Where are user claims stored?

By default, a user's claims are stored in the authentication cookie. If the authentication cookie is too large, it can cause the app to fail because: The browser detects that the cookie header is too long.


1 Answers

Main problem is that claim which represents user's name is not updated in ClaimsIdentity you are using in the last step.

The easiest way to perform the update is to use SignInManager<TUser, TKey>.SignIn method

signInManager.SignIn(identityUser, isPersistent: false, rememberBrowser: false);

This is also an ASP.NET Identity idiomatic way since it is using associated IClaimsIdentityFactory to create claims for new identities.


Complete example

static async Task<IdentityResult> UpdateEmailAsync<TUser>(
    IPrincipal principal,
    UserManager<TUser, string> userManager,
    SignInManager<TUser, string> signInManager,
    string newEmail
)
    where TUser : class, IUser<string>
{
    string userId = principal.Identity.GetUserId();
    IdentityResult result = await userManager.SetEmailAsync(userId, newEmail);
    if (result.Succeeded)
    {
        // automatically confirm user's email
        string confirmationToken = await userManager.GenerateEmailConfirmationTokenAsync(userId);
        result = await userManager.ConfirmEmailAsync(userId, confirmationToken);
        if (result.Succeeded)
        {
            TUser user = await userManager.FindByIdAsync(userId);
            if (user != null)
            {
                // update username
                user.UserName = newEmail;
                await userManager.UpdateAsync(user);

                // creates new identity with updated user's name
                await signInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
            }

            // succeded
            return result;
        }
    }

    // failed
    return result;
}

Then you can just call it from your code

string newEmail = AntiXssEncoder.HtmlEncode(value.Email, true);
IdentityResult result = await UpdateEmailAsync(identityUser, UserManager, SignInManager, newEmail);
if (result.Succeeded)
{
    return Ok();
}
like image 152
laika Avatar answered Sep 21 '22 12:09

laika