Support AccessDeniedPath for Cookie 403 redirection
Fixes https://github.com/aspnet/Security/issues/166
This commit is contained in:
parent
08017f992a
commit
e2bb76280f
|
|
@ -323,6 +323,41 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
|
||||
protected override void ApplyResponseChallenge()
|
||||
{
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
// Handle 403 by redirecting to AccessDeniedPath if set
|
||||
if (Options.AccessDeniedPath.HasValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
var accessDeniedUri =
|
||||
Request.Scheme +
|
||||
"://" +
|
||||
Request.Host +
|
||||
Request.PathBase +
|
||||
Options.AccessDeniedPath;
|
||||
|
||||
var redirectContext = new CookieApplyRedirectContext(Context, Options, accessDeniedUri);
|
||||
Options.Notifications.ApplyRedirect(redirectContext);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var exceptionContext = new CookieExceptionContext(Context, Options,
|
||||
CookieExceptionContext.ExceptionLocation.ApplyResponseChallenge, exception, ticket: null);
|
||||
Options.Notifications.Exception(exceptionContext);
|
||||
if (exceptionContext.Rethrow)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (Response.StatusCode != 401 || !Options.LoginPath.HasValue )
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Authentication.Cookies.Infrastructure;
|
||||
using Microsoft.AspNet.Authentication.DataHandler;
|
||||
using Microsoft.AspNet.Authentication.Cookies.Infrastructure;
|
||||
using Microsoft.AspNet.Authentication.DataHandler;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
|
|
|||
|
|
@ -105,6 +105,15 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "By design")]
|
||||
public PathString LogoutPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The AccessDeniedPath property informs the middleware that it should change an outgoing 403 Forbidden status
|
||||
/// code into a 302 redirection onto the given path.
|
||||
///
|
||||
/// If the AccessDeniedPath is null or empty, the middleware will not look for 403 Forbidden status codes, and it will
|
||||
/// not redirect
|
||||
/// </summary>
|
||||
public PathString AccessDeniedPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ReturnUrlParameter determines the name of the query string parameter which is appended by the middleware
|
||||
/// when a 401 Unauthorized status code is changed to a 302 redirect onto the login path. This is also the query
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@ using System.IdentityModel.Tokens;
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Authentication;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
|
||||
|
|
@ -195,6 +194,7 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Response.StatusCode != 401) || (ChallengeContext == null))
|
||||
|
|
|
|||
|
|
@ -90,23 +90,5 @@ namespace Microsoft.AspNet.Authentication
|
|||
|
||||
return Options.AutomaticAuthentication && string.IsNullOrWhiteSpace(authenticationScheme);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to deal with 401 challenge concerns, if an authentication scheme in question
|
||||
/// deals an authentication interaction as part of it's request flow. (like adding a response header, or
|
||||
/// changing the 401 result to 302 of a login page or external sign-in location.)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
// If authenticate was called and the the status is still 401, authZ failed so set 403 and stop
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
return base.ApplyResponseChallengeAsync();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -420,6 +420,27 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CookieTurns401ToAccessDeniedWhenSetAndIfAuthenticated()
|
||||
{
|
||||
var clock = new TestClock();
|
||||
TestServer server = CreateServer(options =>
|
||||
{
|
||||
options.SystemClock = clock;
|
||||
options.AccessDeniedPath = new PathString("/accessdenied");
|
||||
},
|
||||
SignInAsAlice);
|
||||
|
||||
Transaction transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||
|
||||
Transaction transaction2 = await SendAsync(server, "http://example.com/unauthorized", transaction1.CookieNameValue);
|
||||
|
||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
|
||||
Uri location = transaction2.Response.Headers.Location;
|
||||
location.LocalPath.ShouldBe("/accessdenied");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CookieDoesNothingTo401IfNotAuthenticated()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue