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:
- 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 oneuserId
). - Call
AuthService
to create a passport for the newly created user. It will need theuserId
one obtained in step # 1 and the fieldpassword
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 ofAuthService
. -
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.
source to share
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.
source to share
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").
source to share
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
source to share