Authentication and Authorization in Microservice Architecture
Securing microservices can be hard due to their separation of concerns. Read on for advice on the subject from an expert dev.
Join the DZone community and get the full member experience.Join For Free
This post was triggered by this Reddit post. Mostly because I got people looking strangely at me when I shouted "DO NOT DO THAT!" when I read the post.
We’ll use the usual Users and Orders example, because that is simple to work with. We have the usual concerns about users in our application:
- Password reset
- Two factor auth
- Unusual activity detection
- Etc, etc, etc.
- Can the user perform this particular operation?
- Can the user perform this action on this item?
- Can the user perform this action on this item on behalf of this user?
Authentication itself is a fairly simple process. Don’t build that, go and use a builtin solution, but the good side of it is that there is rarely any business specific stuff around it. You need to authenticate a user, and that is one of those things that is generally such a common concern that you can take an off the shelve solution and go with that.
Authorization is a lot more interesting. Note that we have three separate ways to ask the same question. It might be better to give concrete examples about what I mean for each one of them.
Can the user create a new order? Can they check the recent product updates, etc? Note that, in this case, we aren’t operating on a particular entity, but performing global actions.
Can the user view this order? Can they change the shipping address? Note that, in this case, we have both authorization rules (you should be able to view your own orders) and business rules (you can change the shipping address on your order if the order didn’t ship and the shipping cost is the same).
Can the help desk guy check the status of an order for a particular customer? In this case, we have a user that is explicitly doing an action on behalf on another user. We might allow it (or not), but we almost always want to make a special note of this.
The interesting thing about this kind of system is that there are very different semantics for each of those operations. One off the primary goals for a microservice architecture is the separation of concerns, I don’t want to keep pinging the authorization service on each operation. That is important. And not just for the architectural purity of the system, one of the most common reasons for performance issues in systems is the cost of authorization checks. If you make that go over the network, that is going to kill your system.
Therefore, we need to consider how to enable proper encapsulation of concerns. An easy to do that is to have the client hold that state. In other words, as part off the authentication process, the client is going to get a token, which it can use for the next call(s). That token contains the list of allowed operations/enough state to compute the authorization status for the actual operations. Naturally, that state is not something that the client can modify, and is protected with cryptography. A good example of that would be JWT. The authorization service generates a token with a key that is trusted by the other services. You can verify most authorization actions without leaving your service boundary.
This is easy for operations such as creating a new order, but how do you handle authorization on a specific entity? You aren’t going to be able to encode all the allowed entities in the token, at least not in most reasonable systems. Instead, you combine the allowed operations and the semantics of the operation itself. In other words, when loading an order, you check whatever the user has as the “orders/view/self” operation and that the order is for the same user id.
A more complex process is required when you have operations on behalf of others. You don’t want the help desk people to start sniffing into what <Insert Famous Person's Name Here> ordered last night, for example. Instead of complicating the entire system with “on behalf of” operations, a much better system is to go back to the authorization service. You can ask that service to generate you a special “on behalf of” token, with the user id of the required user. This creates an audit trail of such actions and allows the authorization service to decide if a particular user should have the authority to act on behalf of a particular user.
Published at DZone with permission of Oren Eini, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Never Use Credentials in a CI/CD Pipeline Again
How To Integrate Microsoft Team With Cypress Cloud
Replacing Apache Hive, Elasticsearch, and PostgreSQL With Apache Doris
Transactional Outbox Patterns Step by Step With Spring and Kotlin