0

I'm developing an ASP.NET Core 3.1 Web API with an endpoint to add a new claim onto an existing User Identity. The claim successfully adds to the identity, however, on subsequent requests, the added claim is not in the collection of claims, so not available. I have also tried adding a new identity, assigning it the claim, similarly on subsequent requests, the added identity is not in the collection of identities. Any ideas?

var claims = new List<Claim>()
{
    new Claim("token","value")
}

var identity = httpContextAccessor.HttpContext.User.Identities.FirstOrDefault();
identity.AddClaims(claims);
yhamma
  • 1
  • 1
  • 1
  • 1
    The HttpContext is unique to an individual request. Adding a claim to the identity of request (a) will do nothing for a subsequent request (b). – BlueWater86 Dec 14 '20 at 21:38

2 Answers2

0

To persist newly added claims to an existing HttpContext user, you could implement custom CustomClaimsPrincipalFactory. Make these steps:

public class AppUser : IdentityUser
{
    public string CompanyName { get; set; }
    public string DisplayName { get; set; }
}

public static class IdentityExtensions
{
    public static string FullName(this IIdentity identity)   //@User.Identity.FullName()
    {
        var claim = ((ClaimsIdentity)identity).FindFirst("DisplayName");
        // Test for null to avoid issues during local testing
        return (claim != null) ? claim.Value : string.Empty;
    }

    public static string GetCompanyName(this IIdentity identity)
    {
        if (identity == null)
            throw new ArgumentNullException(nameof(identity));

        var claim = ((ClaimsIdentity)identity).FindFirst("CompanyName");
        // Test for null to avoid issues during local testing
        return (claim != null) ? claim.Value : string.Empty;
    }
}

public class CustomClaimsPrincipalFactory : UserClaimsPrincipalFactory<AppUser, IdentityRole>
{
    public CustomClaimsPrincipalFactory(UserManager<AppUser> userManager, RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> optionsAccessor)
        : base(userManager, roleManager, optionsAccessor)
    {
    }

    public override async Task<ClaimsPrincipal> CreateAsync(AppUser user)
    {
        if (user == null)
            throw new ArgumentNullException(nameof(user));

        var principal = await base.CreateAsync(user);

        // Add your claims here
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("CompanyName", user.CompanyName));
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("DisplayName", user.DisplayName));

        return principal;
    }
}

Register CustomClaimsPrincipalFactory

    public void Configure(IServiceCollection services)
    {
        ...
            services.AddScoped<IUserClaimsPrincipalFactory<AppUser>, CustomClaimsPrincipalFactory>();
        });
    }
Majid Shahabfar
  • 4,010
  • 2
  • 28
  • 36
0

You need to call _signInManager.Context.SignInAsync with updated ClaimsIdentity.

Here is a working demo:

1.Extension for signin with new ClaimsIdentity:

public class CustomClaimsCookieSignInHelper<TIdentityUser> where TIdentityUser : IdentityUser
{
    private readonly SignInManager<TIdentityUser> _signInManager;

    public CustomClaimsCookieSignInHelper(SignInManager<TIdentityUser> signInManager)
    {
        _signInManager = signInManager;
    }

    public async Task SignInUserAsync(ClaimsIdentity claimsIdentity)
    {
        await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme, new ClaimsPrincipal(claimsIdentity));
    }

}

2.Register CustomClaimsCookieSignInHelper<TIdentityUser>:

services.AddTransient<CustomClaimsCookieSignInHelper<IdentityUser>>();

3.Update User Claims:

public class IndexModel : PageModel
{
    private readonly CustomClaimsCookieSignInHelper<IdentityUser> _signInHelper;

    public IndexModel(CustomClaimsCookieSignInHelper<IdentityUser> signInHelper)
    {
        _signInHelper = signInHelper;            
    }       

    public async Task<IActionResult> OnGetAsync()
    {
        var claims = new List<Claim>()
        {
            new Claim("token","value")
        };

        var identity = HttpContext.User.Identities.FirstOrDefault();
        identity.AddClaims(claims);
        await _signInHelper.SignInUserAsync(identity);
        return Page();
    }
}

BTW,if you use jwt authentication,When server side get the API call with token , the AddJwtBearer will decode token ,validate token and make user authenticated, you can add new claims either in OnTokenValidated or in custom middleware. But the claims won't persist in next api calls.So if you want to get updated claim in another request, a new token must be issued.

Rena
  • 30,832
  • 6
  • 37
  • 72