Question

We are currently working on a micro-service based system. The idea behind it is to split our monolithic system to small domains (micro-service per domain).

All services will be used by our internal applications, but we have plans to expose it to public.

One of the services would be identity management service, which would be responsible for issuing and validating tokens.

For shared references, we are using SharedKernel. One of libraries in shared kernel is a custom AuthorizationAttribute, which will be used by all micro-services. Basically what the attribute does, is forwarding Bearer token to authorization service, and authorization service responds with information such as token validity, user information and a set off allowed endpoints/resources for the given token.

If the current endpoint is not listed, 401 HTTP response is returned.

Now, I have 2 concerns:

  1. Are we on the right track and is this a valid approach? If not, what can be done to improve it?
  2. We are still having problems with UserTypes/Roles concept. We will probably have 3 types of users. Admins, Agents and End-Users and yet again, if some 3rd party company decides to use our API, roles which were created for our needs, might not be suitable for their needs. The problem is that not all agents will have set of same permissions. For an example we might have an agent who is responsible for accounting and he would have access only to accounting related micro-services, but we might also have an agent who is responsible only for booking. Should this require us to set a UserType for each agent type, fe. AccountingAgent, BookingAgent etc, and assign multiple UserTypes to user, or there is a simpler approach to solve this?
Was it helpful?

Solution

No, you are not using tokens correctly.

The idea is that you have an Auth service which issues tokens and Resource services which can validate the token and read the claims it contains. So the rather than forwarding the bearer token to the auth service each time the flow should be as follows

  • Client -> Auth : please give me a token, here is my username and pass
  • Auth -> Client : here is a token I have signed it with my private key and it contains the claim "i am an Accounting Agent"

then

  • Client -> Resource : hi, please give me a list of accounts, here is my token
  • Resource -> Client : I have checked the signature on your token using the Auth services public key. so I know I can trust your claim that you are an Accounts Agent. Here is the list of accounts

then

  • Client -> Resource : Please delete this account, here is my token
  • Resource -> Client : sorry Account Agents are not allowed to delete accounts

This flow prevents the Auth service becoming a performance bottleneck and leave the various microservices to decide if Claim X can do Operation Y

Roles

To address your second question about roles and permissions. At the end of the day a service has to have a set of Roles or Permissions that it knows about. eg you have to actually code something like:

If(users.Role != "RoleX")
{
    return "Access Denied!";
}

Rather than have a whole list of different permissions, 'canEditAccounts' ,' canEditCustomers', 'canDeleteAccounts'... etc etc the modern approach is to instead have a shorter list of Roles 'AccountingAgent' which essentially act as a set of permissions.

If a third party consumes your services though they will have to use the roles that the service knows about. This means that they are stuck with the level of granularity that you provide in your Roles.

ie If your Accounting Agent does too little and your Accounting Manager does too much for your third parties needs you are stuck.

One way around this is to have a dynamic mapping between Roles and Permissions. Have your service know about permissions and query what permissions a role has.

You can then allow your third party consumers to create their own roles, selecting the permissions they want each to have

OTHER TIPS

Unless you have something else in place such as TLS to hide the token, realize that if the token is intercepted, anyone could use it to pretend to be that user for the length of it's validity. Personally, I would prefer something like a client certificate that doesn't require passing secrets and eliminate managing sessions from the application but there's nothing wrong with the token approach as long as you understand and account for the risks.

One advantage I see to having a separate service that responds with what the user is authorized for is that it can be replaced as needed. So if a third party wants to do something different, they change the behavior of that service. You can simply say "here's user X" what can he or she do? This completely decouples the authorization structure from the service implementations.

Using a separate service to manage authorizations does create some potential challenges. Mainly it could become a single point of failure and it will receive a request for every call to any service. You should be able to manage this with horizontal scaling throughout it's implementation. You could also incorporate caching to avoid excessive chattiness.

Licensed under: CC-BY-SA with attribution
scroll top