In our environment (as in many others), it is often the case that one microservice must call another in order to accomplish a task.
In our environment, authentication is clear enough - we have a signed JWT containing a list of permissions and roles, as well as a user ID, client ID, and so on.
What we're less clear on is authorization - ensuring that the authenticated client can (or can't) do the right stuff, but that the underlying services have all the access they need to do their jobs (even if the client wouldn't be able to do the same things directly).
We've examined a few different options:
- Each service does all of its own authorization, and if a privilege escalation is needed, it generates a "God mode" token with an otherwise unchanged payload and a different keypair and makes the call using that. The main concern here is copy/pasted authorization code, and the fact that there'll be a strong incentive to just always enable God mode when making cross-service calls (which makes it somewhat redundant).
- Each service does all of its own authorization, and just forwards the user's token if it needs to make a call. The concern here is code duplication like in option 1, and also the fact that this is likely to cause a complex interdependent web of permissions that imply other permissions that imply other permissions that... (ad nauseam), creating a maintenance headache as the number of services grows.
- A lightweight API gateway service that does "simple" authorization (nothing more advanced than "is this client allowed to use this endpoint at all?"), attaches a user object to the payload, and leaves more specific behaviours to the underlying services, which accept any call as being authorized out of the gate. The major concern with this option is performance and stability - the API gateway service creates a single point of failure that can make the entire system inaccessible if it malfunctions, plus creating a frequently-changing dependency for every service.
The question here is twofold:
- Are there any additional pitfalls to the three patterns described above that we haven't considered?
- Which of these is the most common in the wild?
Note that this question is not about service mesh offerings like Istio, as we consider them to be somewhat orthogonal to this issue.