Publish Claims in the IdToken in IdentityServer4

general csharp aspnetcore oauth2

By default, claims are published in the access token. However, there may be some claims that you may wish to publish in the Id Token.

1 - Create an IProfileService

public class IdentityProfileService : IProfileService
{

  private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
  private readonly UserManager<ApplicationUser> _userManager;

  public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager)
  {
      _claimsFactory = claimsFactory;
      _userManager = userManager;
  }

	public Task GetProfileDataAsync(ProfileDataRequestContext context)
	{
	    context.AddRequestedClaims(context.Subject.Claims);

	    // Add claims to access token
	    if (context.Caller == "ClaimsProviderAccessToken")
	    {
	        context.IssuedClaims.Add(new Claim("custom1", "custom1"));
	    }

	    // Add identity token claims
	    if (context.Caller == "ClaimsProviderIdentityToken") (1)
	    {
	        context.IssuedClaims.Add(new Claim("custom1", "custom1"));

	        context.IssuedClaims.Add(new Claim("custom2", "custom2"));
	    }

	    // Add userinfo endpoint claims
	    if (context.Caller == "UserInfoEndpoint")
	    {
	        context.IssuedClaims.Add(new Claim("custom1", "custom1"));

	        context.IssuedClaims.Add(new Claim("custom2", "custom2"));
	    }

	    return Task.CompletedTask;
	}

  public async Task IsActiveAsync(IsActiveContext context)
  {
      var sub = context.Subject.GetSubjectId();
      var user = await _userManager.FindByIdAsync(sub);
      context.IsActive = user != null;
  }
}
1 The GetProfileDataAsync method gets called multiple times for each type of context.caller - "ClaimsProviderAccessToken", "ClaimsProviderIdentityToken", and "UserInfoEndpoint". Here we add a claim when the caller is "ClaimsProviderIdentityToken" and these claims show up in the IdToken.

2 - In Startup.cs, add the IProfileService to the services collection.

services.AddIdentityServer()
       .AddTemporarySigningCredential()
       .AddInMemoryIdentityResources(Configs.IdentityServerConfig.GetIdentityResources())
       .AddInMemoryApiResources(Configs.IdentityServerConfig.GetApiResources())
       .AddInMemoryClients(Configs.IdentityServerConfig.GetClients())
       .AddAspNetIdentity<ApplicationUser>()
       .AddProfileService<IdentityProfileService>() (1)
1 Add the IdentityProfileService to the services collection.

3 The client should have AlwaysIncludeUserClaimsInIdToken = true set

 public static IEnumerable<Client> GetClients()
{
    //Create clients list like webui, console applications and...
  List<Client> clients = new List<Client>();

  //Add WebUI client
  Client webUi = new Client();
  webUi.ClientId = "U2EQlBHfcbuxUo";
  webUi.ClientSecrets.Add(new Secret("TbXuRy7SSF5wzH".Sha256()));
  webUi.ClientName = "WebUI";
  webUi.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
  webUi.RequireConsent = false;
  webUi.AllowOfflineAccess = true;
  webUi.AlwaysSendClientClaims = true;
  webUi.AlwaysIncludeUserClaimsInIdToken = true; (1)
  webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
  webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
  webUi.AllowedScopes.Add("ApplicationApi");
  webUi.AllowedScopes.Add("DefinitionApi");
  webUi.AllowedScopes.Add("FFAPI");
  webUi.ClientUri = "http://localhost:5003";
  webUi.RedirectUris.Add("http://localhost:5003/signin-oidc");
  webUi.PostLogoutRedirectUris.Add("http://localhost:5003/signout-callback-oidc");
  clients.Add(webUi);
}
1 AlwaysIncludeUserClaimsInIdToken is set to true.

References