AuthorizationPolicy by UserName was not found - c#

I'm trying to add a new policy based authorization for a certain user who needs to execute certain actions of a controller that requires rights. I'm new with Policy based authorization but I followed all the instructions on this post, and seems pretty simple.
In my Startup.cs and after the AddMvc() method I have:
services.AddAuthorization(options =>
{
options.AddPolicy("AgentsActivityReport ", policy => policy.RequireUserName("AnaR"));
});
Then, In my controller action I have:
[Authorize(Policy = "AgentsActivityReport")]
public ActionResult AgentsActivity()
{
//some code
}
However, when I launch the application, I receive the following error:
InvalidOperationException: The AuthorizationPolicy named:
'AgentsActivityReport' was not found.
I have also readed a few other threads/posts such as:
Claim Based And Policy-Based Authorization With ASP.NET Core 2.1
reported issue
And everything seems pretty much correct. Any thougts?

Based on this post, I was able to declare and allow my user to invoke an action of a controller by using a requirement. Since my condition has to be, "allow certain roles OR a certain user named AnaR", I had to put that logic into the AuthorizationHandler.cs
Startup.cs
services.AddAuthorization(options => {
options.AddPolicy("ReportActivityPolicy", policy =>
{
policy.AddRequirements(new UserNameRequirement("AnaR"));
});
});
services.AddSingleton<IAuthorizationHandler, AgentsActivityAuthorizationHandler>();
And then, in a separate file:
public class AgentsActivityAuthorizationHandler : AuthorizationHandler<UserNameRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserNameRequirement requirement)
{
if (context.User.IsInRole("Administrator") || context.User.IsInRole("Manager") || context.User.Identity.Name == requirement.UserName)
{
context.Succeed(requirement);
}
return Task.FromResult(0);
}
}
public class UserNameRequirement : IAuthorizationRequirement
{
public UserNameRequirement(string username)
{
this.UserName = username;
}
public string UserName { get; set; }
}
Then, in my controller, the following:
[Authorize(Policy = "ReportActivityPolicy")]
public ActionResult AgentsActivity()
{
//code of your controller.
}
Hope it helps!

It seems that you register a policy named "AgentsActivityReport " with a white space at the end in the middleware but annotate the controller without the whitespace "AgentsActivityReport".

Related

ASP.NET Core AuthorizationHandler not being called

I am trying to add some custom role based authorisation, but I am unable to
get the Startup configured to call my AuthorizationHandler.
I found some related information on GitHub: here.
Is this a bug or not ?
I am using ASP.NET Core 3.1 and my initializaion is as follows:
1: This retrieves the url/roles from the database using Dapper ORM:
private List<UrlRole> GetRolesRoutes()
{
var urlRole = DapperORM.ReturnList<UrlRole>("user_url_role_all");
return urlRole.Result.ToList();
}
2: In my Startup, I get the url/roles and store the result in a global variable:
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
this.environment = env;
UrlRoles = GetRolesRoutes();
}
3: My Configuration is: Note the UrlRoles which is passed along
public void ConfigureServices(IServiceCollection services)
{
// .. snip
services.AddAuthorization(o =>
o.AddPolicy(_RequireAuthenticatedUserPolicy,
builder => builder.RequireAuthenticatedUser()));
services.AddAuthorization(options =>
{
options.AddPolicy("Roles", policy =>
policy.Requirements.Add(new UrlRolesRequirement(UrlRoles)));
});
services.AddSingleton<AuthorizationHandler<UrlRolesRequirement>, PermissionHandler>();
}
5: My Handler: which is not being called
public class PermissionHandler : AuthorizationHandler<UrlRolesRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UrlRolesRequirement urlRolesRequirement)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
}
return Task.CompletedTask;
}
}
6: My Requirement class:
public class UrlRolesRequirement : IAuthorizationRequirement
{
private List<UrlRole> UrlRoles { get; }
public UrlRolesRequirement(List<UrlRole> urlRoles)
{
UrlRoles = urlRoles;
}
}
When I debug the ASP.NET Core AuthorizationHandler, I never see that my custom Requirement as being a requirement, which I configured in the Startup. I expected to see the requirement, and if the requirement is present then the "callback" will happen I assume. But for some reason my configuration fails to add the requirement.
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.Resource is TResource)
{
foreach (var req in context.Requirements.OfType<TRequirement>())
{
await HandleRequirementAsync(context, req, (TResource)context.Resource);
}
}
}
Anybody in 2022 having this problem with asp.net core on dotnet 6+ ?
The problem I had with my code was that, I registered my handlers without specifying their interface type of "IAuthorizationHandler".
For example:
Instead of doing this: builder.Services.AddScoped<MustHaveAdminManagementScopeHandler>();
Do this:
builder.Services.AddScoped<IAuthorizationHandler, MustHaveAdminManagementScopeHandler>();
Also, do not register multiple handlers as Singletons. I see the Microsoft docs recommending this, but for some reason, registering multiple IAuthorizationHandler as singletons cause an exception.
Without telling ASP.NET Core to do so, it will not use your configured policy to authorize anything. Authorization policies are so that you can predefine complex authorization conditions so that you can reuse this behavior when you need it. It however does not apply by default, and it couldn’t considering that you already configure two policies: Which of those should apply? All of them? Then why configure separate policies?
So instead, no policy is being used to authorize a user unless you explicitly tell the framework that. One common method is to use the [Authorize] attribute with the policy name. You can put that on controller actions but also on controllers themselves to make all its actions authorize with this policy:
[Authorize("Roles")] // ← this is the policy name
public class ExampleController : Controller
{
// …
}
If you have a policy that you want to use most of the times to authorize users, then you can configure this policy as the default:
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
}
This for example will define a policy that requires an authenticated users as the default. So whenever you use the [Authorize] attribute without specificing an explicit policy, then it will use that default policy.
This all will still require you to mark your routes somehow that you require authorization. Besides using the [Authorize] attribute, you can also do this in a more central location: The app.UseEndpoints() call in your Startup class.
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireAuthorization("Roles");
This is the default route template for controllers but with a call to RequireAuthorization which will basically require the Roles authorization policy on all routes that match this route template.
You can use also use this place to configure different default authorization policies for your different routes: By splitting up your route template, you can have multiple calls to MapControllerRoute with different route templates that all specify their own authorization policy.
I was thinking that instead of decorating each and every controller or action, I rather wanted to have some pre-configuration map in a DB, and then in the pipeline verify the users role or roles which are allocated when the user authenticates. When the user then tries to access a url, the users role gets verified and access is granted or rejected.
You could move the logic how exactly a user is authorized into the authorization handler that verifies your requirement. You would still enable the policy that has this requirement for all the routes you want to test though.
However, I would generally advise against this: Authorization requirements are meant to be simple, and you usually, you want to be able to verify them without hitting a database or something other external resource. You want to use the user’s claims directly to make a quick decision whether or not the user is authorized to access something. After all, these checks run on every request, so you want to make this fast. One major benefit of claims based authorization is that you do not need to hit the database on every request, so you should keep that benefit by making sure everything you need to authorize a user is available in their claims.
Here is a tested solution, which enables runtime configuration changes.
Also relieving the burden of decorating each class or action.
In the Startup Add the Role Authorization Requirement, and also register the
RoleService which will be responsible for ensuring a particular role is authorised to access a particular URL.
Here is the Startup.cs where we configure the requirement and also the role service:
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddRequirements(new UrlRolesRequirement())
.Build();
});
services.AddSingleton<IUserService, UserService>(); // authenticate
services.AddSingleton<IUserRoleService, UserRoleService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); // authorise
The role IUserRoleService: - the UserRoleService implementation
validates a users claimed role (a JWT claim) against a configuration map consisting of url/role entries that are allowed, by looking in either in cached map or retrieving the data from the database.
A typical url(path) to role map has the following format, retrieve from a database, then cached (if lookups fail, the data is retrieved from the database):
/path/to/resource ROLE
public interface IUserRoleService
{
public bool UserHasAccess(ClaimsPrincipal user, string path);
}
The Permission handler:
public class PermissionHandler : IAuthorizationHandler
{
private readonly IUserRoleService userRoleService;
private readonly IHttpContextAccessor contextAccessor;
public PermissionHandler(IUserRoleService userRoleService, IHttpContextAccessor contextAccessor)
{
this.userRoleService = userRoleService;
this.contextAccessor = contextAccessor;
}
public Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
if (!(requirement is UrlRolesRequirement)) continue;
var httpContext = contextAccessor.HttpContext;
var path = httpContext.Request.Path;
if (userRoleService.UserHasAccess(context.User, path))
{
context.Succeed(requirement);
break;
}
}
return Task.CompletedTask;
}
}
The RolesRequirement - just a POCO
public class UrlRolesRequirement : IAuthorizationRequirement
{
}
Here is a partial implementation of the UserRoleService which validates the JWT role claimed.
private bool ValidateUser(ClaimsPrincipal user, string path)
{
foreach (var userClaim in user.Claims)
{
if (!userClaim.Type.Contains("claims/role")) continue;
var role = userClaim.Value;
var key = role + SEPARATOR + path;
if (urlRoles.ContainsKey(key))
{
var entry = urlRoles[key];
if (entry.Url.Equals(path) && entry.Role.Equals(role))
{
return true;
}
}
}
Console.WriteLine("Access denied: " + path);
return false;
}
I had an immediate 403 response, my custom authorization handler code was never reached. Turns out I forgot to inject them (Scoped is fine). That solved the issue for me.

