This quickstart builds upon Acting as an SAML Service Provider Quickstart in your ASP.NET Core application.
We will discuss the scenario where you have an IdentityServer federating with an external SAML IdP.
Hence, IdentityServer is acting as a SAML Service Provider (SP) to an external IdP.
We recommend following the IdentityServer recommendation for integrating with an external IdP.
This page will only cover the SAML-specific advice. Please refer to the IdentityServer documentation for a thorough explanation of the entire login process.
Temporary Sign-in Scheme
A temporary cookie is used to remember the identity parsed from the external IdP. A callback method is then used to perform custom business logic to map claims and link the external user to a local user.
Our SAML component doesn't map to OIDC claims automatically, so it will never include the "sub" claim that IdentityServer needs.
To handle this when you are retrieving claims information from the enteral cookie, you should look for both the JWT and XML ClaimTypes.
public async Task<IActionResult> OnGet()
{
// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
var externalUser = result.Principal;
if (_logger.IsEnabled(LogLevel.Debug))
{
var externalClaims = externalUser.Claims.Select(c => $"{c.Type}: {c.Value}");
_logger.LogDebug("External claims: {@claims}", externalClaims);
}
// lookup our user and external provider info
// try to determine the unique id of the external user (issued by the provider)
// the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
throw new Exception("Unknown userid");
}
So using this recommended approach, you can ensure that your main authentication cookie has all the required claims that IdentityServer needs. Otherwise, IdentityServer will throw a "sub claim is missing" error.
Sign-out Scheme
When implementing Single Logout, you must set the SignOutScheme
configuration option to the main authentication cookie that you sign the user into.
The SAML logout request needs to contain the NameID of the user that is requesting logout.
We get the current user from the cookie you specify as the SignoutScheme
.
Therefore, SAML logout will fail if the SignoutScheme
is not set to the main authentication cookie.
services.AddAuthentication()
.AddSaml2p("saml2p", options => {
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme;
options.NameIdClaimType = "sub";
// Other configuration code removed for brevity
});
Code Sample
We have an IdentityServer sample configured to act as a SAML SP to an external SAML IdP. The logic for triggering and handling the external sign-on is handled within the ExternalController.cs.