Как обновить заявку в ASP.NET Identity?
Я использую аутентификацию OWIN для своего проекта MVC5. Это мое SignInAsync
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
var AccountNo = "101";
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim(ClaimTypes.UserData, AccountNo));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, RedirectUri="Account/Index"}, identity);
}
Как видите, я добавил AccountNo
в список претензий.
Теперь, как я могу обновить эту Заявку в какой-то момент в моем приложении? Пока у меня есть это:
public string AccountNo
{
get
{
var CP = ClaimsPrincipal.Current.Identities.First();
var Account= CP.Claims.FirstOrDefault(p => p.Type == ClaimTypes.UserData);
return Account.Value;
}
set
{
var CP = ClaimsPrincipal.Current.Identities.First();
var AccountNo= CP.Claims.FirstOrDefault(p => p.Type == ClaimTypes.UserData).Value;
CP.RemoveClaim(new Claim(ClaimTypes.UserData,AccountNo));
CP.AddClaim(new Claim(ClaimTypes.UserData, value));
}
}
когда я пытаюсь удалить иск, я получаю следующее исключение:
Претензия " http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata: 101" не может быть удалена. Он либо не является частью этого удостоверения, либо является претензией, которая принадлежит Принципалу и содержит это удостоверение. Например, Принципал будет владеть заявкой при создании GenericPrincipal с ролями. Роли будут отображаться через Идентификацию, которая передается в конструкторе, но фактически не принадлежит Идентичности. Аналогичная логика существует для RolePrincipal.
Может ли кто-нибудь помочь мне выяснить, как обновить претензию?
17 ответов
Вы можете создать новый ClaimsIdentity
а потом делай претензии обновляй с такими.
set {
// get context of the authentication manager
var authenticationManager = HttpContext.GetOwinContext().Authentication;
// create a new identity from the old one
var identity = new ClaimsIdentity(User.Identity);
// update claim value
identity.RemoveClaim(identity.FindFirst("AccountNo"));
identity.AddClaim(new Claim("AccountNo", value));
// tell the authentication manager to use this new identity
authenticationManager.AuthenticationResponseGrant =
new AuthenticationResponseGrant(
new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true }
);
}
Я создал метод Extension для добавления / обновления / чтения заявок на основе заданного ClaimsIdentity
namespace Foobar.Common.Extensions
{
public static class Extensions
{
public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return;
// check for existing claim and remove it
var existingClaim = identity.FindFirst(key);
if (existingClaim != null)
identity.RemoveClaim(existingClaim);
// add new claim
identity.AddClaim(new Claim(key, value));
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
}
public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return null;
var claim = identity.Claims.FirstOrDefault(c => c.Type == key);
return claim.Value;
}
}
}
а затем использовать его
using Foobar.Common.Extensions;
namespace Foobar.Web.Main.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
// add/updating claims
User.AddUpdateClaim("key1", "value1");
User.AddUpdateClaim("key2", "value2");
User.AddUpdateClaim("key3", "value3");
}
public ActionResult Details()
{
// reading a claim
var key2 = User.GetClaim("key2");
}
}
}
Другой (асинхронный) подход, использующий UserManager Identity и SigninManager, чтобы отразить изменение в cookie-файле Identity (и при необходимости удалить утверждения из таблицы db AspNetUserClaims):
// Get User and a claims-based identity
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var Identity = new ClaimsIdentity(User.Identity);
// Remove existing claim and replace with a new value
await UserManager.RemoveClaimAsync(user.Id, Identity.FindFirst("AccountNo"));
await UserManager.AddClaimAsync(user.Id, new Claim("AccountNo", value));
// Re-Signin User to reflect the change in the Identity cookie
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// [optional] remove claims from claims table dbo.AspNetUserClaims, if not needed
var userClaims = UserManager.GetClaims(user.Id);
if (userClaims.Any())
{
foreach (var item in userClaims)
{
UserManager.RemoveClaim(user.Id, item);
}
}
Используя последние Asp.Net Identity с.net core 2.1, я могу обновлять заявки пользователей с помощью следующей логики.
Зарегистрировать
UserClaimsPrincipalFactory
так что каждый разSignInManager
Поет пользователь, претензии созданы.services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimService>();
Реализовать кастом
UserClaimsPrincipalFactory<TUser, TRole>
как нижеpublic class UserClaimService : UserClaimsPrincipalFactory<ApplicationUser, ApplicationRole> { private readonly ApplicationDbContext _dbContext; public UserClaimService(ApplicationDbContext dbContext, UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor) { _dbContext = dbContext; } public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user) { var principal = await base.CreateAsync(user); // Get user claims from DB using dbContext // Add claims ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("claimType", "some important claim value")); return principal; } }
Позже в вашем приложении, когда вы изменяете что-то в БД и хотите отразить это для вашего аутентифицированного и авторизованного пользователя, следующие строки достигают этого:
var user = await _userManager.GetUserAsync(User); await _signInManager.RefreshSignInAsync(user);
Это гарантирует, что пользователь может видеть актуальную информацию, не требуя входа снова. Я поместил это непосредственно перед возвратом результата в контроллер, чтобы после завершения операции все было безопасно обновлено.
Вместо того, чтобы редактировать существующие заявки и создавать условия гонки для безопасного cookie-файла и т. Д., Вы просто молча регистрируете пользователя и обновляете состояние:)
Я тоже получаю это исключение и все проясняю
var identity = User.Identity as ClaimsIdentity;
var newIdentity = new ClaimsIdentity(identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType);
newIdentity.AddClaims(identity.Claims.Where(c => false == (c.Type == claim.Type && c.Value == claim.Value)));
// the claim has been removed, you can add it with a new value now if desired
AuthenticationManager.SignOut(identity.AuthenticationType);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, newIdentity);
Скомпилировал некоторые ответы отсюда в повторно используемый класс ClaimsManager с моими дополнениями.
Претензии сохранены, файл cookie пользователя обновлен, вход обновлен.
Обратите внимание, что ApplicationUser можно заменить на IdentityUser, если вы не настраивали предыдущий. Также в моем случае у него должна быть немного другая логика в среде разработки, поэтому вы можете удалить зависимость IWebHostEnvironment.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using YourMvcCoreProject.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Hosting;
namespace YourMvcCoreProject.Identity
{
public class ClaimsManager
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IWebHostEnvironment _env;
private readonly ClaimsPrincipalAccessor _currentPrincipalAccessor;
public ClaimsManager(
ClaimsPrincipalAccessor currentPrincipalAccessor,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IWebHostEnvironment env)
{
_currentPrincipalAccessor = currentPrincipalAccessor;
_userManager = userManager;
_signInManager = signInManager;
_env = env;
}
/// <param name="refreshSignin">Sometimes (e.g. when adding multiple claims at once) it is desirable to refresh cookie only once, for the last one </param>
public async Task AddUpdateClaim(string claimType, string claimValue, bool refreshSignin = true)
{
await AddClaim(
_currentPrincipalAccessor.ClaimsPrincipal,
claimType,
claimValue,
async user =>
{
await RemoveClaim(_currentPrincipalAccessor.ClaimsPrincipal, user, claimType);
},
refreshSignin);
}
public async Task AddClaim(string claimType, string claimValue, bool refreshSignin = true)
{
await AddClaim(_currentPrincipalAccessor.ClaimsPrincipal, claimType, claimValue, refreshSignin);
}
/// <summary>
/// At certain stages of user auth there is no user yet in context but there is one to work with in client code (e.g. calling from ClaimsTransformer)
/// that's why we have principal as param
/// </summary>
public async Task AddClaim(ClaimsPrincipal principal, string claimType, string claimValue, bool refreshSignin = true)
{
await AddClaim(
principal,
claimType,
claimValue,
async user =>
{
// allow reassignment in dev
if (_env.IsDevelopment())
await RemoveClaim(principal, user, claimType);
if (GetClaim(principal, claimType) != null)
throw new ClaimCantBeReassignedException(claimType);
},
refreshSignin);
}
public async Task RemoveClaims(IEnumerable<string> claimTypes, bool refreshSignin = true)
{
await RemoveClaims(_currentPrincipalAccessor.ClaimsPrincipal, claimTypes, refreshSignin);
}
public async Task RemoveClaims(ClaimsPrincipal principal, IEnumerable<string> claimTypes, bool refreshSignin = true)
{
AssertAuthenticated(principal);
foreach (var claimType in claimTypes)
{
await RemoveClaim(principal, claimType);
}
// reflect the change in the Identity cookie
if (refreshSignin)
await _signInManager.RefreshSignInAsync(await _userManager.GetUserAsync(principal));
}
public async Task RemoveClaim(string claimType, bool refreshSignin = true)
{
await RemoveClaim(_currentPrincipalAccessor.ClaimsPrincipal, claimType, refreshSignin);
}
public async Task RemoveClaim(ClaimsPrincipal principal, string claimType, bool refreshSignin = true)
{
AssertAuthenticated(principal);
var user = await _userManager.GetUserAsync(principal);
await RemoveClaim(principal, user, claimType);
// reflect the change in the Identity cookie
if (refreshSignin)
await _signInManager.RefreshSignInAsync(user);
}
private async Task AddClaim(ClaimsPrincipal principal, string claimType, string claimValue, Func<ApplicationUser, Task> processExistingClaims, bool refreshSignin)
{
AssertAuthenticated(principal);
var user = await _userManager.GetUserAsync(principal);
await processExistingClaims(user);
var claim = new Claim(claimType, claimValue);
ClaimsIdentity(principal).AddClaim(claim);
await _userManager.AddClaimAsync(user, claim);
// reflect the change in the Identity cookie
if (refreshSignin)
await _signInManager.RefreshSignInAsync(user);
}
/// <summary>
/// Due to bugs or as result of debug it can be more than one identity of the same type.
/// The method removes all the claims of a given type.
/// </summary>
private async Task RemoveClaim(ClaimsPrincipal principal, ApplicationUser user, string claimType)
{
AssertAuthenticated(principal);
var identity = ClaimsIdentity(principal);
var claims = identity.FindAll(claimType).ToArray();
if (claims.Length > 0)
{
await _userManager.RemoveClaimsAsync(user, claims);
foreach (var c in claims)
{
identity.RemoveClaim(c);
}
}
}
private static Claim GetClaim(ClaimsPrincipal principal, string claimType)
{
return ClaimsIdentity(principal).FindFirst(claimType);
}
/// <summary>
/// This kind of bugs has to be found during testing phase
/// </summary>
private static void AssertAuthenticated(ClaimsPrincipal principal)
{
if (!principal.Identity.IsAuthenticated)
throw new InvalidOperationException("User should be authenticated in order to update claims");
}
private static ClaimsIdentity ClaimsIdentity(ClaimsPrincipal principal)
{
return (ClaimsIdentity) principal.Identity;
}
}
public class ClaimCantBeReassignedException : Exception
{
public ClaimCantBeReassignedException(string claimType) : base($"{claimType} can not be reassigned")
{
}
}
public class ClaimsPrincipalAccessor
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ClaimsPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal ClaimsPrincipal => _httpContextAccessor.HttpContext.User;
}
// to register dependency put this into your Startup.cs and inject ClaimsManager into Controller constructor (or other class) the in same way as you do for other dependencies
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddTransient<ClaimsPrincipalAccessor>();
services.AddTransient<ClaimsManager>();
}
}
}
Вы можете обновить заявки для текущего пользователя, реализовав CookieAuthenticationEvents
класс и приоритет ValidatePrincipal
. Там вы можете удалить старую заявку, добавить новую, а затем заменить участника, используяCookieValidatePrincipalContext.ReplacePrincipal
. Это не влияет на претензии, хранящиеся в базе данных. Это использует ASP.NET Core Identity 2.2.
public class MyCookieAuthenticationEvents : CookieAuthenticationEvents
{
string newAccountNo = "102";
public override Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
// first remove the old claim
var claim = context.Principal.FindFirst(ClaimTypes.UserData);
if (claim != null)
{
((ClaimsIdentity)context.Principal.Identity).RemoveClaim(claim);
}
// add the new claim
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(ClaimTypes.UserData, newAccountNo));
// replace the claims
context.ReplacePrincipal(context.Principal);
context.ShouldRenew = true;
return Task.CompletedTask;
}
}
Вам необходимо зарегистрировать класс событий в Startup.cs
:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddScoped<MyCookieAuthenticationEvents>();
services.ConfigureApplicationCookie(o =>
{
o.EventsType = typeof(MyCookieAuthenticationEvents);
});
}
Вы можете добавить сервисы в класс событий, чтобы получить доступ к новому AccountNo
значение, но, согласно предупреждению на этой странице, вам следует избегать слишком дорогих действий:
Предупреждение
Описанный здесь подход срабатывает при каждом запросе. Проверка файлов cookie аутентификации для всех пользователей по каждому запросу может привести к значительному снижению производительности приложения.
Когда я использую MVC5, и добавить претензию здесь.
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(PATAUserManager manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim(ClaimTypes.Role, this.Role));
return userIdentity;
}
когда я проверяю результат заявки в функции SignInAsync, я все равно не могу получить значение роли. Но...
после завершения этого запроса я могу получить доступ к роли в другом действии (другой запрос).
var userWithClaims = (ClaimsPrincipal)User;
Claim CRole = userWithClaims.Claims.First(c => c.Type == ClaimTypes.Role);
Итак, я думаю, что, может быть, асинхронная причина IEnumerable обновляется за процессом.
Оцените, что этот вопрос касался .NET 4/ OWIN, но чтобы помочь поисковикам, ищущим .NET 5 или более поздний эквивалент, вот несколько примеров кода.
Я уверен, что вы можете улучшить его, но это рабочий стартер, использующий
UserManager
а также
SignInManager
в
Microsoft.AspNetCore.Identity
.
// Get the user first first.
var claims = await _userManager.GetClaimsAsync(user);
var givenNameClaim = claims.FirstOrDefault(r => r.Type == JwtClaimTypes.GivenName);
IdentityResult result = null;
if (givenNameClaim != null)
{
result = await _userManager.ReplaceClaimAsync(user, givenNameClaim, new Claim(JwtClaimTypes.GivenName, "<newvalue>"));
}
else
{
result = await _userManager.AddClaimAsync(user, new Claim(JwtClaimTypes.GivenName, "<newvalue>"));
}
if (result.Errors.Any())
{
// TODO: List errors here;
}
else
{
await _signInManager.RefreshSignInAsync(user); // refresh the login, so it takes effect immediately.
}
Я использую приложение.net core 2.2 и следующее решение: в моем statup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
...
})
.AddEntityFrameworkStores<AdminDbContext>()
.AddDefaultTokenProviders()
.AddSignInManager();
Применение
private readonly SignInManager<IdentityUser> _signInManager;
public YourController(
...,
SignInManager<IdentityUser> signInManager)
{
...
_signInManager = signInManager;
}
public async Task<IActionResult> YourMethod() // <-NOTE IT IS ASYNC
{
var user = _userManager.FindByNameAsync(User.Identity.Name).Result;
var claimToUse = ClaimsHelpers.CreateClaim(ClaimTypes.ActiveCompany, JsonConvert.SerializeObject(cc));
var claimToRemove = _userManager.GetClaimsAsync(user).Result
.FirstOrDefault(x => x.Type == ClaimTypes.ActiveCompany.ToString());
if (claimToRemove != null)
{
var result = _userManager.ReplaceClaimAsync(user, claimToRemove, claimToUse).Result;
await _signInManager.RefreshSignInAsync(user); //<--- THIS
}
else ...
Самым простым решением для обновления существующих претензий на данный момент для меня было:
//updating user data
await signInManager.SignOutAsync();
await signInManager.SignInAsync(user, false);
Несколько Cookies, несколько претензий
public class ClaimsCookie
{
private readonly ClaimsPrincipal _user;
private readonly HttpContext _httpContext;
public ClaimsCookie(ClaimsPrincipal user, HttpContext httpContext = null)
{
_user = user;
_httpContext = httpContext;
}
public string GetValue(CookieName cookieName, KeyName keyName)
{
var principal = _user as ClaimsPrincipal;
var cp = principal.Identities.First(i => i.AuthenticationType == ((CookieName)cookieName).ToString());
return cp.FindFirst(((KeyName)keyName).ToString()).Value;
}
public async void SetValue(CookieName cookieName, KeyName[] keyName, string[] value)
{
if (keyName.Length != value.Length)
{
return;
}
var principal = _user as ClaimsPrincipal;
var cp = principal.Identities.First(i => i.AuthenticationType == ((CookieName)cookieName).ToString());
for (int i = 0; i < keyName.Length; i++)
{
if (cp.FindFirst(((KeyName)keyName[i]).ToString()) != null)
{
cp.RemoveClaim(cp.FindFirst(((KeyName)keyName[i]).ToString()));
cp.AddClaim(new Claim(((KeyName)keyName[i]).ToString(), value[i]));
}
}
await _httpContext.SignOutAsync(CookieName.UserProfilCookie.ToString());
await _httpContext.SignInAsync(CookieName.UserProfilCookie.ToString(), new ClaimsPrincipal(cp),
new AuthenticationProperties
{
IsPersistent = bool.Parse(cp.FindFirst(KeyName.IsPersistent.ToString()).Value),
AllowRefresh = true
});
}
public enum CookieName
{
CompanyUserProfilCookie = 0, UserProfilCookie = 1, AdminPanelCookie = 2
}
public enum KeyName
{
Id, Name, Surname, Image, IsPersistent
}
}
Для удаления информации о претензии из базы данных мы можем использовать приведенный ниже код. Кроме того, нам нужно войти снова, чтобы обновить значения файлов cookie.
// create a new identity
var identity = new ClaimsIdentity(User.Identity);
// Remove the existing claim value of current user from database
if(identity.FindFirst("NameOfUser")!=null)
await UserManager.RemoveClaimAsync(applicationUser.Id, identity.FindFirst("NameOfUser"));
// Update customized claim
await UserManager.AddClaimAsync(applicationUser.Id, new Claim("NameOfUser", applicationUser.Name));
// the claim has been updates, We need to change the cookie value for getting the updated claim
AuthenticationManager.SignOut(identity.AuthenticationType);
await SignInManager.SignInAsync(Userprofile, isPersistent: false, rememberBrowser: false);
return RedirectToAction("Index", "Home");
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
identity.RemoveClaim(identity.FindFirst("userId"));
identity.AddClaim(new Claim("userId", userInfo?.id.ToString()));
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(HttpContext.User.Identity));
}
Я знаю старую ветку, но требования, похоже, изменились.
Ниже работает для меня:
var user = await _userManager.FindByEmailAsync(input.Email);
var userClaim = await _userManager.GetClaimsAsync(user);
var userNameClaims = userClaim.Where(x => x.Type == ClaimTypes.GivenName).ToList();
await _userManager.RemoveClaimsAsync(user, userNameClaims);
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.GivenName, user.Forename));
await _signInManager.SignOutAsync();
await _signInManager.SignInAsync(user, new AuthenticationProperties() { IsPersistent = input.RememberMe });
Методы выхода и входа обязательны, без претензий не отражают изменения
Метод расширения работал отлично для меня, за одним исключением, что если пользователь выходит из системы, старые наборы заявок все еще существуют, поэтому с незначительной модификацией, как при передаче usermanager через все, работает отлично, и вам не нужно выходить из системы и входить в систему. Я не могу ответить прямо, так как моя репутация была отклонена:(
public static class ClaimExtensions
{
public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value, ApplicationUserManager userManager)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return;
// check for existing claim and remove it
var existingClaim = identity.FindFirst(key);
if (existingClaim != null)
{
RemoveClaim(currentPrincipal, key, userManager);
}
// add new claim
var claim = new Claim(key, value);
identity.AddClaim(claim);
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
//Persist to store
userManager.AddClaim(identity.GetUserId(),claim);
}
public static void RemoveClaim(this IPrincipal currentPrincipal, string key, ApplicationUserManager userManager)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return ;
// check for existing claim and remove it
var existingClaims = identity.FindAll(key);
existingClaims.ForEach(c=> identity.RemoveClaim(c));
//remove old claims from store
var user = userManager.FindById(identity.GetUserId());
var claims = userManager.GetClaims(user.Id);
claims.Where(x => x.Type == key).ToList().ForEach(c => userManager.RemoveClaim(user.Id, c));
}
public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return null;
var claim = identity.Claims.First(c => c.Type == key);
return claim.Value;
}
public static string GetAllClaims(this IPrincipal currentPrincipal, ApplicationUserManager userManager)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return null;
var claims = userManager.GetClaims(identity.GetUserId());
var userClaims = new StringBuilder();
claims.ForEach(c => userClaims.AppendLine($"<li>{c.Type}, {c.Value}</li>"));
return userClaims.ToString();
}
}
Ну вот:
var user = User as ClaimsPrincipal;
var identity = user.Identity as ClaimsIdentity;
var claim = (from c in user.Claims
where c.Type == ClaimTypes.UserData
select c).Single();
identity.RemoveClaim(claim);
взяты отсюда.