As OpenIddict is an OAuth/OIDC implementation it uses the concept of scopes to define token access scope. Although the default handler set for OpenIddict will reject requests containing unauthorized scopes, it contains no framework mechanism to map scopes to User claims or token destinations. Instead, it expects the claims on the ClaimsPrincipal signed in to the OpenIddict authentication scheme to have Destination
properties.
As this mapping logic is expected to happen during endpoint pass-through individual implementations may vary, the OpenIddict Velusia sample is used as an example.
Get Destinations from the OpenIddict Velusia Sample
During Endpoint-passthrough a ClaimsPrincipal is generated using Claims from the authenticated User, and then a destination is written to each Claim's Property dictionary decided by theGetDestinations
function. When the ClaimsPrincipal is signed into the OpenIddict scheme, the framework will exit pass-through and continue processing the request. When tokens are generated the Claims destinations will determine if the Claim should be placed into the access token, identity token, or both.
private static IEnumerable<string> GetDestinations(Claim claim)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
switch (claim.Type)
{
case Claims.Name:
yield return Destinations.AccessToken;
if (claim.Subject.HasScope(Scopes.Profile))
yield return Destinations.IdentityToken;
yield break;
case Claims.Email:
yield return Destinations.AccessToken;
if (claim.Subject.HasScope(Scopes.Email))
yield return Destinations.IdentityToken;
yield break;
case Claims.Role:
yield return Destinations.AccessToken;
if (claim.Subject.HasScope(Scopes.Roles))
yield return Destinations.IdentityToken;
yield break;
// Never include the security stamp in the access and identity tokens, as it's a secret value.
case "AspNet.Identity.SecurityStamp": yield break;
default:
yield return Destinations.AccessToken;
yield break;
}
}
SAML Claims Mapping
The Saml component for OpenIddict requires each configured ServiceProvider to have a matching OpenIddict Application (OIDC Client object), that connection is made via a matching Saml EntityId
and OIDC ClientId
, see configuring a ServiceProvider for more information. The Application's configured Scopes are used to control what data is inserted into token responses. However, in the case of Saml, it's simplified as there is only one destination.
This data is required as Saml Responses must contain at least one assertion, if a Scope contains no claim types, no assertions will be generated for the Scope. To provide this data the IOpenIddictSamlScopeClaimMapper
interface is available.
/// <summary>
/// Maps a <see cref="OpenIddictScopeDescriptor"/> to a set of claims.
/// </summary>
public interface IOpenIddictSamlScopeClaimMapper
{
/// <summary>
/// Map a users claim types from an OpenIddict scope.
/// </summary>
/// <param name="scope">to scope from which to retrieve claims.</param>
/// <returns>The available claim types from the <paramref name="scope"/>.</returns>
///<exception cref="ArgumentNullException">Thrown when the <paramref name="scope"/> is <c>null</c>.</exception>
Task<IList<string>> MapScopeToClaimTypes(object scope);
}
A default implementation named OpenIddictSamlScopeClaimMapper
is configured by the component. This implementation will use the IOpenIddictScopeManager
to retrieve the scope and check the ImmutableDictionary<string,JsonElement>
Properties
property for a Claims
object containing a array of string values corresponding to ClaimType
values.
The Velusia sample uses a Worker
class to handle database seeding, to configure a Scope to use the OpenIddictSamlScopeClaimMapper
and add a Claims
property containing a Json Serialized list of string values. OpenIddictScopeDescriptor
extension methods AddClaimTypes
and SetClaimTypes
are available to allow you to configure ClaimTypes.
The difference between AddClaimTypes
and SetClaimTypes
is that AddClaimTypes
overwrites any claim types that currently exist in the properties dictionary on the scope descriptor whereas SetClaimTypes
deduplicates and appends the passed in claim type values to the properties dictionary on the scope descriptor.
var emailScopeDescriptor = new OpenIddictScopeDescriptor()
{
//...
};
emailScopeDescriptor.AddClaimTypes("email");
await scopeManager.CreateAsync(emailScopeDescriptor);