13

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.

16
  • Are you checking same request or a subsequent one ? Commented Feb 20, 2018 at 6:40
  • Have a look at this code review and this answer. Commented Feb 20, 2018 at 7:27
  • I want to check each subsequent requests. Commented Feb 20, 2018 at 12:08
  • I see that you're return Ok() Which seems to imply that you are doing this from an HTTP Response instead of doing this from your Authentication Middleware Commented Feb 23, 2018 at 15:53
  • @johnny5, yes, I'm doing it in the controller, similar to how the default does the creation from the AccountController when creating/registering the user. Commented Feb 23, 2018 at 17:26

1 Answer 1

1

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();
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.