How to check access to a given ressource with Asp.Net Core Identity attributes - asp.net-core

We are currently architecturing our next application. For each deployment, we will have different "organizational" level:
We will have several endpoints, either to read either to edit data for each division/factory. The idea is to assign a user with a permission to any level of this tree:
UserA can be Reader of Division 1 and Admin of Division 2(which would
cascade the rights to their factory)
UserB could be just reader of Factory 3
UserC could be reader of the Root-organization(which will cascade) but admin of factory 3
Now, let's admit we are trying to read/write
GET : http://service-url/api/factories/4
--> UserA/UserC should be allowed but not UserB POST: http://service-url/api/factories/4
--> UserA/UserC should be allowed but not UserB
GET : http://service-url/api/factories/3
--> UserA/UserB/UserC should be allowed POST: http://service-url/api/factories/3
--> UserA/UserC should be allowed but not UserB
I've started to check how to do this, I've seen that I can add additional claims in my token. I could even compute every inherited rights at the creation of the token and having something like:
new Claim("roles", "division2:admin,factory3:read")
I've then searched how I can validate that the current user has the rights to execute the service URL, but the closest thing I could find was to do a policy:
builder.Services.AddAuthorization(authOpt =>{
authOpt.AddPolicy("IsRessourceAdmin", policyBuilder =>{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.requireClaim("roles", ...);
}
});
but:
I don't think I've access to the ":id" currently being modified
I'm not sure we can do "contains" or custom logic to validate the claim?
So it possible to validate my scenario? It feels kind of a standard scenario, where the role is assigned to a given ressource for a given user, but I can't find a solution for it.

A better approach is not to use roles, and instead use claims as a way to determine authorization.
Basically, you could have that a user have the following claims:
Divisions: [division1]
Factories: ["factory1:read", "factory2:readwrite"]
Do check out this video https://www.youtube.com/watch?v=t34Cff0pBmA&ab_channel=NDCConferences
To implement the above, do check out the policy concept in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0
With requirements, your architecture looks like this:

Related

Is the appropriate way to fetch user roles/permissions/information from an ID Token or an API endpoint (or other)?

When creating an Angular web application that also has a backend API, I feel like there are a few different options when it comes to getting User Info such as roles/permissions/display name/email/etc.
We can use an ID Token to store user claims like this. That token can be put into local storage or a cookie and the Angular app can read it and render the UI/guard against unauthorized route navigation/etc as soon as the app spins up (since the ID token is available right then and there).
We can NOT use an ID Token for this information at all and instead have an API endpoint that we have to call every page re-load to fetch this data. The server would decode our access token/ID token and return the data in JSON format.
Lastly, there could be some hybrid solution where basic User Info like names/emails are stored int he ID token and available right away, but user permissions (which could be a larger payload and maybe not wanted in a token that should be small) could be fetched via an API
Is there maybe a 4th option I didn't think about?
I haven't been able to find many conventions around which of these options is the best. I like the ID token option as it requires no "blocking" of the UI until the API request is done making the page load that much faster, but I'm not sure if that goes against other conventions.
All your approaches rely on a permissions-based system where you would have been granted permissions upon login. These are sometimes referred to as birth rights since they are typically given when the user is created or whenever their permission sets change. The typical way to carry birth rights around is to have them as scopes / assertions inside an identity token (e.g. OAUth 2.0) that you pass along from service to service.
You can also have your applications retrieve additional permissions / roles / entitlements from a backend store (a database for instance) based on the user ID so that you know what your user can or cannot do.
So far this is essentially role-based access control / permissions-based access control.
The main challenge with that approach is role explosion / permissions explosion as well as token bloat (too many permissions in the token) and administration pains - you have to assign roles and permissions to users all the time. You have to deprovision. It becomes a management nightmare and a risk you may have the wrong permissions set for users. You then need to think about identity and access governance as well as recertification. Heavy.
What's the alternative?
You definitely need some roles - yes - but they should be kept to a minimum - essentially the business roles you need in your apps e.g. a doctor, a nurse, a non-medical staff rather than doctor_hospital1_unitA.
You should then express your authorization as plain-old English policies using any number of attributes - not just user attributes but also contextual information (time, location), resource information (what type of object, who owns it, where is it? How sensitive is it?), and action information (view, edit, delete...).
Sample Policies
A doctor can view a medical record if they are assigned to the patient the medical record belongs to
A nurse can view a medical record if the medical record is in the same unit as the nurse
A non-medical staff can view the financial section of a medical record but not the medical section.
Attribute-Based Access Control
Following this approach is called attribute-based access control (abac). In ABAC, you clearly decouple your app from the authorization process. Authorization is expressed as policies rather than code which makes it easier to:
update
audit
review
How to implement?
You have several options to implement ABAC (from open-source to commercial). You can go down the XACML (xacml) path, the ALFA alfa path, or others. They all have similar architectures with:
the notion of a policy decision point (PDP): a service that evaluates the authorization requests against the set of policies you defined and produce decisions (Permit / Deny) that can be enriched with additional information e.g. order to do two-factor Authentication.
the notion of a policy enforcement point (PEP): an interceptor that sits in front of or inside your API that will send an authorization request to the PDP.
I've written about the architecture more in detail in this SO post.
ALFA Example
In ALFA, a sample policy would look like:
policyset viewMedicalRecord{
target clause object == "medical record" and action == "view"
apply firstApplicable
policy allowDoctors{
target clause role == "doctor"
apply firstApplicable
rule allowAssignedPatient{
permit
condition patient.assignedDoctor == user.name
}
}
}

How do I restrict a user from accessing/updating another user's details?

I have an http endpoint /update-user-details that is authenticated by a JWT token.
There are two valid users in my system User1 and User2.
How do I restrict User1 from updating User2's details using the /update-user-details endpoint?
You have 3 options:
DIY: implement code yourself that will do it. That's what Chappie Johnson recommends in their response.
Externalize authorization logic: use an authorization framework to do the check for you. The way to externalize really depends on the framework you developed the API in. For instance, you could look into Flask Authorization for Python or Ruby CanCanCan or .NET claims.
Externalize authorization using a standard approach: Attribute-Based Access Control (ABAC) is actually what you are looking for. In ABAC you write policies that state what can and cannot happen. alfa and xacml are the two ways you can write policies. The good thing about this approach is that you can always change the policies without rewriting your API.
In your JWT you should have a claim in the body of the token that contains the user id of the requesting user. Before making an edit, you could check to see that the user_id value in your JWT matches the user_id value that user1 is attempting to edit. If the user_id's do not match, then reject the change.
String userId = getUserIdFromJwt();
if (!userId.equals("some user id")) {
throw new HttpUnauthorizedException("You do not have access to edit" +
"this resource.");
}
You have all the information about the current requesting user in the JWT so you are able to make assertions about the user.

Can we restrict users in identity server4 to specific applications?

I am trying to implement IdentityServer 4 for enterprise scenario.
I understand that users are registered against Identity server.
My question is how to give permissions to users against applications, like as users are needed to assign to a particular application, if not assigned application should return unauthorized.
If a user needs to access multiple applications then multiple assignments are needed.
I am looking a way for Identity server to invalidate the submitted token if the user doesn't have access to the application in a single go, even though the challenged token might be valid if it is submitted by other application which the user has access to
Identity Server absolutely handles authorizations on the most basic level. It creates authorization codes and access_tokens that are essential in an applications authorization. Without them you cannot get authorized. Thus for others to claim Identity Server does not do authorizations is flat out wrong.
I came in here a week ago looking for a solution for this very same problem. I want to restrict users to specific applications by not granting them access tokens if they fail to meet certain parameters, in my case a UserClient table. Lucky for you I have a solution. Identity Server 4 implements a few, what they call, CustomValidators that occur at the time of authorization or token creation. They are
internal class DefaultCustomAuthorizeRequestValidator : ICustomAuthorizeRequestValidator
internal class DefaultCustomTokenRequestValidator : ICustomTokenRequestValidator
public class DefaultCustomTokenValidator : ICustomTokenValidator
There name really says it when they get called. Each one contains a single method
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
return Task.CompletedTask;
}
Notice something? That's is right! It does nothing. Almost as if they are meant to be replaced. (It is).
This is the area that you can add your custom logic to reject the request. CustomAuthorizeRequestValidationContext contains ClientId and User claim information. It also contains a boolean value called IsError. Simply set that to true and whamy! Access denied. You can also set error messages etc. Here is an example that implements the ICustomAuthorizeRequestValidator inface that will restrict a user based on there user Id
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
var sub = context.Result.ValidatedRequest.Subject.FindFirst("sub");
if (sub != null && sub.Value != "88421113")
{
context.Result.IsError = true;
context.Result.Error = "Unauthorized";
context.Result.ErrorDescription = "You are not authorized for this client";
}
return Task.CompletedTask;
}
Feel free to inject a dbcontext or two to read off of your userclient table. I check the sub claim to be null because this will get hit several times before actual login occurs.
From what I noticed all three behave similar in terms of use, but different in terms of outcome. Setting an error ICustomAuthorizeRequestValidator will prevent the redirect to your client and instead direct you to the Identity Server error screen. The other two will redirect back to the client and generally throw some throw some sort of HttpResponse error. Therefore replacing the ICustomAuthorizeRequestValidator seems to work best.
So simply created a class that implements ICustomAuthorizeRequestValidator. Then add that into your identity services like so
services.AddIdentityServer().AddCustomAuthorizeRequestValidator<MyCustomValidator>()
and you are done done.
You can add a claim in your IdentityServer4's claims table called "role" and in your application, add some UI to authorize a person via email or similar, and then set his/her role in the claims db. And you can also delete the authorized user from your application, which should un-assign a role to that particular person. Thus he/she although is successfully authenticated, can't use your application because you have authorized then. Hope this approach helps you!
For users, IdentityServer is authentication only. Authorization should be handled by your application.
Authentication = Verifying who a user is
Authorization = Verify what a user can do
Update
I wrote an article on this topic to clarify how OAuth 2.0 does is not user-level authorization. Hope it helps! https://www.scottbrady91.com/OAuth/OAuth-is-Not-User-Authorization
As Scott says, Identity Server will authenticate that the user is who they say they are, not explicitly tell you what that user can do.
You can use the claims returned as part of that authentication to then perform authorization checks within your app. For example, you might use the sub or id claims to perform checks from your app on whether the user associated with that sub/id is allowed to access a specific resource.
The water gets a bit muddier when you bring role claims into the picture, but so long as you appreciate the difference between authentication and authorization you should be ok.
In our enterprise scenario we split it into layers:
We introduced a tenant -- a customer (organization) of our enterprise
solution.
Then we have roles (not more than 20 or so) assigned for
each particular user.
IdentityServer fetches users from tenant and access APIs. The only pre-check it performs is that a particular client (application), requested a token, is not restricted for the particular tenant (customer-level licensing), otherwise we display a message and block the challenge response.
Then we come to an app. With a valid token, having tenant and roles inside. The roles-to-functions assignment could be unique within the tenant. So the application itself performs a granulate permissions check, using a separate API. The application is free to enable-disable some functions or even redirect to the special page in IdSrv "Access denied for the app".
With such approach we are scalable, we are configurable, we are as fast as we want. In previous generation we had "all in one" identity+access+licensing monster-like system, and we decided to split. Today we do not face any real limits with adding new customers (tenants), having 20000 users in average each.
Another way, you can redirect user back to respective client login page it they are not assigned to application/client by using IProfileService of IdentityServer4.Services
public async Task IsActiveAsync(IsActiveContext context)
{
if (!string.Equals("MyAllowedApplicationId", context.Client.ClientId, StringComparison.OrdinalIgnoreCase))
{
context.IsActive = false;
}
}
You have to set IsActive = false to redirect user back to login page where user can login with user details which is allowed in application

