Last active
December 10, 2021 22:09
-
-
Save dsolovay/31ba7e84c2bb57ec67981f8c0b5af8d0 to your computer and use it in GitHub Desktop.
Code samples for Part 2 of Sustainsys.SAML2 Sitecore Identity plugin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Security; | |
using System.Security.Claims; | |
using System.Security.Cryptography; | |
using System.Security.Cryptography.X509Certificates; | |
using System.Threading; | |
using IdentityServer4; | |
using IdentityServer4.Configuration; | |
using Microsoft.AspNetCore.Authentication; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.AspNetCore.Hosting.Server; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Logging; | |
using Microsoft.Extensions.Primitives; | |
using Sitecore.Framework.Runtime.Configuration; | |
using Sustainsys.Saml2; | |
using Sustainsys.Saml2.Configuration; | |
using Sustainsys.Saml2.Metadata; | |
using Sustainsys.Saml2.WebSso; | |
namespace SitecoreIdentitySamlDemo | |
{ | |
public class ConfigureSitecore | |
{ | |
private Saml2Settings _settings; | |
private readonly ISitecoreConfiguration _sitecoreConfiguration; | |
private readonly ILogger _logger; | |
public ConfigureSitecore(ISitecoreConfiguration sitecoreConfiguration, ILogger<ConfigureSitecore> logger) | |
{ | |
_sitecoreConfiguration = sitecoreConfiguration; | |
_logger = logger; | |
_settings = new Saml2Settings(); | |
_sitecoreConfiguration.Bind("Sitecore:ExternalIdentityProviders:IdentityProviders:Saml2Configuration", _settings); | |
} | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
if (!_settings.Enabled) | |
{ | |
return; | |
} | |
var builder = new AuthenticationBuilder(services); | |
builder.AddSaml2(_settings.AuthenticationScheme, _settings.DisplayName, options => | |
{ | |
// Required by Sitecore Identity. See https://doc.sitecore.com/xp/en/developers/102/sitecore-experience-manager/use-the-sitecore-identity-server-as-a-federation-gateway.html | |
options.SignInScheme = "idsrv.external"; | |
options.SignOutScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme; | |
options.SPOptions.EntityId = new EntityId(_settings.EntityId); | |
IdentityProvider provider = GetIdentityProvider(options.SPOptions); | |
options.IdentityProviders.Add(provider); | |
if (!string.IsNullOrEmpty(_settings.PfxPath) && !string.IsNullOrEmpty(_settings.PfxPassword)) | |
{ | |
options.SPOptions.ServiceCertificates.Add(GetCertificate()); | |
options.SPOptions.AuthenticateRequestSigningBehavior = | |
Enum.TryParse(_settings.AuthenticationRequestSigningBehavior, ignoreCase: true, out SigningBehavior result) | |
? result | |
: SigningBehavior.IfIdpWantAuthnRequestsSigned; | |
options.SPOptions.OutboundSigningAlgorithm = _settings.OutboundSigningAlgorithm.ConvertNullOrEmptyTo("SHA256"); | |
} | |
options.Notifications.AcsCommandResultCreated += (result, response) => | |
{ | |
ClaimsIdentity theClaimsHolder = result.Principal.Identity as System.Security.Claims.ClaimsIdentity; | |
IEnumerable<Claim> incomingClaims = theClaimsHolder.Claims; | |
// Any claims removed from "incomingClaims" will not be added to the cookie, avoiding the error: "The size of the request headers is too long." | |
// See https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB0522544 for the issue, though this fix is specific to Sustainsys.SAML2. | |
}; | |
}); | |
} | |
private X509Certificate2 GetCertificate() | |
{ | |
byte[] cert = File.ReadAllBytes(_settings.PfxPath); | |
SecureString ss = new SecureString(); | |
_settings.PfxPassword.ToCharArray().ToList().ForEach(c => ss.AppendChar(c)); | |
return new X509Certificate2(cert, ss); | |
} | |
private IdentityProvider GetIdentityProvider(SPOptions options) | |
{ | |
var idp = new IdentityProvider(new EntityId(_settings.IdentityProviderId), options); | |
idp.Binding = Saml2BindingType.HttpPost; | |
// "Load metadata from the idp and use that information instead of the configuration. It is possible to use a specific certificate | |
// even though the metadata is loaded, in that case the configured certificate will take precedence over any contents in the metadata." | |
// See https://saml2.sustainsys.com/en/stable/config-elements/identity-providers.html?highlight=loadmetadata#identityproviders-element | |
idp.LoadMetadata = true; | |
return idp; | |
} | |
} | |
// https://stackoverflow.com/a/2420155/402949 | |
public static class StringExtensions | |
{ | |
public static string ConvertNullOrEmptyTo(this string input, string defaultValue) | |
{ | |
return !string.IsNullOrEmpty(input) ? input : defaultValue; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNet.Identity; | |
using Microsoft.AspNet.Identity.Owin; | |
using Sitecore.Owin.Authentication.Identity; | |
using Sitecore.Owin.Authentication.Services; | |
using Sitecore.SecurityModel.Cryptography; | |
namespace CustomUserBuilder | |
{ | |
public class CustomExternalUserBuilder: DefaultExternalUserBuilder | |
{ | |
public CustomExternalUserBuilder(ApplicationUserFactory applicationUserFactory, IHashEncryption hashEncryption) : base(applicationUserFactory, hashEncryption) | |
{ | |
} | |
protected override string CreateUniqueUserName(UserManager<ApplicationUser> userManager, ExternalLoginInfo externalLoginInfo) | |
{ | |
string hashDerivedName = base.CreateUniqueUserName(userManager, externalLoginInfo); | |
string domain = hashDerivedName.Substring(0, hashDerivedName.IndexOf('\\')); | |
string providerKeyName = domain + @"\" + externalLoginInfo.Login.ProviderKey; | |
// Check name not already assigned. | |
return userManager.FindByName(providerKeyName) == null ? providerKeyName : hashDerivedName; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" | |
xmlns:set="http://www.sitecore.net/xmlconfig/set"> | |
<sitecore> | |
<federatedAuthentication> | |
<propertyInitializer> | |
<maps> | |
<map name="set FullName" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true"> | |
<data hint="raw:AddData"> | |
<source name="name" /> | |
<target name="FullName" /> | |
</data> | |
</map> | |
<map name="set Email" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true"> | |
<data hint="raw:AddData"> | |
<source name="email" /> | |
<target name="Email" /> | |
</data> | |
</map> | |
<map name="set Portait" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true"> | |
<data hint="raw:AddData"> | |
<source name="portrait" /> | |
<target name="Portrait" /> | |
</data> | |
</map> | |
</maps> | |
</propertyInitializer> | |
<identityProvidersPerSites> | |
<mapEntry name ="all sites"> | |
<externalUserBuilder set:type="CustomUserBuilder.CustomExternalUserBuilder, CustomUserBuilder" resolve="true"> | |
<IsPersistentUser>true</IsPersistentUser> | |
</externalUserBuilder> | |
</mapEntry> | |
</identityProvidersPerSites> | |
</federatedAuthentication> | |
</sitecore> | |
</configuration> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace SitecoreIdentitySamlDemo | |
{ | |
internal class Saml2Settings : Sitecore.Plugin.IdentityProviders.IdentityProvider | |
{ | |
public string EntityId { get; set; } | |
public string PfxPath { get; set; } | |
public string PfxPassword { get; set; } | |
public string AuthenticationRequestSigningBehavior { get; set; } | |
public string OutboundSigningAlgorithm { get; set; } | |
public string IdentityProviderId { get; set; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Settings> | |
<Sitecore> | |
<!-- Add the "portrait" claim --> | |
<IdentityServer> | |
<IdentityResources> | |
<SitecoreIdentityResource> | |
<UserClaims> | |
<Portait>portrait</Portait> | |
</UserClaims> | |
</SitecoreIdentityResource> | |
</IdentityResources> | |
</IdentityServer> | |
<!-- Plugin configuration --> | |
<ExternalIdentityProviders> | |
<IdentityProviders> | |
<Saml2Configuration type="Sitecore.Plugin.IdentityProviders.IdentityProvider, Sitecore.Plugin.IdentityProviders"> | |
<AuthenticationScheme>Saml2</AuthenticationScheme> | |
<DisplayName>Sustainssys SAML2 Stub Login</DisplayName> | |
<Enabled>true</Enabled> | |
<EntityId>https://testidserver2/Saml2</EntityId> | |
<PfxPath>.\mycert.pfx</PfxPath> | |
<PfxPassword>secret</PfxPassword> | |
<AuthenticationRequestSigningBehavior>Always</AuthenticationRequestSigningBehavior> | |
<IdentityProviderId>https://stubidp.sustainsys.com/Metadata</IdentityProviderId> | |
<ClaimsTransformations> | |
<ClaimsTransformation1 type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders"> | |
<SourceClaims> | |
<Claim1 type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" /> | |
</SourceClaims> | |
<NewClaims> | |
<Claim1 type="email" /> | |
</NewClaims> | |
</ClaimsTransformation1 > | |
<ClaimsTransformation2 type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders"> | |
<SourceClaims> | |
<Claim1 type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" /> | |
</SourceClaims> | |
<NewClaims> | |
<Claim1 type="email" /> | |
</NewClaims> | |
</ClaimsTransformation2> | |
<AuthorRule type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders"> | |
<SourceClaims> | |
<Claim1 type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="Author" /> | |
</SourceClaims> | |
<NewClaims> | |
<Claim1 type="role" value="sitecore\Author" /> | |
<Claim2 type="portrait" value="office/16x16/pencil.png" /> | |
</NewClaims> | |
</AuthorRule> | |
<AdminRule type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders"> | |
<SourceClaims> | |
<Claim1 type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="Administrator" /> | |
</SourceClaims> | |
<NewClaims> | |
<Claim1 type="http://www.sitecore.net/identity/claims/isAdmin" value="true"/> | |
<Claim2 type="portrait" value="office/16x16/magician.png" /> | |
</NewClaims> | |
</AdminRule> | |
<DisplayNameRule type="Sitecore.Plugin.IdentityProviders.DefaultClaimsTransformation, Sitecore.Plugin.IdentityProviders"> | |
<SourceClaims> | |
<Claim1 type="DisplayName" /> | |
</SourceClaims> | |
<NewClaims> | |
<Claim1 type="name" /> | |
</NewClaims> | |
</DisplayNameRule> | |
</ClaimsTransformations> | |
</Saml2Configuration> | |
</IdentityProviders> | |
</ExternalIdentityProviders> | |
</Sitecore> | |
</Settings> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See https://www.velir.com/ideas/2021/12/10/how-to-finish-connecting-sitecore-identity-to-saml-for-sso