Role based Authorization without Entity Framework?

I love the way asp.net identity lets you neatly manage role based authorizations by just adding an annotation at the top of your controller method. But what if you are not using entity framework? What if you are using ADO.NET with ASP.NET Core API? How do you manage Role Based Authorization?
[Authorize(Roles = Role.Admin)]
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
Perhaps we could mimic this by creating ActionFilter and checking what is the value of global variable that holds user's role? What are my options?
This is a great case for policy-based authorization. You'll use a custom IAuthorizationHandler and IAuthorizationRequirement. See the documentation here: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.0
I've done this in a project recently. You create named policies, in your case something like "Admin". Your AuthorizationHandler will access the current user identity and can look up their current roles by injecting your services into the handler. Then you decorate your controller / actions with [Authorize(Policy = "Admin")].
In my project, my AuthorizationHandler and AuthorizationRequirement look like this:
public class CreateClientHandler : AuthorizationHandler<CreateClientRequirement, CreateClientRequest>, IAuthorizationRequirement
{
private readonly IStudioService _studioService;
public CreateClientHandler(
IStudioService studioService
)
{
_studioService = studioService;
}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CreateClientRequirement requirement,
CreateClientRequest resource
)
{
var userIdClaim = context.User.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Name);
if (userIdClaim == null)
{
context.Fail();
return Task.CompletedTask;
}
if (resource != null)
{
var userId = int.Parse(userIdClaim.Value);
if (!UserIsAdmin(userId))
{
context.Fail();
return Task.CompletedTask;
}
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class CreateClientRequirement : IAuthorizationRequirement {}
In my ConfigureServices, in Startup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("CreateClient", policy =>
policy.Requirements.Add(new CreateClientRequirement()));
});
Once everything is configured, I can decorate controller actions with [Authorize(Policy = "CreateClient")].

asp.net core w/ cookie middleware - accessing request data on authorization

This question is essentially the same as the one here, but, for asp.net core while using the asp.net core cookie middleware.
Is accessing query string/request body data possible on validation, and if it is, would you encourage the idea? It seems that according to this that it is very much possible, however, are the same rules in play from big boy asp.net (such as you are only to read the request data once in a given requests lifetime)?
Example: I'm creating an app where people have one account, but, are members of different teams. They can perform many different actions in the app, and, they can perform that action while in the "context" of one team or another that they are a member of. So, I have a teamId integer being passed in requests made to the server. I'd like to pull claims off the ClaimsPrincipal verifying that they really are a member of that team in the authorization portion of the pipeline.
As you said it is possible to access request's data on OnValidatePrincipal event. So, you can write something like this:
OnValidatePrincipal = async (context) =>
{
if (context.Request.Path.Value.StartsWith("/teams/"))
{
var teamId = // get team id from Path;
if (user is not team member)
{
context.Response.StatusCode = 403;
}
}
}
However, i think your requirement is related Authorization rather than Authentication. I would use Policy-Based Authorization to handle the requirement. Example policy should be like this:
Requirement and Handler:
public class TeamMemberHandler: AuthorizationHandler<TeamMemberRequirement>
{
private readonly IActionContextAccessor _accessor; // for getting teamId from RouteData
public TeamMemberHandler(IActionContextAccessor accessor)
{
_accessor = accessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TeamMemberRequirement requirement)
{
var teamId = // get teamId with using _accessor
if (user is not member of team(by teamId))
{
context.Fail();
}
return Task.FromResult(0);
}
}
public class TeamMemberRequirement : IAuthorizationRequirement
{
}
Configure Services:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddAuthorization(options =>
{
options.AddPolicy("TeamMember",
policy => policy.Requirements.Add(new TeamMemberRequirement()));
});
services.AddSingleton<IAuthorizationHandler, TeamMemberHandler>();
}
Finally use it on top of controller(or if you want, you can add filter globally)
Authorize[(Policy = "TeamMember")]
public class TeamHomeController : Controller
{
// Authorize[(Policy = "AnotherPolicy")]
public IActionResult Index(){}
}

ASP.NET 5 Policy-Based Authorization Handle Not Being Called

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
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)
Take a look at Asp.net Core Authorize Redirection Not Happening i think adding options.AutomaticChallenge = true; solves your problem.

IAuthorizationFilter not called every time

We use an IAuthorizationFilter class to filter all request, and check if custom user claims are still present (multi-tenant app) in the authentication cookie. These information are essentials for the rest of the application. If these informations are not present, we redirect to the Login page.
public class TokenAuthorizationFilter : IAuthorizationFilter, IAsyncAuthorizationFilter
{
public TokenAuthorizationFilter()
{
// Some dependency injection ...
}
public void OnAuthorization(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
{
CheckToken(context);
}
public Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
{
return CheckToken(context);
}
}
And we register our filter like this
services.AddMvc(config =>
{
config.Filters.Add(typeof(TokenAuthorizationFilter));
});
And the controller's action that I want to access is very simple :
[Authorize(Policy = "TokenValid")]
public class HomeController : AjaxBaseController
{
public IActionResult Index()
{
return View();
}
}
We even not reached the Policy of our AuthorizeAttribute. As I can see in the stacktrace, Identity is attempting to create a Microsoft.AspNet.Identity.SignInManager somewhere in the middleware after checking for a CookieAuthenticationOptions, I assumed that he's attempting to re-login the user, but it's not checking for my Filter ? Login is very special in our application, so I don't want to let Identity log automatically our user. I can reproduced this issue when the authentication cookie expired.
Any ideas ? Thanks !
You also need to make TokenAuthorizationFilter inherit from AuthorizeAttribute for an authorization filter, and rename it as TokenAuthorizationFilterAttribute.
This will become an attribute that you will be able to call with [TokenAuthorizationFilter]:
[TokenAuthorizationFilter]
public class HomeController : AjaxBaseController
{
public IActionResult Index()
{
return View();
}
}
Be careful when implementing both IAuthorizationFilter and IAsyncAuthorizationFilter, as ASP.NET Core will only call the async method in this case: if you do not need any async call, then only implement the IAuthorizationFilter interface.
Also, if you keep to register the filter like this:
services.AddMvc(config =>
{
config.Filters.Add(typeof(TokenAuthorizationFilter));
});
You will notice that the filter will be called for every action, as it will force the authorization filter to be called every time, so in this case you do not need to add the attribute on top of your action.
Finally I found out the problem. Every 30 minutes, Identity is trying to validate the user through SecurityStamp validation, and that's making the app crash because it needed a database connection which doesn't exists at the time of the validation. We've desactive this validation in our startup by reimplementing the OnValidatePrincipal :
options.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents { OnValidatePrincipal = (context) => Task.FromResult(0) };

Categories

Resources