Authorization in GraphQL servers

How to handle Authorization in GraphQL servers?
Shall I pass the JWT token in the Authentication header of every requests and check for the authorized user after resolve() and check for the role of user on every query and mutation
Introduction
First of all, a common approach for authentication as you state is using a signed JWT that contains the id of the user making the request.
Now let's have a look at the different parameters we can use when considering the authorization of a given request.
who is making the request?
determined by the user id mentioned above. More information about the requester like associated user roles can be looked up in the database. This means that we need to maintain a User table if we are using SQL for example, and add new users to this table on registration.
which operation should be executed?
users might be granted read-only access. Certain mutations or queries are only allowed for certain users.
which fields are included in the query/mutation response?
some fields should be only accessed by certain users.
Permissions
With this information in mind, we can come up with different permission systems. Most commonly, in such a system, no operation is allowed by default. When a request comes in, the parameters mentioned above can be matched with the existing permissions and if a matching permission is found, the request is granted.
Role-based permissions
In certain applications, a role-based approach works great.
For example, for a simpler version of Stack Overflow, we could have the roles EVERYONE, AUTHENTICATED and MODERATOR. A sensible permission rule could be this:
EVERYONE can read questions/answers
requester: doesn't matter (everyone)
operations: allQuestions, allAnswers queries
fields: text
Other rules (leaving parameters out):
* AUTHENTICATED users can create new questions/answers
* MODERATOR users can create new questions/answers
* MODERATOR users can delete questions/answers.
Now for example, if a non-authenticated requests comes in that asks for the allQuestions query, that's fine as we find a permission that allows it (the first).
If on the other hand an authenticated requests comes in for a user that doesn't have the MODERATOR role and includes the deleteQuestion mutation, there is no permission to be found for these parameters. So the request is rejected.
Graph permissions
While role-based permissions represent a solid permission system already, they are not suited at all if we want to make granting permission dependant on things like the relation between the requester and the requested node. In our example, it would be quite the work to add the simple rule that any user is allowed to delete their own questions/answers.
At Graphcool, we have come up with a powerful yet rather simple approach that we call graph permissions to tackle this issue. Let's make the following additional parameters available when checking permissions:
which node is about to be accessed or modified?
determined by a node id
Then we can express permissions using a GraphQL query against a special permission schema to grant or reject permissions on a node level. Access to a given node is only given, if the permission query contains at least one leaf-node that is not null.
In our case, we could specify this permission query:
query {
allAnswers(filter:{
authorId: $userId,
id: $nodeId
}) {
id
}
}
For a given node and user specified by GraphQL variables $userId and $nodeId, we use a query argument filter to either return an empty list if the node wasn't created by the current user, or something non-null otherwise.

