2

In ASP.NET Core 2 Microsoft are pretty insistent that all authorization tasks are done using policies and requirements. Using the most basic example I can think of:

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    services.AddAuthorization(options => 
    {
      options.AddPolicy("MyPolicy", p => p.RequireAssertion(a => false));
    });
  }
}

Above I add a policy with an assertion that always fails in place of a full requirement, just to illustrate my point. And below is a simple controller with two actions, where access to the second is denied by the above policy.

public class HomeController : HomeController
{
  public IActionResult Allow() => View();

  [Authorize("MyPolicy")]
  public IActionResult Deny() => View();
}

This kind of works as expected. I am indeed denied access to the second action, but I am redirected to https://localhost:44331/Account/AccessDenied?ReturnUrl=%2FHome%2FDeny.

As far as I can tell I have not actually told my app where to send users when a policy requirement isn't met, nor can I work out how to do so.

How do I specify where a user should be redirected to when a requirement isn't met? Also, is it possible to redirect to different places dependent on the policy/requirement?

UPDATE: Just to be clear, I am using ASP.NET Core Identity in my application. And the answer to the first part of my question is to set the AccessDeniedPath property when configuring the application cookie:

services.ConfigureApplicationCookie(o =>
{
    o.AccessDeniedPath = "/Some/Path";
});

But this means that I am stuck redirecting to the same page regardless of what policy/requirement denied access. Is there any way of deciding where to redirect based on the policy or requirement?

Ben
  • 4,915
  • 7
  • 40
  • 57
  • Redirection isn't affected by how the user gets authorized or rather *denied*. The `access denied` redirection *is* configured in the application itself as part of the ASP.NET Identity routing conventions. That's why the path is to `Account/AccessDenied`. `Deny` means *deny* too - no access to any of the secured resources (including controllers) is allowed. – Panagiotis Kanavos Jan 18 '19 at 12:26
  • Ah, I see. I got hung up on authorization being seperate to authentication. @PanagiotisKanavos if you want to add an answer telling me to set the `AccessDeniedPath` when configuring the application cookie I will accept it. – Ben Jan 18 '19 at 12:38
  • 1
    Check [Configure ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-configuration?view=aspnetcore-2.2). The `AccessDeniedPath` is a property in `CookieAuthenticationOptions`. The redirection itself is handled by the `CookieAuthenticationEvents.OnRedirectToAccessDenied` event handler. This is [shown in this SO question](https://stackoverflow.com/questions/48466528/how-to-throw-forbiddenexception-in-asp-net-core-2-instead-of-using-accessdeniedp) – Panagiotis Kanavos Jan 18 '19 at 12:40
  • @Ben authorization *is* separate from authentication. It's authorization that denied access. The reason both are redirected to the same austere page is that you *don't* want to divulge any more information than necessary to someone that, after all, had no business accessing that URL – Panagiotis Kanavos Jan 18 '19 at 12:41
  • 1
    @Ben what is the actual question? Do you want to implement something like Google Doc's page that tells you to ask for permission to access a document ? You *can* do that if you modify your action code to handle authorization itself. `[Authorize("MyPolicy")]` is applied before the action itself is called though – Panagiotis Kanavos Jan 18 '19 at 12:44
  • @PanagiotisKanavos I actually disagree with that. Depending on the requirement that isn't met I may want to tell the user what to do. Using the example Microsoft give with the minimum age policy, it would be helpful to tell the user that they just aren't old enough for that part of the site. Handling specific redirects in each action that the policy applies to isn't very DRY is it? – Ben Jan 18 '19 at 12:47
  • @Ben another option is to configure ASP.NET Core's middleware. Identity itself is a middleware added to the request pipeline. I suspect it's possible to redirect to a specific page if such and such a condition are met etc. The trick would be to put your middleware after the claims are created and before the actual denial. – Panagiotis Kanavos Jan 18 '19 at 12:48
  • @PanagiotisKanavos would you be able to provide an example of how to do that? Lets say that if `PolicyA` denies access I would like to send users to `ActionA`, and `PolicyB` would send them to `ActionB`? – Ben Jan 18 '19 at 12:54
  • that's not DRY, that's a different *business* scenario. The default when talking about security is "say as little as possible". The age example though is the same example used in [Custom Authorization Policy Providers using IAuthorizationPolicyProvider in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-2.2). Is that what you tried? That's why Kirk Larkin asks of your configuration code. I may be describing what you've *already* done – Panagiotis Kanavos Jan 18 '19 at 12:55
  • Or is it [Policy-based authorization in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2) which shows a named policy and a custom AuthorizationHandler? – Panagiotis Kanavos Jan 18 '19 at 13:00
  • @PanagiotisKanavos sorry for the delay, I am not ignoring you and appreciate your help. I am just stripping down my application to something I can share with you and reading the pages you linked to. – Ben Jan 18 '19 at 13:01
  • Seems there's [a duplicate already](https://stackoverflow.com/questions/46089315/asp-net-core-2-0-redirecting-user-from-authorizationhandler-handlerequirementas?rq=1)! Inside `HandleRequirementAsync` you can set a `RedirectToActionResult` – Panagiotis Kanavos Jan 18 '19 at 13:03
  • @PanagiotisKanavos the accepted answer there is not correct as you cannot cast `AuthorizationHandlerContext` to `AuthorizationFilterContext` :-( – Ben Jan 18 '19 at 13:14
  • It's casting `AuthorizationHandlerContext.Resource` to `AuthorizationFilterContext`, not `AuthorizationHandlerContext` itself. [AuthorizationHandlerContext.Resource](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizationhandlercontext.resource?view=aspnetcore-2.2) returns an `object` – Panagiotis Kanavos Jan 18 '19 at 13:15
  • Can we forget those last 2 comments? ;-) – Ben Jan 18 '19 at 13:16
  • @PanagiotisKanavos that does indeed work; but you have to call `context.Succed(requirement)` which seems a bit counter intuitive and means you can't short circuit the rest of the policy pipeline, meaning another handler may well change where the redirect is going. – Ben Jan 18 '19 at 13:27

0 Answers0