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:
Class: System.Data.Entity.DynamicProxies.IdentityUserClaim_FCA22938ED9E4AAF12F5F2E4717CA49CC6F53B512CF2A371AA0C95D210D35870, Property: UserId, Error: The UserId field is required.
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?
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 });
}