3

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?
  • 1
    Robert I voted down by mistake, sorry about that. I asked moderator to undelete and retracted my vote. Please feel free to re-delete if I misunderstood and the reason was not that unfortunate downvote – gnat Aug 18 '16 at 12:57
  • @gnat I was not sure is this a place to ask or StackOverflow, so i thought I missed :) – Robert Aug 18 '16 at 12:58
6

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

  • 1
    "No, you are not using tokens correctly." I think this is a little too strong. While there are clearly benefits to using the tokens to eliminate centralization around security, there are also drawbacks. Namely, revocation is more difficult and authorization is less flexible. "At the end of the day a service has to have a set of Roles or Permissions that it knows about." This is isn't true. The centralized approach allows the service to simply ask if user X can do action Y. It need not know what role the user has or whether there are roles at all. – JimmyJames Aug 18 '16 at 16:53
  • 1
    well, the token should have an expiry. so the drawback, if it is one, is that the token will be valid up to expiry mins after logout rather than race condition milliseconds after logout – Ewan Aug 19 '16 at 13:14
  • 1
    yeah but you reverse it for signing – Ewan Aug 19 '16 at 18:31
  • 1
    @Robert If the signature were made with the public key, 1. anyone could do it and 2. only the holder of the private key could check it but 3. it would be pointless because of 1. The point is that only one party can sign with the private key and anyone can verify that they signed it using the public key. – JimmyJames Aug 19 '16 at 18:42
  • 1
    ..do not use sessions. Your services should be stateless. This is much worse than a central auth – Ewan Aug 20 '16 at 7:04
1

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.

  • Yes, response from authorization server provides basic user info such as ID and list of endpoints which are valid for this user. for an example {endpoint: "api/accounting/bills" , scope: "CRU"}, which suggests that user has access to "api/accounting/bills" endpoint and can use GET, POST and PUT verbs, but not delete, since user lacks "D" (lol) from the CRUD scope. – Robert Aug 18 '16 at 13:54
  • @Robert It's not really related to your question but I think it's worth pointing out that the CRUD operations don't align completely with the HTTP operations. For example, the PUT operation is generally able to create data as well as update it. – JimmyJames Aug 18 '16 at 21:18
  • I am aware of that, but if we go this approach, all our microservices will honor this principle. It's something we picked up by studying zendesk's api. We have to deal with scopes somehow. – Robert Aug 19 '16 at 9:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.