writeContent)
+ {
+ var bootstrap = "";
+
+ response.ContentType = "text/html";
+ await response.WriteAsync($"{bootstrap}");
+ await writeContent(response);
+ await response.WriteAsync("
");
+ }
+
+ private static async Task WriteTableHeader(HttpResponse response, IEnumerable columns, IEnumerable> data)
+ {
+ await response.WriteAsync("");
+ await response.WriteAsync("");
+ foreach (var column in columns)
+ {
+ await response.WriteAsync($"{HtmlEncode(column)} | ");
+ }
+ await response.WriteAsync("
");
+ foreach (var row in data)
+ {
+ await response.WriteAsync("");
+ foreach (var column in row)
+ {
+ await response.WriteAsync($"{HtmlEncode(column)} | ");
+ }
+ await response.WriteAsync("
");
+ }
+ await response.WriteAsync("
");
+ }
+
+ private static string HtmlEncode(string content) =>
+ string.IsNullOrEmpty(content) ? string.Empty : HtmlEncoder.Default.Encode(content);
+ }
+}
diff --git a/samples/WsFedSample/WsFedSample.csproj b/samples/WsFedSample/WsFedSample.csproj
new file mode 100644
index 000000000..bc3a59f10
--- /dev/null
+++ b/samples/WsFedSample/WsFedSample.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net461;netcoreapp2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WsFedSample/compiler/resources/cert.pfx b/samples/WsFedSample/compiler/resources/cert.pfx
new file mode 100644
index 000000000..7118908c2
Binary files /dev/null and b/samples/WsFedSample/compiler/resources/cert.pfx differ
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/AuthenticationFailedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/AuthenticationFailedContext.cs
new file mode 100644
index 000000000..f643fad97
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/AuthenticationFailedContext.cs
@@ -0,0 +1,35 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// The context object used in for .
+ ///
+ public class AuthenticationFailedContext : RemoteAuthenticationContext
+ {
+ ///
+ /// Creates a new context object
+ ///
+ ///
+ ///
+ ///
+ public AuthenticationFailedContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options)
+ : base(context, scheme, options, new AuthenticationProperties())
+ { }
+
+ ///
+ /// The from the request, if any.
+ ///
+ public WsFederationMessage ProtocolMessage { get; set; }
+
+ ///
+ /// The that triggered this event.
+ ///
+ public Exception Exception { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/MessageReceivedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/MessageReceivedContext.cs
new file mode 100644
index 000000000..4028fa5e3
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/MessageReceivedContext.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// The context object used for .
+ ///
+ public class MessageReceivedContext : RemoteAuthenticationContext
+ {
+ ///
+ /// Creates a new context object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public MessageReceivedContext(
+ HttpContext context,
+ AuthenticationScheme scheme,
+ WsFederationOptions options,
+ AuthenticationProperties properties)
+ : base(context, scheme, options, properties) { }
+
+ ///
+ /// The received on this request.
+ ///
+ public WsFederationMessage ProtocolMessage { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RedirectContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RedirectContext.cs
new file mode 100644
index 000000000..654037d0a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RedirectContext.cs
@@ -0,0 +1,44 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// When a user configures the to be notified prior to redirecting to an IdentityProvider
+ /// an instance of is passed to the 'RedirectToAuthenticationEndpoint' or 'RedirectToEndSessionEndpoint' events.
+ ///
+ public class RedirectContext : PropertiesContext
+ {
+ ///
+ /// Creates a new context object.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public RedirectContext(
+ HttpContext context,
+ AuthenticationScheme scheme,
+ WsFederationOptions options,
+ AuthenticationProperties properties)
+ : base(context, scheme, options, properties) { }
+
+ ///
+ /// The used to compose the redirect.
+ ///
+ public WsFederationMessage ProtocolMessage { get; set; }
+
+ ///
+ /// If true, will skip any default logic for this redirect.
+ ///
+ public bool Handled { get; private set; }
+
+ ///
+ /// Skips any default logic for this redirect.
+ ///
+ public void HandleResponse() => Handled = true;
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RemoteSignoutContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RemoteSignoutContext.cs
new file mode 100644
index 000000000..8aec24a64
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RemoteSignoutContext.cs
@@ -0,0 +1,30 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// An event context for RemoteSignOut.
+ ///
+ public class RemoteSignOutContext : RemoteAuthenticationContext
+ {
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public RemoteSignOutContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options, WsFederationMessage message)
+ : base(context, scheme, options, new AuthenticationProperties())
+ => ProtocolMessage = message;
+
+ ///
+ /// The signout message.
+ ///
+ public WsFederationMessage ProtocolMessage { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenReceivedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenReceivedContext.cs
new file mode 100644
index 000000000..311f41515
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenReceivedContext.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Security.Claims;
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// This Context can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint.
+ ///
+ public class SecurityTokenReceivedContext : RemoteAuthenticationContext
+ {
+ ///
+ /// Creates a
+ ///
+ public SecurityTokenReceivedContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options, AuthenticationProperties properties)
+ : base(context, scheme, options, properties)
+ {
+ }
+
+ ///
+ /// The received on this request.
+ ///
+ public WsFederationMessage ProtocolMessage { get; set; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenValidatedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenValidatedContext.cs
new file mode 100644
index 000000000..1f32014b6
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenValidatedContext.cs
@@ -0,0 +1,34 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Microsoft.AspNetCore.Http;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+using Microsoft.IdentityModel.Tokens;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// The context object used for .
+ ///
+ public class SecurityTokenValidatedContext : RemoteAuthenticationContext
+ {
+ ///
+ /// Creates a
+ ///
+ public SecurityTokenValidatedContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options, ClaimsPrincipal principal, AuthenticationProperties properties)
+ : base(context, scheme, options, properties)
+ => Principal = principal;
+
+ ///
+ /// The received on this request.
+ ///
+ public WsFederationMessage ProtocolMessage { get; set; }
+
+ ///
+ /// The that was validated.
+ ///
+ public SecurityToken SecurityToken { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/WsFederationEvents.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/WsFederationEvents.cs
new file mode 100644
index 000000000..55c3936f9
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/WsFederationEvents.cs
@@ -0,0 +1,74 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// Specifies events which the invokes to enable developer control over the authentication process. />
+ ///
+ public class WsFederationEvents : RemoteAuthenticationEvents
+ {
+ ///
+ /// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed.
+ ///
+ public Func OnAuthenticationFailed { get; set; } = context => Task.CompletedTask;
+
+ ///
+ /// Invoked when a protocol message is first received.
+ ///
+ public Func OnMessageReceived { get; set; } = context => Task.CompletedTask;
+
+ ///
+ /// Invoked to manipulate redirects to the identity provider for SignIn, SignOut, or Challenge.
+ ///
+ public Func OnRedirectToIdentityProvider { get; set; } = context => Task.CompletedTask;
+
+ ///
+ /// Invoked when a wsignoutcleanup request is received at the RemoteSignOutPath endpoint.
+ ///
+ public Func OnRemoteSignOut { get; set; } = context => Task.CompletedTask;
+
+ ///
+ /// Invoked with the security token that has been extracted from the protocol message.
+ ///
+ public Func OnSecurityTokenReceived { get; set; } = context => Task.CompletedTask;
+
+ ///
+ /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
+ ///
+ public Func OnSecurityTokenValidated { get; set; } = context => Task.CompletedTask;
+
+ ///
+ /// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed.
+ ///
+ public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
+
+ ///
+ /// Invoked when a protocol message is first received.
+ ///
+ public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
+
+ ///
+ /// Invoked to manipulate redirects to the identity provider for SignIn, SignOut, or Challenge.
+ ///
+ public virtual Task RedirectToIdentityProvider(RedirectContext context) => OnRedirectToIdentityProvider(context);
+
+ ///
+ /// Invoked when a wsignoutcleanup request is received at the RemoteSignOutPath endpoint.
+ ///
+ public virtual Task RemoteSignOut(RemoteSignOutContext context) => OnRemoteSignOut(context);
+
+ ///
+ /// Invoked with the security token that has been extracted from the protocol message.
+ ///
+ public virtual Task SecurityTokenReceived(SecurityTokenReceivedContext context) => OnSecurityTokenReceived(context);
+
+ ///
+ /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
+ ///
+ public virtual Task SecurityTokenValidated(SecurityTokenValidatedContext context) => OnSecurityTokenValidated(context);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/LoggingExtensions.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/LoggingExtensions.cs
new file mode 100644
index 000000000..e28b7e15b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/LoggingExtensions.cs
@@ -0,0 +1,85 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.Extensions.Logging
+{
+ internal static class LoggingExtensions
+ {
+ private static Action _signInWithoutWresult;
+ private static Action _signInWithoutToken;
+ private static Action _exceptionProcessingMessage;
+ private static Action _malformedRedirectUri;
+ private static Action _remoteSignOutHandledResponse;
+ private static Action _remoteSignOutSkipped;
+ private static Action _remoteSignOut;
+
+ static LoggingExtensions()
+ {
+ _signInWithoutWresult = LoggerMessage.Define(
+ eventId: 1,
+ logLevel: LogLevel.Debug,
+ formatString: "Received a sign-in message without a WResult.");
+ _signInWithoutToken = LoggerMessage.Define(
+ eventId: 2,
+ logLevel: LogLevel.Debug,
+ formatString: "Received a sign-in message without a token.");
+ _exceptionProcessingMessage = LoggerMessage.Define(
+ eventId: 3,
+ logLevel: LogLevel.Error,
+ formatString: "Exception occurred while processing message.");
+ _malformedRedirectUri = LoggerMessage.Define(
+ eventId: 4,
+ logLevel: LogLevel.Warning,
+ formatString: "The sign-out redirect URI '{0}' is malformed.");
+ _remoteSignOutHandledResponse = LoggerMessage.Define(
+ eventId: 5,
+ logLevel: LogLevel.Debug,
+ formatString: "RemoteSignOutContext.HandledResponse");
+ _remoteSignOutSkipped = LoggerMessage.Define(
+ eventId: 6,
+ logLevel: LogLevel.Debug,
+ formatString: "RemoteSignOutContext.Skipped");
+ _remoteSignOut = LoggerMessage.Define(
+ eventId: 7,
+ logLevel: LogLevel.Information,
+ formatString: "Remote signout request processed.");
+ }
+
+ public static void SignInWithoutWresult(this ILogger logger)
+ {
+ _signInWithoutWresult(logger, null);
+ }
+
+ public static void SignInWithoutToken(this ILogger logger)
+ {
+ _signInWithoutToken(logger, null);
+ }
+
+ public static void ExceptionProcessingMessage(this ILogger logger, Exception ex)
+ {
+ _exceptionProcessingMessage(logger, ex);
+ }
+
+ public static void MalformedRedirectUri(this ILogger logger, string uri)
+ {
+ _malformedRedirectUri(logger, uri, null);
+ }
+
+ public static void RemoteSignOutHandledResponse(this ILogger logger)
+ {
+ _remoteSignOutHandledResponse(logger, null);
+ }
+
+ public static void RemoteSignOutSkipped(this ILogger logger)
+ {
+ _remoteSignOutSkipped(logger, null);
+ }
+
+ public static void RemoteSignOut(this ILogger logger)
+ {
+ _remoteSignOut(logger, null);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Microsoft.AspNetCore.Authentication.WsFederation.csproj b/src/Microsoft.AspNetCore.Authentication.WsFederation/Microsoft.AspNetCore.Authentication.WsFederation.csproj
new file mode 100644
index 000000000..4edb55cb3
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Microsoft.AspNetCore.Authentication.WsFederation.csproj
@@ -0,0 +1,17 @@
+
+
+
+ ASP.NET Core middleware that enables an application to support the WsFederation authentication workflow.
+ netstandard2.0
+ true
+ aspnetcore;authentication;security
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..564e826a7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Properties/Resources.Designer.cs
@@ -0,0 +1,114 @@
+//
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ using System.Globalization;
+ using System.Reflection;
+ using System.Resources;
+
+ internal static class Resources
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.AspNetCore.Authentication.WsFederation.Resources", typeof(Resources).GetTypeInfo().Assembly);
+
+ ///
+ /// The service descriptor is missing.
+ ///
+ internal static string Exception_MissingDescriptor
+ {
+ get => GetString("Exception_MissingDescriptor");
+ }
+
+ ///
+ /// The service descriptor is missing.
+ ///
+ internal static string FormatException_MissingDescriptor()
+ => GetString("Exception_MissingDescriptor");
+
+ ///
+ /// No token validator was found for the given token.
+ ///
+ internal static string Exception_NoTokenValidatorFound
+ {
+ get => GetString("Exception_NoTokenValidatorFound");
+ }
+
+ ///
+ /// No token validator was found for the given token.
+ ///
+ internal static string FormatException_NoTokenValidatorFound()
+ => GetString("Exception_NoTokenValidatorFound");
+
+ ///
+ /// The '{0}' option must be provided.
+ ///
+ internal static string Exception_OptionMustBeProvided
+ {
+ get => GetString("Exception_OptionMustBeProvided");
+ }
+
+ ///
+ /// The '{0}' option must be provided.
+ ///
+ internal static string FormatException_OptionMustBeProvided(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0);
+
+ ///
+ /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.
+ ///
+ internal static string Exception_ValidatorHandlerMismatch
+ {
+ get => GetString("Exception_ValidatorHandlerMismatch");
+ }
+
+ ///
+ /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.
+ ///
+ internal static string FormatException_ValidatorHandlerMismatch()
+ => GetString("Exception_ValidatorHandlerMismatch");
+
+ ///
+ /// The sign in message does not contain a required token.
+ ///
+ internal static string SignInMessageTokenIsMissing
+ {
+ get => GetString("SignInMessageTokenIsMissing");
+ }
+
+ ///
+ /// The sign in message does not contain a required token.
+ ///
+ internal static string FormatSignInMessageTokenIsMissing()
+ => GetString("SignInMessageTokenIsMissing");
+
+ ///
+ /// The sign in message does not contain a required wresult.
+ ///
+ internal static string SignInMessageWresultIsMissing
+ {
+ get => GetString("SignInMessageWresultIsMissing");
+ }
+
+ ///
+ /// The sign in message does not contain a required wresult.
+ ///
+ internal static string FormatSignInMessageWresultIsMissing()
+ => GetString("SignInMessageWresultIsMissing");
+
+ private static string GetString(string name, params string[] formatterNames)
+ {
+ var value = _resourceManager.GetString(name);
+
+ System.Diagnostics.Debug.Assert(value != null);
+
+ if (formatterNames != null)
+ {
+ for (var i = 0; i < formatterNames.Length; i++)
+ {
+ value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
+ }
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Resources.resx b/src/Microsoft.AspNetCore.Authentication.WsFederation/Resources.resx
new file mode 100644
index 000000000..e2edafb67
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Resources.resx
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ The service descriptor is missing.
+
+
+ No token validator was found for the given token.
+
+
+ The '{0}' option must be provided.
+
+
+ An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler.
+
+
+ The sign in message does not contain a required token.
+
+
+ The sign in message does not contain a required wresult.
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationDefaults.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationDefaults.cs
new file mode 100644
index 000000000..3b97d995b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationDefaults.cs
@@ -0,0 +1,26 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// Default values related to WsFederation authentication handler
+ ///
+ public static class WsFederationDefaults
+ {
+ ///
+ /// The default authentication type used when registering the WsFederationHandler.
+ ///
+ public const string AuthenticationScheme = "WsFederation";
+
+ ///
+ /// The default display name used when registering the WsFederationHandler.
+ ///
+ public const string DisplayName = "WsFederation";
+
+ ///
+ /// Constant used to identify userstate inside AuthenticationProperties that have been serialized in the 'wctx' parameter.
+ ///
+ public static readonly string UserstatePropertiesKey = "WsFederation.Userstate";
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationExtensions.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationExtensions.cs
new file mode 100644
index 000000000..47091d58d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationExtensions.cs
@@ -0,0 +1,58 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.WsFederation;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ ///
+ /// Extensions for registering the .
+ ///
+ public static class WsFederationExtensions
+ {
+ ///
+ /// Registers the using the default authentication scheme, display name, and options.
+ ///
+ ///
+ ///
+ public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder)
+ => builder.AddWsFederation(WsFederationDefaults.AuthenticationScheme, _ => { });
+
+ ///
+ /// Registers the using the default authentication scheme, display name, and the given options configuration.
+ ///
+ ///
+ /// A delegate that configures the .
+ ///
+ public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder, Action configureOptions)
+ => builder.AddWsFederation(WsFederationDefaults.AuthenticationScheme, configureOptions);
+
+ ///
+ /// Registers the using the given authentication scheme, default display name, and the given options configuration.
+ ///
+ ///
+ ///
+ /// A delegate that configures the .
+ ///
+ public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions)
+ => builder.AddWsFederation(authenticationScheme, WsFederationDefaults.DisplayName, configureOptions);
+
+ ///
+ /// Registers the using the given authentication scheme, display name, and options configuration.
+ ///
+ ///
+ ///
+ ///
+ /// A delegate that configures the .
+ ///
+ public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions)
+ {
+ builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, WsFederationPostConfigureOptions>());
+ return builder.AddRemoteScheme(authenticationScheme, displayName, configureOptions);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationHandler.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationHandler.cs
new file mode 100644
index 000000000..e47f8431f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationHandler.cs
@@ -0,0 +1,425 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.Protocols.WsFederation;
+using Microsoft.IdentityModel.Tokens;
+
+namespace Microsoft.AspNetCore.Authentication.WsFederation
+{
+ ///
+ /// A per-request authentication handler for the WsFederation.
+ ///
+ public class WsFederationHandler : RemoteAuthenticationHandler, IAuthenticationSignOutHandler
+ {
+ private const string CorrelationProperty = ".xsrf";
+ private WsFederationConfiguration _configuration;
+
+ ///
+ /// Creates a new WsFederationAuthenticationHandler
+ ///
+ ///
+ ///
+ ///
+ ///
+ public WsFederationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
+ : base(options, logger, encoder, clock)
+ {
+ }
+
+ ///
+ /// The handler calls methods on the events which give the application control at certain points where processing is occurring.
+ /// If it is not provided a default instance is supplied which does nothing when the methods are called.
+ ///
+ protected new WsFederationEvents Events
+ {
+ get { return (WsFederationEvents)base.Events; }
+ set { base.Events = value; }
+ }
+
+ ///
+ /// Creates a new instance of the events instance.
+ ///
+ /// A new instance of the events instance.
+ protected override Task