Регистрация WS-Federation Asp.NET 5 MVC 6 ADFS
Здравствуйте, поэтому я пытался добиться WS-Fed SSO в моем веб-приложении MVC6, я немного прочитал об аутентификации и все, чтобы определить мои требования. Я должен использовать WsFederationAuth, поэтому для меня не будет работать протокол oauth или saml.
Изменить: После предложения @Pinpoint я попытался использовать промежуточное ПО owin для достижения соединения, но я буду использовать полную инфраструктуру DNX451, а не DNXCore, но это что-то, ожидая поддержки ws-feed от Vnext.
public static class AppBuilderExtensions
{
#if !DNXCORE50
public static IApplicationBuilder UseOwinAppBuilder(this IApplicationBuilder app,
Action<IAppBuilder> configuration)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
return app.UseOwin(setup => setup(next =>
{
var builder = new AppBuilder();
var lifetime = (IApplicationLifetime) app.ApplicationServices.GetService(typeof (IApplicationLifetime));
var properties = new AppProperties(builder.Properties);
properties.AppName = app.ApplicationServices.GetApplicationUniqueIdentifier();
properties.OnAppDisposing = lifetime.ApplicationStopping;
properties.DefaultApp = next;
configuration(builder);
return builder.Build<Func<IDictionary<string, object>, Task>>();
}));
}
#endif
}
И в startup.cs:
#if !DNXCORE50
app.UseOwinAppBuilder(owin =>
{
owin.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
MetadataAddress =
"https://mysite.accesscontrol.windows.net/FederationMetadata/2007-06/FederationMetadata.xml",
Wtrealm = "http://localhost:62569/",
SignInAsAuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
AuthenticationType = "adfs",
SecurityTokenHandlers = new SecurityTokenHandlerCollection
{
new EncryptedSecurityTokenHandler
{
Configuration = new SecurityTokenHandlerConfiguration
{
IssuerTokenResolver = new X509CertificateStoreTokenResolver(StoreName.My,
StoreLocation.LocalMachine)
}
},
new Saml2SecurityTokenHandler
{
CertificateValidator = X509CertificateValidator.None,
}
}
});
});
#endif
Я чувствую, что приближаюсь к решению, но это еще не сделано. У меня проблемы с обработкой маркера (после аутентификации против adfs)
Я получаю эту ошибку с afayu токеном:
SecurityTokenValidationException: IDX10201: ни один из SecurityTokenHandlers не смог прочитать "securityToken":
<Assertion ID="_851fc402-2e9c-4ff8-a743-7d65612255b9" IssueInstant="2015-06-22T16:16:03.852Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
<Issuer>https://mysite.accesscontrol.windows.net/</Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<ds:Reference URI="#_851fc402-2e9c-4ff8-a743-7d65612255b9">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<ds:DigestValue>xZdzOnNIG5Ia***********t0feMWIZMLnY=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>KmuScnZBdxyaAJrfLgB9AYheUdR*****************************Xs4o8R+eMCPdWNsDjhLu500UlCgitYerjpLTTyRRdwvFo8T7LlsXO2yjv3dx83Yn+GthE+FswNRH07yIHF5wo5+/TAwiVzg+9SDbK+Nj84PdLMxwIfALAebf4/ECdpqkWvt2ligzFoQckEgZMRepOcAVfBxfELyJSUDAjnpfJCrlCQip0nykC+5R37X00flIlB563p48veeLIt0JaUdG4bwtQ8OCMg1KeD5gYix9p4E3TQ7QovN0HDoWTurLK/0H01IS73fIe6/k6DBA==</ds:SignatureValue>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIIDSjCCAjagAwIBAgIQrcBhMtovuJ**********************MDExLzAtBgNVBAMTJm1hcmdvY29uc2VpbC5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE1MDYxNjA4MTYzOFoXDTIwMDUzMTIyMDAwMFowMTEvMC0GA1UEAxMmbWFyZ29jb25zZWlsLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpeZseXX1IYTABUOPr7nfIAXc7cXAI0k+vR3qEbvy0UxEhYAkAocQR2qUTPQ8D1v4lPp59tnHKBGJ0eHt9DYm/SyKkfHsWfqsysZx5NHXSJIhy/SgHwpd8b2q1NKxqBRLrdJKyAua+WWza4p/HMFjEVoN/upZtngSqxUKO/oYqy6m7smkz8vwjzpJR8tyqN881XBQzvryiF/i3ZPFQqlCT9263TMcAGPpym9uvxHzFPPx3u8IDz3AQydyHeViaJhiXGic0VEcm6LMn3JLOYqAzJnv8z89NdpsL4ynv1ekytt/8yyza3CnsU1E4tFDj1HU3785aFZ1xm6wr1MUK9VOTAgMBAAGjZjBkMGIGA1UdAQRbMFmAEN1alzwM3lJSHdh4LFl7uxmhMzAxMS8wLQYDVQQDEyZtYXJnb2NvbnNlaWwuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQrcBhMtovuJ9MilbEjMjS7TAJBgUrDgMCHQUAA4IBAQAsQ5jNKvS2fLtqs9oB6DGTXdI5nAli5UyvZUQlnfnMvgYjJKrZu79iMe9iu88cmtwZVifG14SRbVdTjUOzngIWAJ5KAQk5t//wSkwgAS+U6AFYI/mee9NLEvOEhrRbpGUP0oL504OZ9zTDeXmGu2FybRB2TvdTKLaeVsBvwqgP33QFkcuPK50fCGC1l3SecIeyWL5fsiw/2+GuTKHjCaeRqnYBgDTINptc9PGayLPBTjs4UPzbccmaYyuanmTAMZGU0iRoGJYet2uAasT52QvWZqD0NUZbWyR1N8CBf5EIW2S/TrpoOBYNgZQU5n9PRJjTBhESHXjfa8RipC8RXU9o</X509Certificate>
</X509Data>
</KeyInfo>
</ds:Signature>
<Subject>
<NameID>***********</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" />
</Subject>
<Conditions NotBefore="2015-06-22T16:16:03.836Z" NotOnOrAfter="2015-06-22T17:16:03.836Z">
<AudienceRestriction>
<Audience>http://localhost:62569/</Audience>
</AudienceRestriction>
</Conditions>
<AttributeStatement>
<Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid">
<AttributeValue>********************</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
<AttributeValue>************</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname">
<AttributeValue>G****l</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
<AttributeValue>L****s</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider">
<AttributeValue>https://sts.windows.net/7102feaa-34af-4756-85ce-b0f69766d78d/</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider">
<AttributeValue>https://sts.windows.net/7102feaa-34af-4756-85ce-b0f69766d78d/</AttributeValue>
</Attribute>
</AttributeStatement>
<AuthnStatement AuthnInstant="2015-06-22T14:26:14.020Z">
<AuthnContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
</Assertion>
Ответы
Ответ 1
Как вы уже выяснили, промежуточное ПО WS-Federation еще не было перенесено на ASP.NET 5, но не паникуйте, это определенно будет: https://twitter.com/blowdart/status/610526268908535808
В то же время вы можете использовать промежуточное ПО OWIN/Katana 3 WS-Federation в приложении ASP.NET 5 с крошечным адаптером IAppBuilder
/IApplicationBuilder
(например, этот: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/vNext/samples/Mvc/Mvc.Server/Extensions/AppBuilderExtensions.cs#L50), но, конечно, он не будет совместим с dnxcore50
.
Если у вас есть недавняя версия ADFS, вы также можете рассмотреть возможность переключения на OAuth2.
Ответ 2
Я узнал, как получить претензии от HTTP-запроса, возвращаемого из ADFS, используя предыдущие классы ASP.NET таким образом, чтобы он не касался web.config.
Надеюсь, этот код полезен кому-то:
using Microsoft.AspNet.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Specialized;
using System.IdentityModel.Configuration;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Selectors;
using System.IdentityModel.Services;
using System.IdentityModel.Tokens;
using System.IO;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel.Security;
using System.Text;
using System.Xml;
public class FederationHelper
{
public ClaimsIdentity GetClaimsIdentityFromResponse(IConfigurationRoot configurationRoot, HttpContext context)
{
var absoluteUri = string.Concat(
context.Request.Scheme,
"://",
context.Request.Host.ToUriComponent(),
context.Request.PathBase.ToUriComponent(),
context.Request.Path.ToUriComponent(),
context.Request.QueryString.ToUriComponent());
var values = new NameValueCollection();
foreach (var item in context.Request.Query)
{
values.Add(item.Key, item.Value);
}
foreach (var item in context.Request.Form)
{
values.Add(item.Key, item.Value);
}
var federation = new FederationSettings
{
AudienceUri = "http://contoso",
Endpoint = "http://sts/ls",
IssuerName = "http://sts/trust",
IssuerThumbprint = "[thumbprint]",
Realm = "https://myapp"
};
var identityConfiguration = new IdentityConfiguration(false);
identityConfiguration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(federation.AudienceUri));
var issuers = new ConfigurationBasedIssuerNameRegistry();
issuers.ConfiguredTrustedIssuers.Add(federation.IssuerThumbprint, federation.IssuerName);
identityConfiguration.IssuerNameRegistry = issuers;
var tokenHandler = new SamlSecurityTokenHandler();
tokenHandler.Configuration = new SecurityTokenHandlerConfiguration();
tokenHandler.Configuration.AudienceRestriction.AudienceMode = AudienceUriMode.Always;
tokenHandler.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(federation.AudienceUri));
tokenHandler.Configuration.IssuerNameRegistry = identityConfiguration.IssuerNameRegistry;
tokenHandler.Configuration.CertificateValidationMode = X509CertificateValidationMode.None;
tokenHandler.Configuration.RevocationMode = X509RevocationMode.NoCheck;
tokenHandler.Configuration.CertificateValidator = X509CertificateValidator.None;
var message = (SignInResponseMessage)WSFederationMessage.CreateFromNameValueCollection(new Uri(absoluteUri), values);
var token = CreateSecurityToken(tokenHandler, identityConfiguration, message);
var claims = tokenHandler.ValidateToken(token);
return claims[0];
}
public SecurityToken CreateSecurityToken(SamlSecurityTokenHandler handler, IdentityConfiguration configuration, SignInResponseMessage message)
{
var quotas = new XmlDictionaryReaderQuotas();
using (var reader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(message.Result), quotas))
{
var serializer = new WSFederationSerializer(reader);
var context = new WSTrustSerializationContext(configuration.SecurityTokenHandlerCollectionManager);
var xml = serializer.CreateResponse(message, context).RequestedSecurityToken.SecurityTokenXml.OuterXml;
return ReadToken(handler, xml, quotas);
}
}
SecurityToken ReadToken(SamlSecurityTokenHandler handler, string xml, XmlDictionaryReaderQuotas quotas)
{
using (var reader = new StringReader(xml))
{
using (var xmlReader = XmlReader.Create(reader))
{
xmlReader.MoveToContent();
return handler.ReadToken(xmlReader);
}
}
}
}