Following the docs here I tried to implement a policy-based auth scheme. http://docs.asp.net/en/latest/security/authorization/policies.html#security-authorization-handler-example
I ran into the issue that my Handle method was not being called on my custom AuthorizationHandler. (It does not throw here). It also does inject the dependency currently in the constructor.
Here it the AuthorizationHandler Code.
using WebAPIApplication.Services;
using Microsoft.AspNet.Authorization;
namespace WebAPIApplication.Auth
{
public class TokenAuthHandler : AuthorizationHandler<TokenRequirement>, IAuthorizationRequirement
{
private IAuthService _authService;
public TokenAuthHandler(IAuthService authService)
{
_authService = authService;
}
protected override void Handle(AuthorizationContext context, TokenRequirement requirement)
{
throw new Exception("Handle Reached");
}
}
public class TokenRequirement : IAuthorizationRequirement
{
public TokenRequirement()
{
}
}
}
In Start Up I have
// Authorization
services.AddSingleton<IAuthorizationHandler, TokenAuthHandler>()
.AddAuthorization(options =>
{
options.AddPolicy("ValidToken",
policy => policy.Requirements.Add(new TokenRequirement()));
});
The controller method is
// GET: api/values
[HttpGet, Authorize(Policy="ValidToken")]
public string Get()
{
return "test";
}
Hitting this endpoint returns nothing and there is a warning in the console of
warn: Microsoft.AspNet.Mvc.Controllers.ControllerActionInvoker[0]
Authorization failed for the request at filter 'Microsoft.AspNet.Mvc.Filters.AuthorizeFilter'.
I am able to hit other endpoints that don't have the attribute successfully.
SOS, Jack
In ASP.NET Core, the policy-based authorization framework is designed to decouple authorization and application logic. Simply put, a policy is an entity devised as a collection of requirements, which themselves are conditions that the current user must meet.
In this blog, I am going to take a deep-dive into ASP.Net Core Authorization. Authorization is the process to find out what action a user can perform. In the case of a REST API, it can be the resources a user can access. Or a particular HTTP verb associated with a resource.
ASP.Net core authorization mechanism provides two types of implementation: In role-based authorization, we perform authorization checks with an attribute-based declaration. We will use AuthorizeAttribute attribute in the method which we want to allow access to a specific role.
In ASP.NET Core there are two ways to set up an authorization layer. You can use roles or you can use policies. The former approach—role-based authorization—has been maintained from previous versions of the ASP.NET platform, while policy-based authorization is new to ASP.NET Core. Roles have been used in ASP.NET applications since the early days.
I'm putting this here for reference because I spent way too long figuring this out...
I had implemented a custom requirement and handler (empty for testing's sake):
using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
public class TestHandler : AuthorizationHandler<TestRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TestRequirement requirement)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class TestRequirement : IAuthorizationRequirement
{
}
Registered it in my Startup.cs
ConfigureServices()
section:
services.AddAuthorization(options =>
{
options.AddPolicy("Test", policy => policy.Requirements.Add(new TestRequirement()));
// Other policies here
}
Added it to my controller method:
[HttpGet]
[Authorize(Policy = "Test")]
public IActionResult Index()
{
Return View();
}
But was getting a 403 error (not 401) with every request to the controller method!
Turns out, I was not registering TestHandler
with the ConfigureServices()
(Dependency Injection) section of Startup.cs
.
services.AddSingleton<IAuthorizationHandler, TestHandler>();
Hope this saves someone from banging their head on their desk. :|
The answer to this question is alluded to in a comment to adem caglin, so props to him.
The issue is that the AuthorizeFilter
is rejecting the request before the AuthorizationHandler
is being called. This is because for every use of the Authorize
tag MVC adds AuthorizeFilter
ahead of the AuthorizationHandler
in the pipeline. This AuthorizeFilter
checks to see if any of the current users identities are authorized. In my case there were no authorized identities associated with any user so this would always fail.
A solution (which IMO is somewhat hackish) is to insert a peice of middleware that will get executed before any MVC code. This middleware will add a generic authenticated identity to a User (if the user does not already have one).
Consequently the AuthorizeFilter
check will pass and the Handle
method on the AuthenticationHandler
method will be executed and our problem will be solved. The middleware code (which needs to be added to Configure
before app.UseMvc();
is called) is as follows
app.Use(async (context, next) =>
{
if (!context.User.Identities.Any(i => i.IsAuthenticated))
{
context.User = new ClaimsPrincipal(new GenericIdentity("Unknown"));
}
await next.Invoke();
});
An alternative way to override the AuthorizeFilter
is outline here (Override global authorize filter in ASP.NET Core MVC 1.0)
Citing the response from here (Asp.Net Core policy based authorization ends with 401 Unauthorized)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With