Search code examples
asp.net-coreblazorblazor-client-sideblazor-webassemblyasp.net-blazor

Define claims using the UserClaimsPrincipalFactory


For several days I try to declare claims for a Blazor application. I finally found a way to do it which is supposed to work but for some reason I can not find my claim in the claims list. I created the following AppClaimsPrincipalFactory:

public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
    public AppClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor)
    {
    }
    public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var id = await GenerateClaimsAsync(user);
        if (user != null)
        {
            id.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName));
        }
        return new ClaimsPrincipal(id);


        var principal = await base.CreateAsync(user);

        if (!string.IsNullOrWhiteSpace(user.FirstName))
        {
            ((ClaimsIdentity)principal.Identity).AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName));
        }

        if (!string.IsNullOrWhiteSpace(user.LastName))
        {
            ((ClaimsIdentity)principal.Identity).AddClaim(new Claim(ClaimTypes.Surname, user.LastName));
        }

        return principal;
    }
}

I added the following code in the ConfigureServices of the Startup.cs file:

services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();

Can someone help me find what is wrong with my code? Thank you in advance.

UPDATE

Thanks to Brian Parker who paved the way to me. In order to make it work in case someone else needs it, I changed the AppClaimsPrincipalFactory class like this:

public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
    public AppClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor)
    {
    }
    protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
    {
        ClaimsIdentity claims = await base.GenerateClaimsAsync(user);
        claims.AddClaim(new Claim("name", "Aris"));
        claims.AddClaim(new Claim("customClaim", "test value"));
        claims.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName));
        claims.AddClaim(new Claim(ClaimTypes.Surname, user.LastName));
        return claims;
    }
}

I also changed the code services.AddDefaultIdentity in my Startup.cs file:

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddClaimsPrincipalFactory<AppClaimsPrincipalFactory>();

which had as a result to have the following claim values:

name: ["[email protected]","Aris"]
customClaim: test value
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname: Aristotelis
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname: Pitaridis

Thank you Brian Parker. You made my day.


Solution

  • Try :

    services.AddScoped<AppClaimsPrincipalFactory>();
    
    services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddClaimsPrincipalFactory<AppClaimsPrincipalFactory>();
    
    services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.IdentityResources["openid"].UserClaims.Add(ClaimTypes.GivenName);
        options.ApiResources.Single().UserClaims.Add(ClaimTypes.GivenName);
    
        options.IdentityResources["openid"].UserClaims.Add(ClaimTypes.Surname);
        options.ApiResources.Single().UserClaims.Add(ClaimTypes.Surname);
    
    });