5

I'm having a problem choosing the right approach to sharing some user related data throughout my microservices-based application.

Imagine the following scenario:

Users microservice handles creation of users, but also management of the hierarchy of those users. It has the information regarding which users have Manager role and which User role, but also which Users are subordinates of which Managers.

There's also a Books microservice which allows for creation and management of Book and related entities. Users can create and manage their own Book, however, their managers should be able to update their Book, too. The authorization of Update endpoint of BooksController should check if the User trying to do the update is the owner, or in the case when he's not, does he have a Manager role and also if the owner of the Book in question is his direct subordinate. This information is only available in the Users microservice.

I'm considering following solutions to this problem:

  1. A request/response pattern implementation to get the subordinates of the Manager in question - Feels like a very bad option for this, as it's essentially creating a tight coupling between the services and also creating a single point of failure for both services.

  2. Sharing the Users database with Books microservice (read-only) - another case of tight coupling, however, with no dependency of the Users service to be alive to retrieve the information.

  3. Merging the microservices together - Maybe the line was drawn in a wrong place and those two microservices should become one. However, looking at the Users service, it feels like this sort of scenario can reappear for other microservices that will be introduced to the application. Solving it this way sets a precedence for just merging it all back together into a monolithic application.

  4. Adding the data regarding users hierarchy to the access token as a custom claim and using that data to do Authorize in the Books service - I think it could work. My worry is that I'd be misusing custom claims for passing data that isn't really 'part' of the user.

  5. Other?

3 Answers 3

3

I'd go for option 5: Event based microservice architecture.

When a user is created, the user service emits a UserCreated event. If the created user is a manager, the event contains a list of UserIds of subordinates.

The Book service is subscribed to this event and stores the Id, list of subordinate UserIds and perhaps name or other details it's interested in, in its own data store.

This seperation leads to a loosely coupled yet robust system without the need for querying other services.

3
  • I'm not familiar with event based architectures, wouldn't that lead to repeated data and repeated business logic regarding manager/subordinate roles? The Book service would need to follow and interpret all events emitted by the User service which creates pretty strong coupling in case you implement new transactions within the User service which emit new types of events. And every other dependent of the User service would need to duplicate this code and data, too. Jul 13, 2020 at 17:28
  • 1
    Repeated data: yes. Repeated business logic: no. The user service contains the business logic about managing users, the book service contains the business logic about books. Other services, for example a service that sends happy birthday mails to users, is also subscribed to the events, but only stores users names, emails and birth dates. No other system has to know this birthday system even exists. And yes, the book service is coupled to the user service, but loosely via the message bus. Replacing the user service is possible without changing the book service code.
    – Rik D
    Jul 13, 2020 at 17:38
  • 1
    This could be the answer. Personally, I think data replication is not a bad thing to do in microservice-based applications if done carefully. I'll throw this as another option in the next discussion with my team and see what happens.
    – Slowacki
    Jul 14, 2020 at 16:35
1

The authorization logic you describe is a business rule and one constant about business rules is that they are always subject to change. One such change could be that managers must be able to assign a "stand-in" who gets the authority to edit Books of the manager's subordinates during a particular time frame or until it is revoked.

You don't want to have that kind of stuff in a claim in a token, because it would mean issuing a new token every time someone becomes a "stand-in" or stops being one. For these kinds of authorization business rules, I see only two plausible solutions

  1. The User service has knowledge of all authorization rules for each possible claim and the Book service asks the User service if the current request is authorized.

  2. The Book service knows the authorization rules and asks the User service for the required information.

In either case, the User service becomes a central player in the authorization checks, so measures should be taken to ensure the User service has a higher availability than each of the other services, for example by running multiple redundant instances of it that synchronize their databases.

1
  • Providing that the manager of a group of users would only change infrequently, wouldn't it be ok to invalidate the access token then? Regarding 1. it could be just something specific to me, but I thought that keeping authorization of one service outside of it is rather bad practice? As for 2., this basically means using the option 1. from my question, right? And what do you think about data replication like mentioned by Rik D here: softwareengineering.stackexchange.com/a/412676/229203
    – Slowacki
    Jul 14, 2020 at 16:27
0

I would not say I am an expert in micro-services, so here are some thoughts:

  1. It seems like you have identified a shared dependency (i.e. interrogating identities) between the 'manage users' feature and other internal services.
  • Is there enough business logic around creating vs interrogating identities, that these services need to be split?
  • For example Manage Users service has business logic around what and when users can be created, by whom, perhaps having a DB that is an audit trail. While the other Current User service is the most up to date view of users but has no edit functionality. This may also be a better segregation of interfaces.
  1. Would it make sense to validate the 'rights' of the requester at the first gateway/API level so that the 'not authorised' response is generated before the request hits the books micro-service?
  • This would avoid having to add rights-checking to all of your services.
  • Would you be happy with the Books service assuming that any request that arrived had already been checked for authority?

Compromise - if you are having 'rights checking' in various service, create a service to meet this need, which may itself depend on the Users service. That way, if you decide to unpack these responsibilities later, you have captured the common dependency in a single service.

2
  • Regarding justifying the split, I'm not sure. However, I've worked with a larger microservice based solution before and similar problems arose regarding handling of some users data that was required in other services. In the end most of the things that was needed, we packed into custom claims. It worked just fine, but I'm not sure if it's the best solution.
    – Slowacki
    Jul 13, 2020 at 11:23
  • As for 2., I'm a bit opposed to authorizing at the higher level. It goes against what I'm usually trying to follow: 'Authenticate globally, authorize locally', as described for example here: blog.andyet.com/2015/05/12/micro-services-user-info-and-auth
    – Slowacki
    Jul 13, 2020 at 11:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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