diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs
index 00652c32f5..26c9129e07 100644
--- a/samples/OpenIdConnectSample/Startup.cs
+++ b/samples/OpenIdConnectSample/Startup.cs
@@ -31,11 +31,11 @@ namespace OpenIdConnectSample
});
app.UseOpenIdConnectAuthentication(options =>
- {
- options.ClientId = "fe78e0b4-6fe7-47e6-812c-fb75cee266a4";
- options.Authority = "https://login.windows.net/cyrano.onmicrosoft.com";
- options.RedirectUri = "http://localhost:42023";
- });
+ {
+ options.ClientId = "fe78e0b4-6fe7-47e6-812c-fb75cee266a4";
+ options.Authority = "https://login.windows.net/cyrano.onmicrosoft.com";
+ options.RedirectUri = "http://localhost:42023";
+ });
app.Run(async context =>
{
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
index 6b457df156..6a209dd45d 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs
@@ -11,6 +11,7 @@ using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
@@ -19,6 +20,7 @@ using Microsoft.Framework.Caching.Distributed;
using Microsoft.Framework.Internal;
using Microsoft.Framework.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
+using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Authentication.OpenIdConnect
@@ -30,6 +32,22 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
{
private const string NonceProperty = "N";
private const string UriSchemeDelimiter = "://";
+
+ private const string InputTagFormat = @"";
+ private const string HtmlFormFormat = @"
+
+
+ Please wait while you're being redirected to the identity provider
+
+
+
+
+
+";
+
private OpenIdConnectConfiguration _configuration;
protected HttpClient Backchannel { get; private set; }
@@ -93,13 +111,43 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
message = redirectToIdentityProviderNotification.ProtocolMessage;
}
- var redirectUri = message.CreateLogoutRequestUrl();
- if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
+ if (Options.AuthenticationMethod == OpenIdConnectAuthenticationMethod.RedirectGet)
{
- Logger.LogWarning(Resources.OIDCH_0051_RedirectUriLogoutIsNotWellFormed, redirectUri);
- }
+ var redirectUri = message.CreateLogoutRequestUrl();
+ if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
+ {
+ Logger.LogWarning(Resources.OIDCH_0051_RedirectUriLogoutIsNotWellFormed, redirectUri);
+ }
- Response.Redirect(redirectUri);
+ Response.Redirect(redirectUri);
+ }
+ else if (Options.AuthenticationMethod == OpenIdConnectAuthenticationMethod.FormPost)
+ {
+ var inputs = new StringBuilder();
+ foreach (var parameter in message.Parameters)
+ {
+ var name = Options.HtmlEncoder.HtmlEncode(parameter.Key);
+ var value = Options.HtmlEncoder.HtmlEncode(parameter.Value);
+
+ var input = string.Format(CultureInfo.InvariantCulture, InputTagFormat, name, value);
+ inputs.AppendLine(input);
+ }
+
+ var issuer = Options.HtmlEncoder.HtmlEncode(message.IssuerAddress);
+
+ var content = string.Format(CultureInfo.InvariantCulture, HtmlFormFormat, issuer, inputs);
+ var buffer = Encoding.UTF8.GetBytes(content);
+
+ Response.ContentLength = buffer.Length;
+ Response.ContentType = "text/html;charset=UTF-8";
+
+ // Emit Cache-Control=no-cache to prevent client caching.
+ Response.Headers.Set(HeaderNames.CacheControl, "no-cache");
+ Response.Headers.Set(HeaderNames.Pragma, "no-cache");
+ Response.Headers.Set(HeaderNames.Expires, "-1");
+
+ await Response.Body.WriteAsync(buffer, 0, buffer.Length);
+ }
}
}
@@ -218,14 +266,50 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
message.State = Options.StateDataFormat.Protect(properties);
- var redirectUri = message.CreateAuthenticationRequestUrl();
- if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
+ if (Options.AuthenticationMethod == OpenIdConnectAuthenticationMethod.RedirectGet)
{
- Logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
+ var redirectUri = message.CreateAuthenticationRequestUrl();
+ if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
+ {
+ Logger.LogWarning(Resources.OIDCH_0036_UriIsNotWellFormed, redirectUri);
+ }
+
+ Response.Redirect(redirectUri);
+
+ return true;
+ }
+ else if (Options.AuthenticationMethod == OpenIdConnectAuthenticationMethod.FormPost)
+ {
+ var inputs = new StringBuilder();
+ foreach (var parameter in message.Parameters)
+ {
+ var name = Options.HtmlEncoder.HtmlEncode(parameter.Key);
+ var value = Options.HtmlEncoder.HtmlEncode(parameter.Value);
+
+ var input = string.Format(CultureInfo.InvariantCulture, InputTagFormat, name, value);
+ inputs.AppendLine(input);
+ }
+
+ var issuer = Options.HtmlEncoder.HtmlEncode(message.IssuerAddress);
+
+ var content = string.Format(CultureInfo.InvariantCulture, HtmlFormFormat, issuer, inputs);
+ var buffer = Encoding.UTF8.GetBytes(content);
+
+ Response.ContentLength = buffer.Length;
+ Response.ContentType = "text/html;charset=UTF-8";
+
+ // Emit Cache-Control=no-cache to prevent client caching.
+ Response.Headers.Set(HeaderNames.CacheControl, "no-cache");
+ Response.Headers.Set(HeaderNames.Pragma, "no-cache");
+ Response.Headers.Set(HeaderNames.Expires, "-1");
+
+ await Response.Body.WriteAsync(buffer, 0, buffer.Length);
+
+ return true;
}
- Response.Redirect(redirectUri);
- return true;
+ Logger.LogError("An unsupported authentication method has been configured: {0}", Options.AuthenticationMethod);
+ return false;
}
///
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMethod.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMethod.cs
new file mode 100644
index 0000000000..147d576f9b
--- /dev/null
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMethod.cs
@@ -0,0 +1,21 @@
+namespace Microsoft.AspNet.Authentication.OpenIdConnect
+{
+ ///
+ /// Lists the different authentication methods used to
+ /// redirect the user agent to the identity provider.
+ ///
+ public enum OpenIdConnectAuthenticationMethod
+ {
+ ///
+ /// Emits a 302 response to redirect the user agent to
+ /// the OpenID Connect provider using a GET request.
+ ///
+ RedirectGet = 0,
+
+ ///
+ /// Emits an HTML form to redirect the user agent to
+ /// the OpenID Connect provider using a POST request.
+ ///
+ FormPost = 1
+ }
+}
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs
index e4bef30dbb..b26fd1f5e8 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs
@@ -54,6 +54,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
Options.SignInScheme = sharedOptions.Options.SignInScheme;
}
+ if (Options.HtmlEncoder == null)
+ {
+ Options.HtmlEncoder = services.GetHtmlEncoder();
+ }
+
if (Options.StateDataFormat == null)
{
var dataProtector = dataProtectionProvider.CreateProtector(
diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs
index 70809636cd..4bdfcca994 100644
--- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs
+++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs
@@ -11,6 +11,7 @@ using System.Security.Claims;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.Framework.Caching.Distributed;
+using Microsoft.Framework.WebEncoders;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
@@ -190,6 +191,11 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
///
public bool RefreshOnIssuerKeyNotFound { get; set; } = true;
+ ///
+ /// Gets or sets the method used to redirect the user agent to the identity provider.
+ ///
+ public OpenIdConnectAuthenticationMethod AuthenticationMethod { get; set; }
+
///
/// Gets or sets the 'resource'.
///
@@ -242,5 +248,10 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
/// This is enabled by default.
///
public bool UseTokenLifetime { get; set; } = true;
+
+ ///
+ /// Gets or sets the used to sanitize HTML outputs.
+ ///
+ public IHtmlEncoder HtmlEncoder { get; set; }
}
}
diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectMiddlewareTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectMiddlewareTests.cs
index 912a61047e..72c82999bf 100644
--- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectMiddlewareTests.cs
+++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectMiddlewareTests.cs
@@ -40,6 +40,22 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
const string Signin = "/signin";
const string Signout = "/signout";
+ [Fact]
+ public async Task ChallengeWillIssueHtmlFormWhenEnabled()
+ {
+ var server = CreateServer(options =>
+ {
+ options.Authority = DefaultAuthority;
+ options.ClientId = "Test Id";
+ options.Configuration = TestUtilities.DefaultOpenIdConnectConfiguration;
+ options.AuthenticationMethod = OpenIdConnectAuthenticationMethod.FormPost;
+ });
+ var transaction = await SendAsync(server, DefaultHost + Challenge);
+ transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ transaction.Response.Content.Headers.ContentType.MediaType.ShouldBe("text/html");
+ transaction.ResponseText.ShouldContain("form");
+ }
+
[Fact]
public async Task ChallengeWillSetDefaults()
{