I'm just getting started with ASP.NET Core Identity and have the following requirements defined:
public sealed class IsCustomerUserRequirement : IAuthorizationRequirement
public sealed class IsSuperUserRequirement : IAuthorizationRequirement
With the following basic handlers:
public class IsCustomerUserHandler : AuthorizationHandler<IsCustomerUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == "customer"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class IsSuperUserHandler : AuthorizationHandler<IsSuperUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsSuperUserRequirement requirement)
{
if (context.User.IsInRole("super_user"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
I can then put these inside basic policies:
services
.AddAuthorization(options =>
{
options.AddPolicy("MustBeSuperUser", policy => policy.Requirements.Add(new IsSuperUserRequirement()));
options.AddPolicy("CustomersOnly", policy => policy.Requirements.Add(new IsCustomerUserRequirement()));
});
And apply it using [Authorize("CustomersOnly")], which works fine.
My requirement is to be able to allow super users, claim principals with the super_user role but without the customer claim, to also access Customers Only areas.
I have currently implemented this by changing the handler to manually check:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == Claims.Customer) ||
context.User.IsInRole(Roles.SuperUser))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
My issue is this feels like I'm missing the point. Is there a better way to define this so I don't have to repeat the super user check in each handler in future?
The bigger picture in all this is I use IdentityServer4 (ASP.NET Identity-backed) to Authenticate, and then intend to use some JWT-based claims (one claim, two roles) to further identify the user Authorisation falls into an application-specific roles / permissions structure and some custom middleware that has nothing to do with Identity Server. What, if any, best practices are there around this topic?
“this feels like I'm missing the point” – Yes, in a way you are missing the point. You are doing role based authorization: A user can be a customer or a super user.
But instead, the new model is claims based authorization where the user has a claim about something, and you are using that to authorize them. So ideally, the super user would get the same claim the customer gets, and is allowed access to the resource that way. Such a claim also wouldn’t be called customer then, but be rather something that is a property of the user.
You can still use a role-based authorization model with claims but you should probably avoid mixing them. As you noticed yourself, this gets a bit weird eventually.
That being said, there are multiple ways to succeed a policy using different requirements. If you were using roles only (instead of that customer claim), you could simply use the built-in way:
options.AddPolicy("MustBeSuperUser", policy => policy.RequireRole("super_user"));
options.AddPolicy("CustomersOnly", policy => policy.RequireRole("customer", "super_user"));
That way, the CustomersOnly policy would be fulfilled by both customer and super_user roles.
Since you aren’t using a role for your customers, you will have to follow your requirements implementation here. The way authorization requirements work though is that you can have multiple handlers for the same requirement type and only one of them needs to succeed (as long as none fails) for the requirement to be successful.
So you could have your IsSuperUserHandler handle multiple requirements. YOu can follow the AuthorizationHandler<T> implementation to make this work:
public class IsSuperUserHandler : IAuthorizationHandler
{
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var req in context.Requirements)
{
if (req is IsSuperUserRequirement || req is IsCustomerUserRequirement)
{
if (context.User.IsInRole("super_user"))
context.Succeed(req);
}
}
}
}
So your IsSuperUserHandler is now an authorization handler for both the IsSuperUserRequirement and the IsCustomerUserRequirement. So the CustomersOnly policy that requires the IsCustomerUserRequirement will also be fulfilled for super users.
Related
In a Rest Api SaaS project developed with .Net Core 3.1.
When the user's subscription expires (needs to pay), what kind of a method would be better to follow.
There are 2 methods that I think of but I think there will be some problems in both of them.
Method 1) Checking the subscription status during JWT generate and not generating JWT if the subscription period has expired:
If I use this method;
Advantage: Since a token is not given to a user whose subscription expires,
they will not be able to access other endpoints.
I think this will work extremely safe without doing any other coding work.
Disadvantage: When I need to redirect the user to the payment page,
I will have to do a special work for the payment endpoints since there are no tokens.(Example: Password Reset Methods)
I will get it with query string, I think I can create a special token for this method.
But I think there might be a security bug because I couldn't protect this process with my standard authorization method?
Method 2) Even if the subscription expires, jwt will be generated, but membership will be restricted:
If I use this method;
Advantage: I can use my standard authorization method without any problems
when I need to direct the user to the payment endpoints or to another endpoints.
I will use with jwt and security bugs will be considerably reduced.
Disadvantage: I need to determine endpoints that cannot be accessed on the application for user whose subscription period expired
and I will need to code a working service in middleware that will make them inaccessible. (Like to permission methods)
This will both do extra coding work and each endpoint will require extra work.
These are my thoughts....
Or other solutions...
How should we restrict a user whose subscription expires and how should we act?
Thank you very much for your information sharing.
I solved the question I asked above using Method 2.
I wanted to explain how I did it, as I thought it might help those who investigate this question in the future.
I said in method 2, jwt has generated but membership restricted.
First of all, when generating tokens, I set claims whether they have a subscription or not.
....
new Claim(JwtClaimIdentifier.HasSubscription, hasSubscription)
I do not explain here in detail. Standard claims.
Subscription Control
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class SubscriptionRequiredAttribute : TypeFilterAttribute
{
public SubscriptionRequiredAttribute()
: base(typeof(SubscriptionFilter)) { }
}
--
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class AllowWithoutSubscriptionAttribute : Attribute
{
public AllowWithoutSubscriptionAttribute() { }
}
--
public class SubscriptionFilter : IAuthorizationFilter
{
private bool AllowWithoutSubscription(AuthorizationFilterContext context)
{
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
bool allowWithoutSubscriptionForMethod = controllerActionDescriptor.MethodInfo.CustomAttributes.Any(x => x.AttributeType == typeof(AllowWithoutSubscriptionAttribute));
if (allowWithoutSubscriptionForMethod)
return true;
bool allowWithoutSubscriptionForController = controllerActionDescriptor.ControllerTypeInfo.CustomAttributes.Any(x => x.AttributeType == typeof(AllowWithoutSubscriptionAttribute));
if (allowWithoutSubscriptionForController)
return true;
return false;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (AllowWithoutSubscription(context))
return;
var hasSubscription = context.HttpContext.User.Claims.First(x => x.Type == JwtClaimIdentifier.HasSubscription).Value.ToLower() == "true";
if (!hasSubscription)
context.Result = new BadRequestObjectResult(**ErrorCode**);
}
}
I added, an attribute that override subscription control.
For example; To use it in a controller or method that I need to override when checking subscriptions on base.
Use Controller
[SubscriptionRequired]
public class FooController
{
public async Task<IActionResult> FooMethodOne(){...}
public async Task<IActionResult> FooMethodTwo(){...}
[AllowWithoutSubscription]
public async Task<IActionResult> FooMethodThree(){...}
}
While FooMethodOne and FooMethodTwo above require subscription, FooMethodThree will work without subscription.
Likewise, all controls are called "AllowWithoutSubscription".
It can also be called "SubscriptionRequired" in methods.
Hopefully it benefits your business...
I want to improve my API's security with some sort of "self" policy to validate the call to some user actions (like DELETE user) is made by the same user the token was issued to. Is there a way to do this in a similar way to the policy based authorization?
I have a .Net Core 2.2 with MVC WebAPI running on Kestrel. I have users, roles and user-roles and I have token-based authentication with roles enabled. I can issue tokens and validate then with the "Authorize" attribute in the controllers. However, I've been looking for a way to validate that some actions to users are made only by the users itself, a "self" authentication policy to validate that, for example, user 3 is trying to delete user 3 and only user 3. I've dug up to the claims and everything and I know I can make a simple service passing the claims and the validating it but I wanted to do it in a smoother way similar to the policy-based or role-based authentication. I don't know if I can make it with some sort of middleware or something but it would be great to be able to make it as clean as possible.
[Edit]
The main purpose is to avoid users to delete resources created by other users and make them be able only to delete resources created by themselves.
[Edit2 - Solution]
Thanks to Paul Lorica's Answer I can now describe how I did it.
The first thing is to create a Requirement and a Handler similar to the examples provided by Microsoft in the docs. What we do is to add a Claim to the token generation method/service we have and add the ID as NameIdentifier. After that, we inject in the IHttpContextAccessor in the handler. And then we can validate if the ID in the request is the same than the Id in the Claim. So it was very easy.
I'm adding examples of logic to make it work.
PS: Inject IHttpContextAccessor as a singleton in the startup clas or it won't work.
Handler:
public class SelfUserHandler: AuthorizationHandler<SelfUserRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public SelfUserHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SelfUserRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
{
return Task.CompletedTask;
}
var nameIdentifier = context.User.FindFirst(c => c.Type == ClaimTypes.NameIdentifier).Value;
if (_httpContextAccessor.HttpContext.Request.Path.ToString().ToUpper().Contains(nameIdentifier.ToUpper()))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
Requirement
public class SelfUserRequirement : IAuthorizationRequirement
{
public SelfUserRequirement() { }
}
Additional info:
Nate Barbettini Answer here
Joe Audette Answer here
First off, when your code validates against the policy, the policy has no understanding, and does not need to know, what you are doing.
I suppose you can retrieve the context via URL. So say if its a DELETE user/3
then you can create a policy that would check the user's claims that it has an ID == 3.
See the docs here on creating policies and accessing the httpContext
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2
Its a bit of a naive check, I would rather just place that logic within the method of the controller.
I am trying to figure out what is the best way to create custom authorization attribute for my asp.net core application. I have seen this post and I am aware of the 2 approaches discussed here.
How do you create a custom AuthorizeAttribute in ASP.NET Core?
1) Using IAuthorizationFilter
2) Using Policies
I saw that the official document suggests that we should be using policies and not IAuthorizationFilter but I felt that using policies for my scenario is an overkill. I personally liked IAuthorizationFilter approach more.
I have a very basic requirement. I want to create an authorize attribute for my web api and need to throw 403 if the current user is not whitelisted to use this API. I really don't care about the scopes(canRead, canWrite, can readWrite etc). If I go ahead with policy approach, I may be using the same policy for all my APIs.
What is the best way to achieve this?
Using policies for something like this isn't overkill. You need a requirement:
public class WhitelistRequirement: IAuthorizationRequirement
{
}
A handler:
public class WhitelistHandler : AuthorizationHandler<WhitelistRequirement>
{
// Implement a constructor to inject dependencies, such as your whitelist
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
WhitelistRequirement requirement)
{
if (isInWhitelist) // Your implementation here
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Register both in ConfigureServices:
services.AddAuthorization(options =>
options.AddPolicy("WhitelistPolicy",
b => b.AddRequirements(new WhitelistRequirement())));
services.AddSingleton<IAuthorizationHandler, WhitelistHandler>();
Then use your policy:
[Authorize(Policy = "WhitelistPolicy")]
You can apply the policy globally with a global filter:
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.AddRequirements(new WhitelistRequirement())
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
})
The resulting behavior for unauthenticated or forbidden users depends on the implementation of the "challenge" and "forbid" behaviors in your app's authentication handler.
See here.
I am looking for a solution/suggestion that helps me creating permission based access to web api endpoints/controller actions.
Role based access is not suitable becuase I don't have fixed rules that I could use in code like Role("Admin") oder Role("Controller").
Claim based permissions is also not feasable because each user/client can have different permissions on each business object/entity (e.g. Read/Write-access to own tickets and read access to all ticket of his/her company or if its a technician of my company full access to all tickets of all customers. So each user would have 10s or even hundrets of claims which I would have to evaluate at each access of my API.
It is some kind of multi tenancy in just on database and the tenants are our customers with some kind of "master tenant" that has access to all of the tenant data.
I think that something like Visual Guard would satisfy my needs but it is pretty expensive and they don't support net core for now and their documentation seems pretty outdated.
I don't need a usable solution at once but some hints and tricks how I could achieve that would very much be apprieciated because I am looking and searching for some time now.
Details on "database permissions":
What I mean is in my frontend (Winforms app) I want to establish a security system where I can create and assign roles to users and in those roles is defined which actions a user can execute and which CRUD operations he/she can do on specific business objects. Each role can have n users and each role can have n permissions. Each permission on itself declares for exmaple Create:false, Read:true, Write:true and Delete:false. If a permission for a specific business object is not found CRUDs on that BO is denied totally.
So whenever an action in my API is called I have to check if that user and his/her rule allows him to do that specific action based on rules and permissions in my database.
Details an application structure:
Frontend will be a Winforms app which calls the API in the background by OData. I don't want to rely solely on security in the Winforms app because the API will be accessible from the internet and I can't be sure if a user would not try to access the api with his credentials just to see what is possblie without the "frontend filter". So the permissions lie in the API and if a user tries to access s.t. in the frontend app the app itself "asks" the API if that is possible.
Later on I want to create mobile clients that also use the Odata Web API.
The relevant API in asp.net core are:
IAuthorizationService
AuthorizationPolicy
IAuhtorizationRequirement
IAuthorizationHandler
The authorization pattern you are looking for is called Resource-based authorization
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-2.2
Basically, you can define AuthorizationPolicy, and apply it to a instance of a resource:
var ticket = _ticketRepository.Find(ticketID);
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, ticket, "EditTicketPolicy");
In the authorization handler, you can check if the user is the owner of the resource.
public class ResourceOwnerRequirement : IAuthorizationRequirement
{
}
public class ResourceOwnerHandler
: AuthorizationHandler<ResourceOwnerRequirement, MyBusinessObject>
//: AuthorizationHandler<ResourceOwnerRequirement> use this overload to handle all types of resources...
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
ResourceOwnerRequirement requirement,
MyBusinessObject resource)
{
int createdByUserId = resource.CreatedBy;
Claim userIdClaim = ((ClaimsIdentity)context.User.Identity).FindFirst("UserId");
if (int.TryParse(userIdClaim.Value, out int userId)
&& createdByUserId == userId)
{
context.Succeed(requirement);
}
}
}
//admin can do anything
public class AdminRequirementHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.User.Claims.Any(c => c.Type == "Role" && c.Value == "Administator"))
{
while (context.PendingRequirements.Any())
{
context.Succeed(context.PendingRequirements.First());
}
}
return Task.CompletedTask;
}
}
BTW, this still can be called claims or role based authorization. Users with specific role can edit their own tickets, but users with admin role also other tickets. The difference is that you apply authorization to a resource, not just action
EDIT:
I was trying to make a custom authorization attribute in ASP.NET vNext, until I found this excelent answer from #blowdart in this post:
https://stackoverflow.com/a/31465227/1756978
indicating that Authorization requirements is now the way to go. The answer is very clarifying but doesn't indicates how to pass a parameter to this requirements / policies.
What I'm trying to do is porting a MVC 5 custom authorization attribute which has this signature:
[Autorizacion(Requires = enumPermission.DeleteCustomer)]
since I use a very customised set of permissions mirrored in the backend/frontend as enums/strings.
As this features are still not documented I feel a little lost... Could anybody give guidance about?
Thanks in advance
I happen to comes up with a workround that can satisfy my requirement, hope it will help your too.
In my case, I need to pass IHttpContextAccessor and EFCore's AppDbContext to my Requirement class.
in my Startup.cs, I write something like this:
services.AddAuthorization(options =>
{
options.AddPolicy("ThePolicy", policy => policy.Requirements.Add( new ThePolicyRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ThePolicyAuthorizationHandler>();
the ThePolicyAuthorizationHandler class:
public class ThePolicyAuthorizationHandler : AuthorizationHandler<ThePolicyRequirement>
{
readonly AppDbContext _appContext;
readonly IHttpContextAccessor _contextAccessor;
public ThePolicyAuthorizationHandler(AppDbContext c, IHttpContextAccessor ca)
{
_appContext = c;
_contextAccessor = ca;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ThePolicyRequirement requirement)
{
var result = await requirement.isPass(_appContext, _contextAccessor, context);
if (result)
context.Succeed(requirement);
else
context.Fail(requirement);
}
}
and ThePolicyRequirement class:
public class ThePolicyRequirement : IAuthorizationRequirement
{
AppDbContext _context;
IHttpContextAccessor _contextAccessor;
AuthorizationHandlerContext _authHandlerContext;
public async Task<bool> isPass(AppDbContext context, IHttpContextAccessor contextAccessor, AuthorizationHandlerContext authorizationHandlerContext)
{
_context = context;
_contextAccessor = contextAccessor;
_authHandlerContext = authorizationHandlerContext;
//logic here
return result;
}
}
The key idea is using ThePolicyAuthorizationHandler to obtain as much as possible all needed objects, and pass it to ThePolicyRequirementto do the logic of the authorization mechanism.
Indeed, #blowdart’s post is very insightful and from my understanding, the key thing to understand is the following:
Authorization act upon Identities. Identities are created by
authentication.
So it seems that identities are created by the authentication process.
Then (if you wish) you can make the authorization process kick in. This means creating a custom authorization requirements to which this requirement will be looking at those identities and act upon them.
In plain English, this is what I believe is happening:
As mentioned in blowdart’s post, we should have some sort of
authentication middleware that happens to do the actual
authentication. Once successfully authenticated, you take whatever
information you want from that now-authenticated user and create an
authenticated ClaimsPrincipal.
For example, we could store into that ClaimsPrincipal, the sets of
permission the user has.
Then, when you create your authorization requirement you look at the
ClaimsPrincipal, extract the sets of permissions from the
ClaimsPrincipal and take appropriate action based on whatever
business rules you want.
Assuming you can’t store the sets of permission into the
ClaimsPrincipal for some reason, one could easily store the UserId
and from within the requirement, read that UserId from the
ClaimsPrincipal, invoke the database and get the sets of permissions
and then act upon them.
Conclusion:
So in short, I don’t think you pass stuff to the requirement(s), I think you obtain them from within a ClaimsPrincipal.
In your example, you could create a requirement that reads the ClaimsPrincipal and compare whatever value with your Enum and act upon that.
Let us know what you’ve managed to do and if it works.
And if my understanding of this is wrong, then by all means, feel free to correct me since all of this is new stuff :-)