Last week there was a great post on the NGINX blog discussing how Netflix moved to microservices to decouple their overall architecture. For Netflix, the outcome of decoupling meant wins in terms of deployments, team size, architectural flexibility, and testability. It's a great read, and it shows the overall value of properly developing a lightweight, loosely coupled architecture. Everything offered in the article is sage advice, but like most things in technology, it's much easier said than done.
In this post, I want to cover one of the initial pitfalls that you may encounter as you work towards decoupling into microservices. That pitfall is moving authentication and user management to a microservice. Some of the concepts and content I cover in this post are applicable not just to authentication but microservices in general.
The Scaffolding Effect
The Scaffolding Effect is one of the most difficult hurdles to overcome when it comes to building a microservices architecture. The concept is this: no matter what framework engineers use, authentication will always come out of the box. Typically, this is thought of as a major win as it expedites development and delegates security to a (hopefully) trusted entity. Engineers are wisely taught to delegate security to security experts.
Using built-in authentication and user management is certainly a quick win, but it tightly couples user management with product functionality. In some situations, that may be okay. In others, your product just incurred technical debt on Day 1. As the enterprise expands and the business wants SSO, architects will often find themselves fighting with multiple "master" user tables due to the easy-to-use scaffolding. How can we solve this problem? How could it have been avoided in the first place?
Authentication via REST
Before merging any user data stores into a Master, the team must decide on whether authentication and authorization should exist as a service at all. If the answer is yes, this service needs to be stateless, reliable, and secure. Authentication should be your most stable service given it is the gateway to your product. From a security perspective, SSL is a given and the core service itself should reside behind a bastion host. I highly recommend using SSL for every hop even if a proxy is involved.
For actual credential passing, I highly recommend signing and authenticating requests. This requires the client application maintain keys and pass the signature in the Authorization HTTP header. I'm not going to get into the details of how this is all handled, but Amazon has a fairly comprehensive document about it that you can follow.
The most obvious way of decoupling user management from product functionality is via an OAuth server. OAuth is the classic SSO protocol that you see across the web. If, for example, you sign into a product with your Facebook account, then you are most likely leveraging OAuth.
Given how pervasive OAuth is, one would be inclined to think that standing up an OAuth provider is straight forward. I have not found that to be the case. OAuth, OpenID, and SAML providers can be remarkably difficult to build, and without proper understanding of the protocol, can become a large target for hacking. There are tools that exist to make it easier, but even then, it requires a deep understanding of what is being exposed and how it is being leveraged.
Synchronization is a Hard Problem. In the past, I've seen teams attempt to synchronize user data across stores rather than merge into a master store. This can be an absolute nightmare and creates five new problems for every one it solves. Synchronization drives your architecture away from loose coupling.
That said, in some situations Master synchronization is unavoidable. If this is the situation, ensure that your master table maintains a unique identifier for that user and that any clients that need to perform updates have this identifier as well. It's a primary key. This concept is paramount as you expand to using more microservices as well.
The second thing that you need to create is a policy for concurrency (this also applies to all microservices). In small systems, the most simple rule to follow is Last Write Wins. In larger and more distributed systems, you should study concepts of eventual consistency to develop a plan.
Synchronization is not a final solution, though.
A True Microservice
A true microservice has a bounded context which means it does one thing well without knowing what others are doing. For User details that are pertinent to the entire architecture (username, password, email, etc), the microservice should be responsible for managing that data, its input, and its output. This could mean exposing a user interface for updating that data. It could also mean a separate user interface microservice that talks to the API. You can see this in action today by visiting Gmail and then updating your Google account settings. The Gmail service itself is not responsible for handling updates. It delegates that to myaccount.google.com.
Jumping over the Trap
When launching a new product, it's tempting to follow the beaten path and leverage existing authentication frameworks. Personally, I'm not against them if that's what it takes to launch. Products and services are, after all, the lifeblood of businesses. My best advice for engineers that have to launch "quick and dirty" is to code towards an interface and not an implementation. Leave yourself enough rope to introduce a microservice without significant refactoring. For architects, try to keep the environment additive early-on. Rather than needing to fully replace major components when the architecture starts to mature, design things in a way where you're not just shuffling servers.
Without significant foresight and capital, creating a loosely coupled microservices architecture can be difficult if not impossible. It takes significant time not to mention trial and error for finding what works. It also takes some level of business partnership in the sense that you need to educate stakeholders on any restrictions a microservice may impose, why it's valuable, and how it positively impacts the company.
There is a lot more to extracting major artifacts such as User Management and Authentication into microservices. There are also other ways to solve this problem that are not mentioned here. Hopefully this post offers enough to plant some ideas, and get you started asking the right questions.