Microservices Authentication & Authorization Best Practice
Services require authentication and authorization. In a microservices world you typically have a front-end gateway that manages connections from the outside world which then connect you to back-end microservices to handle the request. How and where you do these two crucial steps depends greatly on your environment and how well you protect the different services. Here are my thoughts on specifically the four approaches outlined in this Gist.
Approach 1: Global Authentication and Authorization
If your back-end (micro) services are to have total faith that the front-end (global) services have authenticated and authorized the transaction, then you can go for option 1. The problem with this is that when you push authorization into the global context, you are moving conceptual business logic from your back-end microservices to the front-end — and that is often the wrong move to make.
Approach 2: Global Authentication, Service Authorization
This approach is likely the best fit for most people. Keep the clunky authentication layer at your front-end global services layer. Then, when the front-end calls the back-end microservices to do an actual job, it can provide a security context. This allows the microservices to not care how someone is authenticated, but is still able to maintain the business logic decision making of what actions this security context is allowed to perform.
Let us take an analogous model of logging into your desktop computer — Windows, Mac, Linux does not matter. You enter your credentials and a login process authenticates you and then creates a desktop process assigned to your user context. All applications launched from that point on are associated with your user context and all file system, network, etc. access is authorized using that single user context. Your applications and the kernel do not care how you obtained that security context, but they will sure as heck keep you within the boundaries of what you are allowed to access.
This is essentially what I meant by saying “I do not believe user-level authorization stops at the gateway.” The authentication can stop, but as calls are made into the back-end microservices, a security or user context should come along with any calls such that the microservices can enact their own business logic of what the caller is allowed to do.
Note: This requires that you have strong authentication and authorization between your services (by using something like Istio and its mTLS). You must be certain that when you get a request to do something in your microservice with a given security context that the security context can be trusted. If any service within your microservices mesh is able to call you and provide a faked security context… That is just a recipe for disaster.
Approach 3: Service Authentication, Global Authorization
Totally bad idea to authorize globally and authenticate in the back-end. It is just wrong — you don’t authorize before you authenticate.
Approach 4: Service Authentication and Authorization
You can do it, but it is really a bad violation of good layered architectures to not do the authentication globally. If you have a small set of services, this is probably not big problem — but once you grow past a certain size you really want your services to deal with only what they need to deal with.
Note: If you do not have strong authentication and authorization between your services through something like Istio and its mTLS, then this really is your only option. Yes, this is repetitious of the Note on Approach 2, but it is important enough to be said again in reverse.
Closing
In the end you should only be choosing between options 2 and 4. Your authorization should always be managed by your microservice or you risk messy architecture by having business logic in your front-end globaly gateway. Then the path of choice between 2 and 4 come down to whether or not your back-end microservices can be confident that only trusted services can make requests of it.
A simple way of ensuring that you can use approach #2 (the best in my opinion) is to use Kubernetes as your orchestration engine and Istio as your service mesh to provide mTLS between services with proper Istio routing configurations to restrict service-to-service connections between trusted services.
Thanks for the question — I enjoy the mental thought exercise going through these scenarios!