How to restrict access some part of module in ZendFramework 2 (i.e. only administrator can do some actions)

so!
I have a question: how to allow access some part of module only for adminisitrator, for example.
For example, I have module album. It has controllers index, delete, add, edit, full. I want full and index controller be available for all roles, but edit, delete and add action only for administrators.
What module I have to use to do that? I found Zend\Authentification.
Table is: username, password, role.
How to authentificate user?:
// do the authentication
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// success: store database row to auth's storage
// system. (Not the password though!)
$data = $authAdapter->getResultRowObject(null, 'password');
$auth->getStorage()->write($data);
$this->_redirect('/');
} else {
// failure: clear database row from session
$this->view->message = 'Login failed.';
}
After that I will get access to user data, for example, by:
Zend_Auth::getInstance()->getIdentity()->username;
So, in action, in which I want to restrict access I just need to use:
if(Zend_Auth::getInstance()->getIdentity()->role == admin) {
redirect("auth/login");
}
Right?
The questions:
Is my suggestion about how to check user role in each contoller correct?
Do I understand correctly how to work with Zend\Authentification and restrict access to some actions? So in future I will just use same for each action, right?
Additional question: Does Aclmodule uses for managing permissions? So Acl is needed to help Zend_Auth with permissions, right?
To be able to do this you have to build or implement an ACL (Access Control List). You can also use a third party solution in combination with the earlier mentioned Zend_Auth (or any other authentication module). You can read more on Zend ACL here: Zend ACL introduction
You could for example also take a look at BjyAuthorize. This ACL module provides a complete authorization solution for your application but depends on ZfcUser for user authentication and registration. It might be a good way to get started.
If you are done building or implementing BjyAuthorize you can easily tie your access permission checking to your routes (but there are many other ways). You can see how this works here on the BjyAuthorize GitHub page
These modules will teach you a lot about how authentication and authorization can be build into your Zend Framework 2 application.