Background
I'm building a application where all users belongs to a Organization. I only want the user to be able to Create/Read/Update/Delete records in the Organization they belong to.
I'm using sails, but I'm looking for Connect/Express-based, or a standalone answer as sails-permissions node module is unmaintained.
Question
How can one implement a authorization that allow CRUD only for the organization the user belongs to?
We are also not relying on sails-permissions. In our app, users can be members of multiple orgs.
We are using auth0 for all authentication activities, i.e. every request must include a jwt that is included in the request header. The jwt includes userId, orgId and role.
Sails policies decode the jwt and attach userId, orgId and role the the req object for all later checks.
Every model has the property orgId - we are using MongoDB.
Every controller, db operation, etc. adds this verified orgId to the query. Actually we have a small pipeline preparing the query: we add the orgId, in update cases we filter out unwanted property updates, etc.
This approach does not require additional db calls for separation of tenants.
Some models have specific access requirements per individual RECORD. Here we store allowedUser properties (one for read, one for update, etc.) on exactly this record and we extend the query once more so that only records are returned or updated or Xyz where the current user is included in the applicable allowedUsers property.
This approach also does not require additional db calls. This leverages MongoDB-specific query features, though.
We currently do not have ACL-like requirements which would be right between the 2 approaches I described above (re access control granularity).
You'll need to intercept each request using middlewares
combine them with a role system by checking if a certain token is present on request headers and in a acl map and finally, if the token is present in the acl map, see which permissions are related with this token.
Related
I want to understand how to deal with authorization in REST API with endpoints like below
GET /resource/:id
DELETE /resource/:id
GET /resource
Assumptions
User Bob is authenticated.
Bob only owns resources with id 1,2,4,5,6 but 3
System has an Access Control and Busines Logic Layers
The business layer doesn't have any awareness of data ownership
Access Control Layer has policies for resources and users and it can check if users have the right to access resources and
reject the request with HTTP403 or pass it to the Business Logic
layer to be processed
Scenario
Bob sends a request to GET /resource/2, the application returns resource details with HTTP 200
Bob sends a request to DELETE /resource/3, and the application returns HTTP 403.
Bob sends a request to list resources GET /resources?page=1&pageSize=10, the application returns resource summaries of 1,2,4,5,6 but 3
Problem
The Access control layer can prevent(403) access to a specific resource by checking the claims of the user for a given resource with defined policies.
But It should not be PREVENTED(403) when accessing search endpoints. It should be FILTERED.
Approach 1
It may assumed that search would include summaries of resources that is not owned by the authenticated user.
Approach 2
Search endpoints may be entirely separated and have the awareness of resource ownership and have the responsibility of resolving ownership and filtering.
Other endpoints stay clean by having only business logic.
Questions
Which alternative approach is better?
Is there any alternative?
Am I mixing concepts of data ownership, access control, and business logic?
I think you are mixing these concepts. Let's start from the very beginning:
If a particular user, in this case Bob, is authenticated and his authentication records have policy which defines access to a particular set of resources ( or defines prevention of access to particular set of resources ) then that status should be PREVENTED. Why?
Bob is PREVENTED from accessing particular resources. FILTERED means filtering data and that's something that you can do even when Bob has access to the data. When Bob receives 200 OK status with records that he wanted, internal functionalities of the API can still filter the data that will be adapted to the policy Bobs authentication records hold.
If in our database we have set of records like this: [1,2,3,4,5,6,7,8,9,10]
And we want to create policies that will prevent some users from access a particular record then we can have policies set up in the way where we describe records that define access a particular user has. On example policy can define a record that holds number 3 in a array ( [3] ) and based on this we can create a logic that would obviously filter out the data that's in the array and return [1,2,4,5,6,7,8,9,10].
However, this model heavily depends on how your data is structured. Questions you might want to ask yourself before designing your policy records:
How are my records structured? Do they have records/tables that I can split my data into? If they do, I can define something like <COLLECTION/TABLE_NAME>:<ACCESS_LEVEL> which would in this case yield numbers:* or numbers:[1,2,4,5,6,7,8,9,10].
Can I save access needed into my records? Common practice is to have needed access definitions saved in the records or related records of the data you save. Something like: access_needed: [ "read", "write" ]
Again, it all comes down to your records and based on that you can structure how to define your policy format.
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
}
}
}
Here is the scenario, I have a service containing many records. My service also has many users, each with the ability to create, read, update and delete records. The ability to perform these operations on each record must be controlled at the record level.
For example, user A can only read and update record 1 but user B can read, update and delete records 1, 2 and 3 and user C can perform all operations on all records.
How if at all, can this be done using Azure AD?
Obviously, using application roles is not sufficient because that gives the user uniform access rights to all records.
The tenant is also not useful because its the same for all users (in this example).
I definitely do not want to define access rights for every record individually, I would like to do something like assigning roles to a user group and then somehow assign records to the group.
Is there a standard way to deal with this type of resource based authorization?
As you correctly mention, role based access or authorization is very generic and using that, a user with specific role gets access (or gets denied access) to all resources. (If your sceanrio permits, you could make it a little better by dividing your resources into a few types and give access for 1 or more types of resources to 1 or more roles).
When trying to control access for each record individually, you will need to implement custom logic for resource based authorization. Typically applications utilize a mix of role-based and resource-based authorization driven by their requirements.
In the end it will boil down to a mapping that you need to maintain between 3 things
Resource (or a collection of resources)
Azure AD object (like role, group, individual user that is being given permission)
Permission that you're giving (understood and enforced by your application e.g. Blogs.Create permission for a Blogs application)
Relevant Documentation available on Microsoft Docs
Role-based and resource-based authorization
This documentation talks about similar concepts and shows a good example which makes use of both role based and resource based. CRUD operations on resources based on roles and then special privileges on specific resource for the owner of that resource (i.e. resource based)
Code Samples
Multi-tenant Survey Application Code
Code base for the documentation link above
Authorization in a web app using Azure AD groups & group claims
This one provides a sample task tracker application where users can share tasks with other users or Azure AD groups. Again you will see a mapping between resources (i.e. tasks in this case) and Azure AD objects (i.e. Users or Groups) is being maintained in sample database.
Assigning Application Roles for groups
Since you mentioned this as part of your question, just letting you know that this is possible from Azure Portal but only if you have Azure AD Premium license (I'm not sure which specific ones support it, but search on Group based assignments feature and you'll find it.)
I understand the usage of claims for things I would commonly refer to as "roles" or "permissions". I know that claims are more general, but from what I have seen in practice, it usually boils down to this: If user has this set of claims they can access certain areas, or perform certain functions.
Imagine a wiki application. You might have a content_contributor claim that would allow a user to add content, a content_admin claim that would allow a user to remove content, and a modify_user claim that would allow the granting of contributor rights to other user.
Taking this example a step farther, I may want to restrict users so that they can only see content created by themselves or their team.
If a user can only see content created by themselves, would we have a claim for each piece of content they created, or would we delegate that authorization to the application?
When you are talking about roles and permissions then you are talking about authorization.
Claims are typically not for authorization. (Identity)Claims are there to model the identity of the user: who is the user? The claims on itself do not tell anything about authorization. A user can have a role claim, but this doesn't tell the application what the user is allowed to do.
Authorization is done by the application, based on who the user is. Think of authorization as a set of rules, like:
18+: allow when user is older than 18 (DateOfBirth).
Use car: allow when user has a drivers license.
Or something like that.
Roles are a bit confusing, as they are often misused for authorization. Please read this article for some background information.
The problem with roles IMO is that these are not universal. I can be a Doctor in one hospital, while I'm a Patient in another. And I can be Admin for one tenant, but a User for another tenant. So they have only meaning within a certain context.
The only reason to include roles as claim is that you won't need to lookup this information as it is already present. But given the previous remark, you actually can't include this information. And it will only give you headaches when you do. Because you can't do simple things like update or change permissions or profile settings, until the user logs in again.
So as a rule of thumb: keep authorization close to the resource (api / website). Because that is the place where the business rules are implemented. And that's the place where you can store and update permissions, etc.
Keep a seperation of concerns when it comes to authentication and authorization. Authentication tells you who the user is, and authorization tells you what the user is allowed to do. Don't mix these two.
Translating this to your wiki application:
Create a seperate context where you store authorization information like roles and permissions. You can manage this in a central resource (for multiple applications) or use the context in your application. I would not mix this context with the business context.
Add a user in the authorization context and add a role content_contributor. Within the application read the permissions (from the central API, the local authorization context, a settings file, or anything that suits best) for that user (based on the sub claim). Cache it to speed up performance, and apply the rules to determine whether the user is allowed to access the resource.
You can extend this with resource-based authorization. Save the value of the sub claim in the content record to identify the owner. When the current user matches the sub claim value, then the current user is the owner.
You can use the same approach for teams. Add a teams table to the business context and link the user to one or more teams. Directly using the sub claim value or indirectly, using a Users table, also in the business context, where the user is linked to the sub claim value. You can add name, etc. in case you want to show this information (like in a report).
You can save team id and / or user id or sub claim value (owner is member of the same team as current user) in the content record in order to determine the allowed access for the user.
My setup would be like this:
Identity context: users + userclaims. For authentication only. Application independent.
Authorization context: users (id = sub claim) + per application: roles, permissions, etc. In seperate 'local' databases or in a central database. For authorization only.
Business context: users (Id, Name, 'foreign key' sub claim, without the actual database relation as the table is outside the context) + teams, profile, settings, etc. Linked to the sub claim value when users table is omitted.
In order to keep the users table in the business context up-to-date, periodically refresh the values. You can for instance update values when the user logs in after x time. Or once in a while query the Identity Context (using the API) to request user information (using the identities User Info endpoint).
In all contexts there can be a users table, but they all have a different meaning and contain other information. So there is no redundant information.
Authorization takes place inside the application and is based on the business rules (policies) and authorization information from the authorization context.
As a final remark, when the current system requires role claims (like for User.IsInRole() or [Authorize("role")]) then you can read (from cache) the role / permissions on each call and add these to the claims collection of the current user (claims transformation).
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.