Я добавляю аутентификацию SAML2 в приложение ASP. NET MVC5, которое использует OWIN. Я интегрировал Sustainsys.Saml2.Owin
в приложение, я правильно перенаправлен в сконфигурированный Identity Provider для входа в систему, и Identity Provider возвращает правильный ответ (проверено с помощью SAML-Tracer), но однажды /Account/ExternalLoginCallback
вызывается в приложении ( у провайдера идентификации есть, что в качестве URL-адреса ACS) объект AuthenticationResponseGrant
имеет значение NULL, и аутентификационный код ie никогда не генерируется.
Startup.cs
код:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
using Sustainsys.Saml2;
using Sustainsys.Saml2.Configuration;
using Sustainsys.Saml2.Metadata;
using Sustainsys.Saml2.Owin;
using Sustainsys.Saml2.WebSso;
using System;
using System.Security.Cryptography.X509Certificates;
using System.Web.Hosting;
using TestAuthCMPC.WebSite.Models;
namespace TestAuthCMPC.WebSite
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseSaml2Authentication(GetSamlOptions());
}
private static Saml2AuthenticationOptions GetSamlOptions()
{
var provider_options = GetServiceProviderOptions();
var options = new Saml2AuthenticationOptions(false)
{
SPOptions = provider_options
};
var idp = GetIDP(
"ENTITY_ID_OMITTED",
providerOptions,
new Uri("SSO_URL_OMITTED"),
"~/App_Data/saml_cert.crt");
options.IdentityProviders.Add(idp);
new Federation("http://localhost:64484/Federation", true, options);
return options;
}
private static IdentityProvider GetIDP(string entityId, SPOptions providerOptions, Uri ssoUri, string certificatePath)
{
var idp = new IdentityProvider(new EntityId(entityId), providerOptions)
{
AllowUnsolicitedAuthnResponse = true,
Binding = Saml2BindingType.HttpRedirect,
SingleSignOnServiceUrl = ssoUri
};
var cert_path = HostingEnvironment.MapPath(certificatePath);
var certificate = new X509Certificate2(cert_path);
idp.SigningKeys.AddConfiguredKey(certificate);
return idp;
}
private static SPOptions GetServiceProviderOptions()
{
var options = new SPOptions
{
EntityId = new EntityId("ENTITY_ID_OMITTED"),
ReturnUrl = new Uri("http://localhost:64484/Account/ExternalLoginCallback"),
};
var attribute_consuming_service = new AttributeConsumingService
{
IsDefault = true,
ServiceNames = { new LocalizedName("Saml2", "en") }
};
var requested_attr = new RequestedAttribute("urn:someName")
{
FriendlyName = "Some Name",
IsRequired = true,
NameFormat = RequestedAttribute.AttributeNameFormatUri
};
attribute_consuming_service.RequestedAttributes.Add(requested_attr);
options.AttributeConsumingServices.Add(attribute_consuming_service);
return options;
}
}
}
Ответ SAML от провайдера идентификации:
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_9ee7823f-88e5-400b-8ae2-9abd48c39a94"
Version="2.0"
IssueInstant="2020-03-02T15:18:50.813Z"
Destination="https://www.google.cl"
>
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">ENTITY_ID_OMITTED</saml:Issuer>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#_9ee7823f-88e5-400b-8ae2-9abd48c39a94">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#"
PrefixList="#default samlp saml ds xs xsi"
/>
</Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>oqed0BjPY97IykUdJznlrlb57J0=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Hc2x/xLMtAnxfQIbs/lVXODsH6D1vfkghWQMsUXooy2TEcXQiJUYiU8T80ciCU70rMB/CBVwjjE7ArHh4l4UknmcS2ZNznyeIqdHM+JocOrH0TSzcd/BBznjAYDcI6PuzlSPFQEyaHtGqHm0Ya7JPibYLE3W8MIXo06lF9cWW9kBXu+YjBcANoplTz4K9so3fuclxOBQ/uPbJf11x7yYFcRIdZW6BJ6tcLxb5gI2/wT9wtEGZ1VaLPPXmnIou/avZmE0Jg/GLE12asssHlcqbCKQZCm6imi8xnuXUNhqiPmt342Icy0DidZSJGIo0GaRukMRyahoS64ZZdkSwfGKkQ==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIID7zCCAtegAwIBAgIUWJQQ8iGfhdHnm/2zx49/8FQEu+owDQYJKoZIhvcNAQELBQAwgYYxCzAJBgNVBAYTAkNMMRYwFAYDVQQIDA1NZXRyb3BvbGl0YW5hMREwDwYDVQQHDAhTYW50aWFnbzESMBAGA1UECgwJQ01QQyBTLkEuMRQwEgYDVQQLDAtHZXJlbmNpYSBUSTEiMCAGCSqGSIb3DQEJARYTY2FybG9zLnNhZXpAY21wYy5jbDAeFw0yMDAyMjUwMjU1MTFaFw0yMTAyMjQwMjU1MTFaMIGGMQswCQYDVQQGEwJDTDEWMBQGA1UECAwNTWV0cm9wb2xpdGFuYTERMA8GA1UEBwwIU2FudGlhZ28xEjAQBgNVBAoMCUNNUEMgUy5BLjEUMBIGA1UECwwLR2VyZW5jaWEgVEkxIjAgBgkqhkiG9w0BCQEWE2Nhcmxvcy5zYWV6QGNtcGMuY2wwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv5KqzSNTBRk0VRzdqPcc4mKJ6RPYO2IPH1T8wSkfuqYFJLHONk+qkbQuROyj9c6vXx1L4KNSc/hov8bdg5WCK0uiuc7GDPuoRRrQYPBZGzM9NJMnQYHXRvXNNyriodhdvVRLyBGkxodtWvhaOUZxaC5C+P3fqhEeYAsggKblyajYwLzFR890rYk029jTnEXFCFDfoFv+7dWp8zbgp8txXkFsA79I+bh/rrLl6n2+rVaKE+Gy9sWpI516j4bO8yuWMYaQfJGtrm/bo2ahJ+2+H06j1ICcFZdbgF04WPpuM6BYHKV1D2BlVKB69mU1jvgc/lthMa0vTx71MzweP3cNHAgMBAAGjUzBRMB0GA1UdDgQWBBRFtzc0KgnQMl1D/ho2ZI+GIeDK6DAfBgNVHSMEGDAWgBRFtzc0KgnQMl1D/ho2ZI+GIeDK6DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCIPjjJzjnc4koyUH0IK8P/c5cs82QyU2vpOIrQPxHyypqgbibNu20GFTQrIMA1mi+CQKa5K0AJNF7B88CkuBv1FmeJoxh4ppc+PeYJ3gt3bG6XZYBWoyluZ2afy4EcWxoqWEzdLmYF0hvWSZfwp+JgBriH5QLfzQCBFAymHcBaPGGZMTbF900TcOL8Rb0r3yfMy7ib1k7kLcSP7PB1mYE3ihqisPkS3vc9VxnzICAkxcm3H4Rap9GEu1fupi4dPxQEQSh7gq/bdDydDZpb1t6ijarr3uDA94HAPsCqNKzi6+ENxxaOi/I9xJOjLOq5acYIIVaiOZcQgNJ1rJrX76wv</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
Version="2.0"
ID="_cd6fc633-b1bc-43d2-a2e4-2a46fbe1b4c2"
IssueInstant="2020-03-02T15:18:50.86Z"
>
<saml:Issuer>ENTITY_ID_OMITTED</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">NAME@DOMAIN.COM</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData Recipient="http://localhost:64484/Account/ExternalLoginCallback" />
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2020-03-02T15:18:50.86Z"
NotOnOrAfter="2020-03-02T15:48:50.86Z"
>
<saml:AudienceRestriction>
<saml:Audience>https://www.google.cl</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2020-03-02T15:18:50.86Z">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="Names">
<saml:AttributeValue>NAME</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="Lastname">
<saml:AttributeValue>LASTNAME</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="SecondLastname">
<saml:AttributeValue>SECONDLASTNAME</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>