Cookies no longer redirects for AJAX requests
This commit is contained in:
parent
35b7248734
commit
1d2c6ba122
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Authentication.Cookies
|
namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
{
|
{
|
||||||
|
|
@ -36,12 +37,75 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A delegate assigned to this property will be invoked when the related method is called.
|
/// A delegate assigned to this property will be invoked when the related method is called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<CookieRedirectContext, Task> OnRedirect { get; set; } = context =>
|
public Func<CookieRedirectContext, Task> 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);
|
return Task.FromResult(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate assigned to this property will be invoked when the related method is called.
|
||||||
|
/// </summary>
|
||||||
|
public Func<CookieRedirectContext, Task> 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate assigned to this property will be invoked when the related method is called.
|
||||||
|
/// </summary>
|
||||||
|
public Func<CookieRedirectContext, Task> OnRedirectToLogout { get; set; } = context =>
|
||||||
|
{
|
||||||
|
if (IsAjaxRequest(context.Request))
|
||||||
|
{
|
||||||
|
context.Response.Headers["Location"] = context.RedirectUri;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.Redirect(context.RedirectUri);
|
||||||
|
}
|
||||||
|
return Task.FromResult(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate assigned to this property will be invoked when the related method is called.
|
||||||
|
/// </summary>
|
||||||
|
public Func<CookieRedirectContext, Task> 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";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the interface method by invoking the related delegate method.
|
/// Implements the interface method by invoking the related delegate method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -71,24 +135,24 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
/// Implements the interface method by invoking the related delegate method.
|
/// Implements the interface method by invoking the related delegate method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">Contains information about the event</param>
|
/// <param name="context">Contains information about the event</param>
|
||||||
public virtual Task RedirectToLogout(CookieRedirectContext context) => OnRedirect(context);
|
public virtual Task RedirectToLogout(CookieRedirectContext context) => OnRedirectToLogout(context);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the interface method by invoking the related delegate method.
|
/// Implements the interface method by invoking the related delegate method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">Contains information about the event</param>
|
/// <param name="context">Contains information about the event</param>
|
||||||
public virtual Task RedirectToLogin(CookieRedirectContext context) => OnRedirect(context);
|
public virtual Task RedirectToLogin(CookieRedirectContext context) => OnRedirectToLogin(context);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the interface method by invoking the related delegate method.
|
/// Implements the interface method by invoking the related delegate method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">Contains information about the event</param>
|
/// <param name="context">Contains information about the event</param>
|
||||||
public virtual Task RedirectToReturnUrl(CookieRedirectContext context) => OnRedirect(context);
|
public virtual Task RedirectToReturnUrl(CookieRedirectContext context) => OnRedirectToReturnUrl(context);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the interface method by invoking the related delegate method.
|
/// Implements the interface method by invoking the related delegate method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">Contains information about the event</param>
|
/// <param name="context">Contains information about the event</param>
|
||||||
public virtual Task RedirectToAccessDenied(CookieRedirectContext context) => OnRedirect(context);
|
public virtual Task RedirectToAccessDenied(CookieRedirectContext context) => OnRedirectToAccessDenied(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +33,67 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
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]
|
[Theory]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
|
|
@ -225,9 +286,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var server = CreateServer(options =>
|
var server = CreateServer(options =>
|
||||||
{
|
{
|
||||||
options.SystemClock = clock;
|
options.SystemClock = clock;
|
||||||
},
|
},
|
||||||
SignInAsAlice,
|
SignInAsAlice,
|
||||||
baseAddress: null,
|
baseAddress: null,
|
||||||
claimsTransform: o => o.Transformer = new ClaimsTransformer
|
claimsTransform: o => o.Transformer = new ClaimsTransformer
|
||||||
{
|
{
|
||||||
OnTransform = p =>
|
OnTransform = p =>
|
||||||
|
|
@ -401,7 +462,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies")))));
|
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies")))));
|
||||||
|
|
||||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||||
|
|
||||||
var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue);
|
var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue);
|
||||||
Assert.NotNull(transaction2.SetCookie);
|
Assert.NotNull(transaction2.SetCookie);
|
||||||
Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name));
|
Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name));
|
||||||
|
|
@ -554,7 +615,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
context =>
|
context =>
|
||||||
{
|
{
|
||||||
Assert.Equal(new PathString("/base"), context.Request.PathBase);
|
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 ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))));
|
||||||
},
|
},
|
||||||
new Uri("http://example.com/base"));
|
new Uri("http://example.com/base"));
|
||||||
|
|
@ -573,7 +634,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
{
|
{
|
||||||
options.AutomaticAuthenticate = automatic;
|
options.AutomaticAuthenticate = automatic;
|
||||||
options.SystemClock = clock;
|
options.SystemClock = clock;
|
||||||
},
|
},
|
||||||
SignInAsAlice);
|
SignInAsAlice);
|
||||||
|
|
||||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||||
|
|
@ -708,7 +769,8 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var server = TestServer.Create(app =>
|
var server = TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
app.UseCookieAuthentication();
|
app.UseCookieAuthentication();
|
||||||
app.Run(async context => {
|
app.Run(async context =>
|
||||||
|
{
|
||||||
await Assert.ThrowsAsync<InvalidOperationException>(() => context.Authentication.ChallengeAsync());
|
await Assert.ThrowsAsync<InvalidOperationException>(() => context.Authentication.ChallengeAsync());
|
||||||
});
|
});
|
||||||
}, services => services.AddAuthentication());
|
}, services => services.AddAuthentication());
|
||||||
|
|
@ -739,7 +801,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
var server = TestServer.Create(app =>
|
var server = TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
app.UseCookieAuthentication(options => options.LoginPath = new PathString("/login"));
|
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())));
|
new ClaimsPrincipal())));
|
||||||
},
|
},
|
||||||
services => services.AddAuthentication());
|
services => services.AddAuthentication());
|
||||||
|
|
@ -970,6 +1032,10 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
||||||
{
|
{
|
||||||
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
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"))
|
else if (req.Path == new PathString("/unauthorized"))
|
||||||
{
|
{
|
||||||
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties(), ChallengeBehavior.Unauthorized);
|
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties(), ChallengeBehavior.Unauthorized);
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,7 @@ namespace Microsoft.AspNet.CookiePolicy.Test
|
||||||
var identity = new ClaimsIdentity("Cookies");
|
var identity = new ClaimsIdentity("Cookies");
|
||||||
identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
|
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 =>
|
var interopServer = TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests";
|
app.Properties["host.AppName"] = "Microsoft.Owin.Security.Tests";
|
||||||
|
|
@ -146,8 +145,7 @@ namespace Microsoft.AspNet.CookiePolicy.Test
|
||||||
identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
|
identity.AddClaim(new Claim(ClaimTypes.Name, "Alice"));
|
||||||
user.AddIdentity(identity);
|
user.AddIdentity(identity);
|
||||||
|
|
||||||
var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("."));
|
var dataProtection = new DataProtection.DataProtectionProvider(new DirectoryInfo("..\\..\\artifacts"));
|
||||||
|
|
||||||
var newServer = TestHost.TestServer.Create(app =>
|
var newServer = TestHost.TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
app.UseCookieAuthentication(options => options.DataProtectionProvider = dataProtection);
|
app.UseCookieAuthentication(options => options.DataProtectionProvider = dataProtection);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue