Authentication and authorization are always good topics
I will try to explain to you how we deal with authorizations in the current multi-tenant service that I am working. The authentication and authorization are token based, using the JSON Web Token open standard. The service exposes a REST API that any kind of client (web, mobile, and desktop applications) can access. When a user is successfully authenticated the service provides an access token that must be sent on each request to the server.
So let me introduce some concepts we use based on how we perceive and treat data on the server application.
Resource: It is any unit or group of data that a client can access through the service. To all the resources that we want to be controlled we assign a single name. For instance, having the next endpoint rules we can name them as follow:
product
/products
/products/:id
payment
/payments/
/payments/:id
order
/orders
/orders/:id
/orders/:id/products
/orders/:id/products/:id
So let's say that so far we have three resources in our service; product
, payment
and order
.
Action: It is an operation that can be performed on a resource, like, read, create, update, delete, etc. It is not necessary to be just the classic CRUD operations, you can have an action named follow
, for instance, if you want to expose a service that propagates some kind of information using WebSockets.
Ability: The ability to perform an action
on a resource
. For instance; read products, create products, etc. It is basically just a resource/action pair. But you can add a name and description to it too.
Role: A set of abilities that a user can own. For example, a role Cashier
could have the abilities "read payment", "create payment" or a role Seller
can have the abilities "read product", "read order", "update order", "delete order".
Finally, a user can have various roles assigned to him.
Explanation
As I said before, we use JSON Web Token and the abilities that a user possesses are declared in the payload of the token. So, suppose we have a user with the roles of cashier and seller at the same time, for a small retail store. The payload will look like this:
{
"scopes": {
"payment": ["read", "create"],
"order": ["read", "create", "update", "delete"]
}
}
As you can see in the scopes
claim we don't specify the name of the roles (cashier, seller), instead, just the resources and the actions that are implicated are specified. When a client sends a request to an endpoint, the service should check if the access token contains the resource and action required. For example, a GET
request to the endpoint /payments/88
will be successful, but a DELETE
request to the same endpoint must fail.
How to group and name the resources and how to define and name the actions and abilities will be a decision made by the developers.
What are the roles and what abilities will have those roles, are decisions made by the customers.
Of course, you must add extra properties to the payload in order to identify the user and the customer (tenant) that issued the token.
{
"scopes": {
...
},
"tenant": "acme",
"user":"coyote"
}
With this method, you can fine-tune the access of any user account to your service. And the most important, you don't have to create various predefined and static roles, like Admin, Agents, and End-Users as you point out in your question. A Super User will be a user that owns a role
with all the resources
and actions
of the service assigned to it.
Now, What if there are 100 resources and we want a role that gives access to all or almost all of them?. Our token payload would be huge. That is solved by nesting the resources and just adding the parent resource in the access token scope.
Authorization is a complicated topic that must be addressed depending on the needs of each application.
Best Answer
This has been sitting for a while, and I figured to answer my own question since we've solved this problem by now...
We decided to convince those clients that were using the homegrown SOAP services to utilize SAML, and we partnered with Ping Identity to help us out with the SAML component as we weren't experts.
As for our design, we were able to keep it relatively simple: Some SAML clients were also in need of a username/password authentication type for certain users, so we designed both a connection for SAML as well as for username and password.
We also decided to go with an IdP only approach, which so far, hasn't been an issue.