The SAML component will use IdentityServer to retrieve all the requested claims for a user and then map those OIDC claim types into SAML claim types. Check out the IdentityServer documentation for how IdentityServer 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.AddIdentityServer()
// 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.AddIdentityServer()
// Other configuration code removed for brevity
.AddInMemoryIdentityResources(new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource("custom", new List<string>{ "name" })
});
- 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
The IdentityServer IProfileService
encapsulates the retrieval of user claims.
We recommend extending the default implementation of the IProfileService
to manage user claims based on your use case.
The SAML component will retrieve the requested claims from your IProfileService
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.
Our default implementation uses the IdentityServer IProfileService
.
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>()));