Cookies no longer redirects for AJAX requests
This commit is contained in:
parent
35b7248734
commit
1d2c6ba122
|
|
@ -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
|
|||
/// <summary>
|
||||
/// A delegate assigned to this property will be invoked when the related method is called.
|
||||
/// </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);
|
||||
};
|
||||
|
||||
/// <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>
|
||||
/// Implements the interface method by invoking the related delegate method.
|
||||
/// </summary>
|
||||
|
|
@ -71,24 +135,24 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
/// Implements the interface method by invoking the related delegate method.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// Implements the interface method by invoking the related delegate method.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// Implements the interface method by invoking the related delegate method.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// Implements the interface method by invoking the related delegate method.
|
||||
/// </summary>
|
||||
/// <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);
|
||||
}
|
||||
|
||||
[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<InvalidOperationException>(() => 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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue