diff --git a/src/Microsoft.AspNet.Authentication.Cookies/Events/CookieAuthenticationEvents.cs b/src/Microsoft.AspNet.Authentication.Cookies/Events/CookieAuthenticationEvents.cs
index 43ad5b0e70..615eb2b0c7 100644
--- a/src/Microsoft.AspNet.Authentication.Cookies/Events/CookieAuthenticationEvents.cs
+++ b/src/Microsoft.AspNet.Authentication.Cookies/Events/CookieAuthenticationEvents.cs
@@ -3,6 +3,7 @@
using System;
using System.Threading.Tasks;
+using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Authentication.Cookies
{
@@ -36,12 +37,75 @@ namespace Microsoft.AspNet.Authentication.Cookies
///
/// A delegate assigned to this property will be invoked when the related method is called.
///
- public Func OnRedirect { get; set; } = context =>
+ public Func OnRedirectToLogin { get; set; } = context =>
{
- context.Response.Redirect(context.RedirectUri);
+ if (IsAjaxRequest(context.Request))
+ {
+ context.Response.Headers["Location"] = context.RedirectUri;
+ context.Response.StatusCode = 401;
+ }
+ else
+ {
+ context.Response.Redirect(context.RedirectUri);
+ }
return Task.FromResult(0);
};
+ ///
+ /// A delegate assigned to this property will be invoked when the related method is called.
+ ///
+ public Func OnRedirectToAccessDenied { get; set; } = context =>
+ {
+ if (IsAjaxRequest(context.Request))
+ {
+ context.Response.Headers["Location"] = context.RedirectUri;
+ context.Response.StatusCode = 403;
+ }
+ else
+ {
+ context.Response.Redirect(context.RedirectUri);
+ }
+ return Task.FromResult(0);
+ };
+
+ ///
+ /// A delegate assigned to this property will be invoked when the related method is called.
+ ///
+ public Func OnRedirectToLogout { get; set; } = context =>
+ {
+ if (IsAjaxRequest(context.Request))
+ {
+ context.Response.Headers["Location"] = context.RedirectUri;
+ }
+ else
+ {
+ context.Response.Redirect(context.RedirectUri);
+ }
+ return Task.FromResult(0);
+ };
+
+ ///
+ /// A delegate assigned to this property will be invoked when the related method is called.
+ ///
+ public Func OnRedirectToReturnUrl { get; set; } = context =>
+ {
+ if (IsAjaxRequest(context.Request))
+ {
+ context.Response.Headers["Location"] = context.RedirectUri;
+ }
+ else
+ {
+ context.Response.Redirect(context.RedirectUri);
+ }
+ return Task.FromResult(0);
+ };
+
+ private static bool IsAjaxRequest(HttpRequest request)
+ {
+ return request.Query["X-Requested-With"] == "XMLHttpRequest" ||
+ request.Headers["X-Requested-With"] == "XMLHttpRequest";
+ }
+
///
/// Implements the interface method by invoking the related delegate method.
///
@@ -71,24 +135,24 @@ namespace Microsoft.AspNet.Authentication.Cookies
/// Implements the interface method by invoking the related delegate method.
///
/// Contains information about the event
- public virtual Task RedirectToLogout(CookieRedirectContext context) => OnRedirect(context);
+ public virtual Task RedirectToLogout(CookieRedirectContext context) => OnRedirectToLogout(context);
///
/// Implements the interface method by invoking the related delegate method.
///
/// Contains information about the event
- public virtual Task RedirectToLogin(CookieRedirectContext context) => OnRedirect(context);
+ public virtual Task RedirectToLogin(CookieRedirectContext context) => OnRedirectToLogin(context);
///
/// Implements the interface method by invoking the related delegate method.
///
/// Contains information about the event
- public virtual Task RedirectToReturnUrl(CookieRedirectContext context) => OnRedirect(context);
+ public virtual Task RedirectToReturnUrl(CookieRedirectContext context) => OnRedirectToReturnUrl(context);
///
/// Implements the interface method by invoking the related delegate method.
///
/// Contains information about the event
- public virtual Task RedirectToAccessDenied(CookieRedirectContext context) => OnRedirect(context);
+ public virtual Task RedirectToAccessDenied(CookieRedirectContext context) => OnRedirectToAccessDenied(context);
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs b/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs
index 3f40b1cebb..646375b880 100644
--- a/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs
+++ b/test/Microsoft.AspNet.Authentication.Test/Cookies/CookieMiddlewareTests.cs
@@ -33,6 +33,67 @@ namespace Microsoft.AspNet.Authentication.Cookies
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
+ [Fact]
+ public async Task AjaxLoginRedirectToReturnUrlTurnsInto200WithLocationHeader()
+ {
+ var server = CreateServer(options =>
+ {
+ options.AutomaticChallenge = true;
+ options.LoginPath = "/login";
+ });
+
+ var transaction = await SendAsync(server, "http://example.com/protected?X-Requested-With=XMLHttpRequest");
+ Assert.Equal(HttpStatusCode.Unauthorized, transaction.Response.StatusCode);
+ var responded = transaction.Response.Headers.GetValues("Location");
+ Assert.Equal(1, responded.Count());
+ Assert.True(responded.Single().StartsWith("http://example.com/login"));
+ }
+
+ [Fact]
+ public async Task AjaxForbidTurnsInto403WithLocationHeader()
+ {
+ var server = CreateServer(options =>
+ {
+ options.AccessDeniedPath = "/denied";
+ });
+
+ var transaction = await SendAsync(server, "http://example.com/forbid?X-Requested-With=XMLHttpRequest");
+ Assert.Equal(HttpStatusCode.Forbidden, transaction.Response.StatusCode);
+ var responded = transaction.Response.Headers.GetValues("Location");
+ Assert.Equal(1, responded.Count());
+ Assert.True(responded.Single().StartsWith("http://example.com/denied"));
+ }
+
+ [Fact]
+ public async Task AjaxLogoutRedirectToReturnUrlTurnsInto200WithLocationHeader()
+ {
+ var server = CreateServer(options =>
+ {
+ options.LogoutPath = "/signout";
+ });
+
+ var transaction = await SendAsync(server, "http://example.com/signout?X-Requested-With=XMLHttpRequest&ReturnUrl=/");
+ Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
+ var responded = transaction.Response.Headers.GetValues("Location");
+ Assert.Equal(1, responded.Count());
+ Assert.True(responded.Single().StartsWith("/"));
+ }
+
+ [Fact]
+ public async Task AjaxChallengeRedirectTurnsInto200WithLocationHeader()
+ {
+ var server = CreateServer(options =>
+ {
+ });
+
+ var transaction = await SendAsync(server, "http://example.com/challenge?X-Requested-With=XMLHttpRequest&ReturnUrl=/");
+ Assert.Equal(HttpStatusCode.Unauthorized, transaction.Response.StatusCode);
+ var responded = transaction.Response.Headers.GetValues("Location");
+ Assert.Equal(1, responded.Count());
+ Assert.True(responded.Single().StartsWith("http://example.com/Account/Login"));
+ }
+
+
[Theory]
[InlineData(true)]
[InlineData(false)]
@@ -225,9 +286,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
var server = CreateServer(options =>
{
options.SystemClock = clock;
- },
- SignInAsAlice,
- baseAddress: null,
+ },
+ SignInAsAlice,
+ baseAddress: null,
claimsTransform: o => o.Transformer = new ClaimsTransformer
{
OnTransform = p =>
@@ -401,7 +462,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies")))));
var transaction1 = await SendAsync(server, "http://example.com/testpath");
-
+
var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue);
Assert.NotNull(transaction2.SetCookie);
Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name));
@@ -554,7 +615,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
context =>
{
Assert.Equal(new PathString("/base"), context.Request.PathBase);
- return context.Authentication.SignInAsync("Cookies",
+ return context.Authentication.SignInAsync("Cookies",
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))));
},
new Uri("http://example.com/base"));
@@ -573,7 +634,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
{
options.AutomaticAuthenticate = automatic;
options.SystemClock = clock;
- },
+ },
SignInAsAlice);
var transaction1 = await SendAsync(server, "http://example.com/testpath");
@@ -708,7 +769,8 @@ namespace Microsoft.AspNet.Authentication.Cookies
var server = TestServer.Create(app =>
{
app.UseCookieAuthentication();
- app.Run(async context => {
+ app.Run(async context =>
+ {
await Assert.ThrowsAsync(() => context.Authentication.ChallengeAsync());
});
}, services => services.AddAuthentication());
@@ -739,7 +801,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
var server = TestServer.Create(app =>
{
app.UseCookieAuthentication(options => options.LoginPath = new PathString("/login"));
- app.Map("/notlogin", signoutApp => signoutApp.Run(context => context.Authentication.SignInAsync("Cookies",
+ app.Map("/notlogin", signoutApp => signoutApp.Run(context => context.Authentication.SignInAsync("Cookies",
new ClaimsPrincipal())));
},
services => services.AddAuthentication());
@@ -970,6 +1032,10 @@ namespace Microsoft.AspNet.Authentication.Cookies
{
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
+ else if (req.Path == new PathString("/signout"))
+ {
+ await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+ }
else if (req.Path == new PathString("/unauthorized"))
{
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties(), ChallengeBehavior.Unauthorized);
diff --git a/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs b/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs
index 63ab2d36cd..725c5b1f2f 100644
--- a/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs
+++ b/test/Microsoft.Owin.Security.Cookies.Interop.Test/TicketInteropTests.cs
@@ -106,8 +106,7 @@ namespace Microsoft.AspNet.CookiePolicy.Test
var identity = new ClaimsIdentity("Cookies");
identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
- var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("."));
-
+ var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("..\\..\\artifacts"));
var interopServer = TestServer.Create(app =>
{
app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests";
@@ -146,8 +145,7 @@ namespace Microsoft.AspNet.CookiePolicy.Test
identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
user.AddIdentity(identity);
- var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("."));
-
+ var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("..\\..\\artifacts"));
var newServer = TestHost.TestServer.Create(app =>
{
app.UseCookieAuthentication(options => options.DataProtectionProvider = dataProtection);