2

OWASP states:

Non-public REST services must perform access control at each API endpoint. Web services in monolithic applications implement this by means of user authentication, authorisation logic and session management. This has several drawbacks for modern architectures which compose multiple microservices following the RESTful style.

  • in order to minimize latency and reduce coupling between services, the access control decision should be taken locally by REST endpoints
  • user authentication should be centralised in a Identity Provider (IdP), which issues access tokens

Does this mean that we should have an IdP integration in every microservice we build?

It seems really confusing and not feasible at all in a microservice architecture. I imagine things get easier with something like a service mesh, but we're still not at that level of maturity.

Mike Ounsworth
  • 57,707
  • 21
  • 150
  • 207

2 Answers2

3

Background

Lets start from the top here. Your server receives a request:

Hi server,

Can you do X for me?

- Jane

Access Control is when the server checks to see that Jane is actually allowed to do X before processing the request. Generally speaking, all apps will have some endpoints that need an access control check -- maybe Jane is not allowed to delete records from the DB, or maybe Jane is not allowed to change settings of Bob's account, etc.

OWASP's point is that in an old-school app, when the user logs in, you might build a session info object telling you who the user is and what permissions they have, and keep that in server memory until the user logs out. Obviously that does not scale with modern cloud architectures.

JWT To The Rescue!

The key part of that OWASP quote is (emphasis mine)

user authentication should be centralised in a Identity Provider (IdP), which issues access tokens

Take anything that your microservices need to know about the user (ie anything that you would traditionally have put in the sessionInfo object) and stuff it in a JWT, hand it to the user, and have them hand it back with each REST call. Omitting the crypto parts of a JWT, the claims you care about might be:

{
  "sub": "48",
  "name": "Jane",
  "admin": true,
  "exp": 1621004706
}

Now, by unpacking the JWT from the request, microservices have everything they need to make an access control decision; no network calls needed! Trying to delete data? Do they have Admin: true? Trying to change Bob's settings? Are they Bob?

To make this all secure, we rely on the fact that JWTs are cryptographically signed (and sometimes encrypted, but that is optional) by the IdP. So to make this whole thing work, the only piece of info the microservice needs is the cryptographic key to verify the signature on the JWT. This is generally static, so you can stick it in config data or whatever. If the IdP is external (say you've integrated with Google or GithHub's SSO service) then the JWT verification key will be an RSA public key. If your IdP is an internal component of your app, then you can simplify a bit and use a symmetric HMAC key where the IdP and the microservices all have the same "JWT secret" in their config.

Service-to-service

Question from comments: is this model applicable for a service-to-service API?

Sure, why not?

Your client, even an automated one, will typically have some sort of long-term credential, typically an API key or a TLS client cert. You still want to make the client authenticate against a central IdP for a number of reasons:

  1. The long-term credential usually does not carry all the meta-data. So something in your backend needs to look up the associated meta-data.
  2. What if that access changes? Say you're auditing your config and realize you gave that component wider DB access than it really needs, or heaven-forbid someone accidentally posts that client secret key in a public git repo and you need to revoke it.

Doing the full access control check against the DB on every HTTP request is really expensive, but issuing a JWT with an expiry timestamp and forcing the client to re-do the /auth/login every 15 mins / 1 hr / 24 hrs is usually a reasonable security-performance tradeoff.

In some simple cases you may not have any meta-data and you just want to check that a request is coming from another authorized component; basically a "Are you me?" check. In these cases I've seen each component get a copy of the same symmetric JWT and just create themself a token before each request.

Mike Ounsworth
  • 57,707
  • 21
  • 150
  • 207
  • That makes sense, but in a micro services environment you are gonna have many services interacting with each other and making background tasks for example. In that case the agent is not a user, but a system. Does it make sense to use JWT in those cases as well? – Eduardo Bueno May 14 '21 at 15:45
  • 1
    @EduardoBueno I thought about this a bit more, deleted my previous comment and added another section to my answer. – Mike Ounsworth May 14 '21 at 17:33
1

user authentication should be centralised in a Identity Provider (IdP), which issues access tokens

This means that when a user wishes to use a client application, they are required to identify themselves to the identity provider, who then provides the client with an access token that certifies that the bearer of that token is authorized to act on behalf of that user.

If the user instructs the client to make a request to a microservice, the client attaches the access token to the request, usually using the "Authorization" header.

Upon verifying that the access token is genuine, the microservice can then read the token to determine that identity of the calling user, as well as any other pertinent metadata the identity provider thought to include, which can then be used for access control decisions.

In order to minimize latency and reduce coupling between services, the access control decision should be taken locally by REST endpoints.

This means that microservices should verify the token without calling back to the IdP, by checking its cryptographic signature. It also implies the IdP must include any information it may have that is needed for access control in the access token itself (rather than calling back to the IdP for that).

Does this mean that we should have an IdP integration in every microservice we build?

Depends what you mean by "integration". The microservice must be able to verify and interpret the access token. If you follow the OWASP recommendation, that does not require the microservice to interact with the identity provider.

It seems really confusing and not feasible at all in a microservice architecture.

Not at all. You just need a library to verify and parse JWT tokens, and tell it to trust the identity provider's public key. Most server side security frameworks support this out of the box.

And, if a microservice calls another, it should pass on the access token. That doesn't pose any particular difficulty either.

As a microservice developer, I actually find this easier then having to deal with identifying users, because I don't have to know how they identify themselves to the identity provider. I don't need to store passwords securely and implement a secure password recovery, let alone fancy new multi factor authentication schemes. And should the business ever need to switch authentication methods (for instance because an executive order is handed down), it can accomplish this without touching all its microservices.

meriton
  • 1,449
  • 1
  • 10
  • 13