5
votes

I'm developing an app with microservices and I don't know how to distribute microservices to allow auth.

I've read that each microservice should have its own database to avoid coupling.

The problem is that Authentication (via JWT) and Users Microservices must have access to the same database and table (Users). I suppose this problem has been solved before due to similar applications having to deal with the same issue.

How can I solve this?

3

3 Answers

14
votes

JWS (the signed version of a JWT) is a perfect example as it was thought for similar scnearios:

  • you have an authentication app: every login goes through that (signin.domain.com), and once you verify the credentials of a user you issue the token, generated through private keys
  • each service (service1.domain.com, service2.domain.com) can implement a middleware that instead does authorization: all your services will receive the public key and will be able to verify the authenticity of the token through that key. They don't need a DB since what they need to verify is that the token is valid, not that the user exists etc etc.

To clarify my last statement: you should probably issue very short-lived tokens. At that point, say that:

  • user X logs in
  • his token will be valid for ten minutes
  • user X deletes his account but still has a valid token
  • he then hits service.domain.com

On service.domain.com you will still consider him logged in until you, for example, need to interact with an API that actually hits the DB (ie. add a new user address). At that point the service that is responsible for writing into the DB will throw an exception saying the user doesnt exist and you can probably trap it and log the user out. All of this can be tweaked / fine-tuned but you get a rough idea of how it could work.

Getting back to JWTs and their usage, I don't know if you are familiar with PHP but this is a pretty straightforward example.

If you want to get fancy you could use nginx as a middleware and have something like the auth module doing authorization for you.

Last but not least, we've only covered authentication here: to do authorization you will probably either want to, in each service, either read the user's roles from the token (assuming you saved them there once the user logs in -- but this is a bit flawed as if a user loses a role then his token would still list it) or simply call signin.domain.com/users/me from each service to retrieve an up-to-date list of user roles, and then check that he's allowed to perform certain operations on that specific service.

Oh, and remember that you should never put sensitive data in a JWT / JWS as they can be decoded. So yes, you can add user roles to a JWT but, for example, never save passwords or other plaintext tokens there.

Hope this helps!

1
votes

We solve this by using composition of services to protect services that need authentication. A lot like the way you would approach it if it was a monolith.

As @odino mentioned:

  • Use an Auth service to authenticate the user and generate a token (we use an OAuth flow for this)
  • Subsequent services would then use the token to verify if the user exists by passing it through the Auth service ("composition")

Here's an example with StdLib (what we use in-house):

const lib = require('lib');

module.exports = function(params, callback) {
  lib.user.isAuthenticated(params, (err, user) => {
    if (err) return callback(err);

    // We're authenticated – do the rest of the work.
  });
}

The caveat to this is that if you're using HTTP as the protocol to communicate between your services, you're essentially adding a 200-300ms overhead to your authentication. You can solve for this by loading the services that need authentication within the same container (StdLib does this out of the box iirc)

0
votes

We are using a slightly different strategy. We have skipped the signing or encryption of JWT which makes our JWT just a Base64 encoded string of our token object and a JWT once created is valid for all our microservices. We have made sure that our micro-services are not accessible directly but only through an Api Gateway. API gateway does the authentication(Http Basic), generates the token, caches the token and validate it for every requests and then pass it as a header while delegating to the micro-service. This micro-service can access another service by just passing the JWT it received from Api Gateway and we are using JWT as just a mechanism to know who the logged in user is.

Each micro-service upon receiving the request checks if JWT is present in header. If present, fetch privileges that are available for the user specified in the token and use them for authorization. Authorization(privileges) make sure that right person is trying to access the resource.