The SAML component will use OpenIddict to retrieve all the requested claims for a user and then map those OIDC claim types into SAML claim types. Check out the OpenIddict documentation for how OpenIddict handles claims.
Claims Mapping
To map from OIDC claims to SAML claims, you can configure a claims mapping on a per Service Provider level or at a global level using the startup configuration options (SamlIdpOptions
).
If you set the claims mapping on a ServiceProvider
, it will overwrite the global mappings.
You can find our default claim mappings in our options documentation.
Setup claims mapping per ServiceProvider
using the ClaimsMapping
property:
new ServiceProvider
{
// Other configuration code removed for brevity
ClaimsMapping = new Dictionary<string, string>
{
{ "name", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"},
// Other mappings
}
},
Setup claims mapping at the global level using the DefaultClaimsMapping
option:
services.AddOpenIddict()
// Other configuration code removed for brevity
.AddSamlPlugin(options =>
{
options.DefaultClaimMapping.Add("name", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
})
.AddInMemoryServiceProviders(new List<ServiceProvider>());
Requesting Additional Claims
The SAML IdP component supports adding additional assertion attributes.
To add an assertion attribute:
- Define and register an IdentityResource that authorizes access to the JWT style claims types.
services.AddOpenIddict()
// Other configuration code removed for brevity
.AddServer(options =>
{
options.RegisterScopes(Scopes.OpenId, Scopes.Profile, "custom");
});
- Authorize the Service Provider's Client record to access your new resource.
new Client
{
ClientId = "https://local.sp",
ClientName = "Local Client",
AllowedScopes = {"openid", "profile", "custom"}
}
- Map the JWT style claim type to the attribute type you want to use in SAML assertions by adding it to the
ClaimsMapping
property on theServiceProvider
object or theDefaultClaimsMapping
option on the globalSamlIdpOptions
class.
{"name", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"}
If you have everything set up correctly and the user has a claim of type "name", then you should start seeing an attribute with a type of "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" in your SAML assertions.
Managing User Claims
You may need to update/change the user claims, for example:
- Add custom claims retrieved from another service or an API
- Issue static claims, such as the Identity Provider name
- Change the claims for a specific service provider
- Group multiple user claims into a single assertion attribute
- Change how complex claim values are serialized into a string in SAML assertions. For example, converting an object to JSON or XML
When configuring OpenIddict, you can specify how claims are included in tokens. This is done in the startup configuration, where you can map specific claims to be included in access tokens, ID tokens, or both. OpenIddict allows for fine-grained control over which claims are included in tokens and can use the claims provided by ASP.NET Core Identity or any other claims available in the user's ClaimsPrincipal. We recommend using the default OpenIddict methods to manage user claims based on your use case. The SAML component will retrieve the requested claims from your OpenIddict configuration and map them to SAML claims.
If you need to modify the claims for only SAML service providers, you can extend our ISamlClaimsService
.
This service retrieves and maps the OIDC claims to SAML claims. You can extend this service to modify the claims before they are included in the SAML assertion.
Here's an example of extending the ISamlClaimsService
:
public class CustomSamlClaimsService : ISamlClaimsService
{
private readonly ISamlClaimsService defaultClaimsService;
public CustomSamlClaimsService(ISamlClaimsService defaultClaimsService)
{
this.defaultClaimsService = defaultClaimsService ?? throw new ArgumentNullException(nameof(defaultClaimsService));
}
public Task<IList<Claim>> GetUserClaims(ClaimsPrincipal subject, SamlClient client)
{
var claims = defaultClaimsService.GetUserClaims(subject, client);
if (client.ClientId == "sp")
{
// modify claim values
}
return claims;
}
public IEnumerable<Claim> MapToSamlClaims(IDictionary<string, string> claimsMapping, IEnumerable<Claim> claims)
=> defaultClaimsService.MapToSamlClaims(claimsMapping, claims);
}
Lastly, register your custom implementation in the DI container, which you must do after registering our component, as the DI container will use the last registered implementation of ISamlClaimsService
. For example:
services.AddTransient<SamlClaimsService>();
// Decorate SamlClaimsService
services.AddTransient<ISamlClaimsService, CustomSamlClaimsService>(provider => new CustomSamlClaimsService(provider.GetRequiredService<SamlClaimsService>()));