Search code examples
c#asp.net-mvcasp.net-mvc-5claims-based-identityasp.net-identity-2

ASP.NET EF6 Identity 2 Update User Claims Error Updating For User


I have a web application in which I am using ASP.NET Identity 2 Claims for the access control list for Users of the site. I have all the claims available correctly in the ApplicationUser model, which has a Claims list.

I am trying to clear the claims for a user and then give them a whole set of new ones. I have a Claim Group object which contains a list of Claim Items

[HttpPost, ValidateAntiForgeryToken]
    public async Task<ActionResult> Claims_UpdateByClaimGroup([Bind(Include = "Id,SelectedClaimGroupId")] UserClaimsViewModel viewModel)
    {
        User user = await _repository.GetAsync<User>(viewModel.Id);
        ApplicationUser appUser = await UserManager.FindByIdAsync(user.AppUser.Id);
        var claimGroup = await _repository.GetAsync<ClaimGroup>(Convert.ToInt32(viewModel.SelectedClaimGroupId));

        appUser.Claims.Clear();// Clear all the claims first

        // Then add the new claims.
        foreach (ClaimGroupItems claimGroupItem in claimGroup.Items)
        {
            //UserManager.AddClaim(user.AppUser.Id, new Claim(claimGroupItem.MenuItemId.ToString(), claimGroupItem.ClaimValue));
            appUser.Claims.Add(new IdentityUserClaim()
            {
                ClaimType = claimGroupItem.MenuItemId.ToString(),
                ClaimValue = claimGroupItem.ClaimValue,
                UserId = user.AppUser.Id,
            });
        }

        try
        {
            _repository.Save<User>(appUser.User);
            //await UserManager.UpdateAsync(appUser);
        }
        catch (DbEntityValidationException dbEx)
        {
            foreach (var validationErrors in dbEx.EntityValidationErrors)
            {
                foreach (var validationError in validationErrors.ValidationErrors)
                {
                    Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
                        validationErrors.Entry.Entity.GetType().FullName,
                        validationError.PropertyName,
                        validationError.ErrorMessage);
                }
            }

            throw;  // You can also choose to handle the exception here...
        }

        return RedirectToAction("Claims", new { id = viewModel.Id, Updated = true });
    }

You can see here a few things:

  1. I am trying to remove, add and Save the claims on the ApplicationUser.Claims object without using the UserManager methods. (ApplicationUser contains a reference to User).
  2. I have a try catch block because it is throwing some validation errors. The error message that comes out on the trace is:

Class: System.Data.Entity.DynamicProxies.IdentityUserClaim_FCA22938ED9E4AAF12F5F2E4717CA49CC6F53B512CF2A371AA0C95D210D35870, Property: UserId, Error: The UserId field is required.

  1. If I try and use the UserManager methods I get the same error.

    UserManager.AddClaim(user.AppUser.Id, new Claim(claimGroupItem.MenuItemId.ToString(), claimGroupItem.ClaimValue));

and

await UserManager.UpdateAsync(appUser);

The error is caused by this being called: appUser.Claims.Clear();

How can I correctly update the claims for the user?


Solution

  • I got it working. For some reason appUser.Claims.Clear() wasn't working and was causing the userId is null error. So I've manually removed the old claims in a loop, and it all works now.

    [HttpPost, ValidateAntiForgeryToken]
    public async Task<ActionResult> Claims_UpdateByClaimGroup([Bind(Include = "Id,SelectedClaimGroupId")] UserClaimsViewModel viewModel)
    {
        var claimGroup = await _repository.GetAsync<ClaimGroup>(Convert.ToInt32(viewModel.SelectedClaimGroupId));
        User user = await _repository.GetAsync<User>(viewModel.Id);
        ApplicationUser appUser = await UserManager.FindByIdAsync(user.AppUser.Id);
    
        // Remove all the old claims
        var listToRemove = appUser.Claims.ToArray();
        foreach (var item in listToRemove)
        {
            await UserManager.RemoveClaimAsync(item.UserId, new Claim(item.ClaimType, item.ClaimValue));
        }
    
        try
        {
            // Then add the new claims.
            foreach (ClaimGroupItems claimGroupItem in claimGroup.Items)
            {
                //UserManager.AddClaim(appUser.Id, new Claim(claimGroupItem.MenuItemId.ToString(), claimGroupItem.ClaimValue));
                appUser.Claims.Add(new IdentityUserClaim()
                {
                    UserId = appUser.Id,
                    ClaimType = claimGroupItem.MenuItemId.ToString(),
                    ClaimValue = claimGroupItem.ClaimValue,
                });
            }
            await UserManager.UpdateAsync(appUser);
        }
        catch (DbEntityValidationException dbEx)
        {
            foreach (var validationErrors in dbEx.EntityValidationErrors)
            {
                foreach (var validationError in validationErrors.ValidationErrors)
                {
                    Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
                        validationErrors.Entry.Entity.GetType().FullName,
                        validationError.PropertyName,
                        validationError.ErrorMessage);
                }
            }
    
            throw;  // You can also choose to handle the exception here...
        }
    
        return RedirectToAction("Claims", new { id = viewModel.Id, Updated = true });
    }