How do you handle validation in a composite microservices request?

Consider an application with two objects:

  • User

    (contains basic user data such as name)
  • Passport

    (contains credentials i.e. password)

And two internal microservices:

  • UserService

    (responsible for creating and managing users and their master data)
  • AuthService

    (responsible for user authentication and password handling)

The object User

belongs to the object UserService

and Passport

belongs AuthService

.

These two services should be kept separate as they perform very different tasks: profile data and authentication.

Also, consider that we have a registration form with three fields:

  • Email
  • Name
  • Password

This form will initiate an HTTP request in GatewayService

, which intercepts all requests to the application and routes them to internal microservices (or assembles / aggregates them).

Now that the gateway service receives a request with all the form data, it needs to do the following:

  • A call UserService

    to create a new user (it will respond with a generated one userId

    ).
  • Call AuthService

    to create a passport for the newly created user. It will need the userId

    one obtained in step # 1 and the field password

    from the original request.

It looks pretty straightforward, but what happens if it's AuthService

not available in step 2? We need to separate these requests somehow!

The classic approach is to use finite consistency and create an object Passport

through an asynchronous call (we can queue this request and process it in a separate service). To do this, we will send an asynchronous request to AuthService

passing through userId

and password

instead of step # 2, so step # 1 will immediately return a response to the client.

However, what if the field is password

not formatted correctly (violating validation rules)? The validation logic is only present in AuthService

, so we cannot know if the password is correct until the call is made. And now the request is processed asynchronously, so we cannot go back to the user and ask him to correct the password.

SO, how do you properly handle validation in distributed compound queries for a microservice application?

  • The naive solution is to move the validation logic to itself GatewayService

    , but that is a terrible idea because that will make it thick and leak the business logic out of AuthService

    .

  • Another idea is to provide an additional password verification method and call it before steps # 1 and # 2. This seems like a viable solution, but it will force us to have two methods for each business method in our microservices, one for preliminary validation and one for actual work. In addition, there is a time gap between validation and the operation, so the earlier correct value may become incorrect when the operation is performed.

  • We could split the form into two parts to avoid complicated requests and prompt the user for a password after requesting personal information and creating an account for him. However, this can lead to security issues where the user account could be intercepted by another party who might guess the next one userId

    . We could use some additional security token, but it will introduce odd functionality to the services and make the complex setup more difficult.

    Also, this approach is like trying to avoid the problem, you can't always avoid compound queries.

  • We could use full blown distributed transactions, for example. 2PC , but it would make the system extremely complex and mitigate the use of MSA in the first place.

  • And the last idea is to bundle these two services together, but for a microservice architecture that doesn't make sense.

+3


source to share


3 answers


Here is my thought

1. User service - should be responsible for

User creation which includes username, password (hashing), email and any other profile data

validation of input data for validation rules

verification of a user using his password

the pros

further use of profile data is easy



search and validate a user in a single query

user login in one place

2. The authentication service should only be responsible for generating tokens based on successful user authentication through the user service

this token should be used for further processing by all other services in the ecosystem and will provide proper authorization

the pros

the future addition of services that require user authentication and authorization can work independently and only require a security token.

Generating a token based on a previous token can be straightforward, the user will not need to enter a username and password every time their token expires.

+1


source


As for # 5, I really don't see you committing a terrible violation of principles in some way by bundling into one service here (point 5), a kind of "UserManagementService" that does both; even if it can be argued that one MC should do one task and do it well, the two are closely related, IMHO.

Another option would be to make the UserService a sync client of AuthService (load balancing, with a circuit breaker, regardless of whether a single instance is available), which would be basically the same as nesting a password validation dependency in "normal" code.



If this is a new thing, and you are not stuck with these two services, in general, I would go for a simpler first implementation and resist the impulse to optimize as they tend to be premature or unreasonable commitments to purity of principles / dogmas. You yourself know about the complexities that you can enter (2PC), plus you have your requirement there ("so we cannot go back to the user and tell him to correct the password").

0


source


I agree with # 5. These two services will have a lot of dependencies, as access to the UserService will always be AuthService and they will likely access related data. If you separate them, you may need to separate their common logic, like creating a passport. It can be in a service that has an operation that gets the user ID either from AuthService (after checking credentials) or from UserService (after registration).

Also, handling the absence of AuthService will raise questions about other scenarios, for example if the user is already registered and cannot log in, how do you handle it? .. If you show an error, why not register ?. just a question for more thoughts on your requirements.

Greetings

0


source







All Articles