TLDR; JWT(JSON Web Token) can be a very easy yet effective way to build an auth platform(authentication and authorization).
In one of my past employments, I was fortunate enough to be part of an interesting yet technically challenging project, that aimed to solve the auth problem(authentication+authorization) in a distributed service-oriented architecture(I am referring to this as service-oriented instead of microservice as it started in that way and still had a way to go to establish a microservice infrastructure).
In this post, I will explain the auth problem we were trying to solve, the architectural decisions we made, and our execution approach.
Problems we were solving:
Engineering Problem:
At that point in time, as an engineering organization, we were still using monolithic architecture. Naturally, we were growing as a startup and started to feel the pains of the monolith(slow deployment pipeline, tight and/or wrong coupling between components, slower developer productivity etc) and were thriving as a team to move towards a distributed microservice architecture. Some example components which were some great candidates to be in their separate services (It was an ed-tech platform):
Payment
Gradebook
Lecture
However, to achieve this, there is another core problem we would have to solve. Which supports authentication/authorization in a distributed service-oriented architectural setup. The authentication problem was critical as we started building crucial features such as Gradebook, which would split into its own service as one of our first independent service candidates.
Product problems:
As a startup, we were growing our product line and had a couple of specific business problems we needed to solve with our users:
Support new roles:
Our platform only had a few different roles defined from the very early stage of the company, like:
Teacher
Student
Anonymous
Superuser
We were in need of introducing a few new roles, like:
TA(Teaching Assistant)
School Admin
Context-based roles:
Just introducing new roles was not enough. We also needed to make those roles context-based. For example, a user who is a student in one course may be assigned as TA in another course. Teachers sometimes also want to preview how a student experience looks like for their students, which is, being a student in certain courses for a short period of time.
The approach to building the auth platform:
Making the core technology decision:
First and foremost, we had to pick the core technology that would be relying upon to build this auth platform. We had a bunch of different options to pick from:
Use a token-based solution that doesn’t require a separate service to manage.
Finally, we settled down on the token(JWT) based solution. Some of the key reasons for that:
The cost was the primary factor that caused us to move away from third-party solutions.
As we decided to build our own platform, we needed an approach that would be simple but effective.
We wanted to reduce service level overhead as it would introduce a central point of failure and would require some serious investment around high availability/reliability which we weren’t highly experienced with as an engineering organization just yet.
Also, as a side note, I will highly recommend using a decision matrix to help with such a scenario where you have multiple paths and trying to decide on one that fits your criteria.
Approaching auth with JWT:
After the core technology decision, the next problem was to settle down on the approach we would take to build the auth platform using JWT. As a result of this, some fundamental considerations we need to make:
Shortlived Tokens: The tokens needed to be shortlived, as, by design, JWT tokens can be inspected by any third party. To guard against a phishing attack, the JSON payload is encoded(base 64) and signed by a secret key shared by all the services so that a service can validate that it is issued by a trusted source. These tokens are usually valid for a very short period and the client would need to refresh their token after it expires.
Client ability to force refresh the tokens: Based on how long a token can be valid before it expires, the information inside the JWT can expire (right after a user enrolls/unenrolls/creates a course, etc) and would experience unexpected behaviours if not refreshed forcefully right after those certain use cases.
Token size: Token size was another fundamental consideration as we would inject a fairly good amount of roles/authorization info in those tokens. (depending on the user’s limit).
I will revisit some of these in my future article around challenges/lessons learned.
To give an idea of how a token would look like, after injecting the user’s identify+authorization info:
This was a multi-quarters project that needed a long-term roadmap and phased delivery. These are the brief steps we followed:
Centralizing all authorization usages in our existing monolith.
Building JWT-based Solution:
Inject identity/authorization info inside the JWT whenever someone registers/logs in to the system.
Validate the user’s role in the context by looking into the JWT payload for requests that require authorizations(hint: applies to almost all traffic other than a very small number of public APIs).
Build a client library as the authorization abstraction that we can use as a dependency on other new services. API was quite simple as:
auth_lib.is_authenticated(JWT_token) – check if the client making requests with a valid JWT token.
auth_lib.is_authorized(user, context, cotext_id, role) – check if the user is authorized as a specified role for the given context.
Migration/Rollout:
Migrate every user’s authorization data on demand. Helped us avoid dealing with large migration pain.
Uninterrupted rollout: We used feature flipper flags to gatekeep this new platform so that we can disable them anytime it doesn’t work as we expect it to reduce the impact on our end users.
Wrapping up:
This is what our new architecture (except auth was still part of the monolith) afterwards:
Fig: New Service-oriented architecture with JWT.
I will try to write another article focusing on the challenges we have faced along the way, lessons learned and some fun findings throughout the journey. Stay tuned!
Also, feel free to share your thoughts in the comments below.
var JetpackInstantSearchOptions=JSON.parse(decodeURIComponent("%7B%22overlayOptions%22%3A%7B%22colorTheme%22%3A%22light%22%2C%22enableInfScroll%22%3Atrue%2C%22enableFilteringOpensOverlay%22%3Atrue%2C%22enablePostDate%22%3Atrue%2C%22enableSort%22%3Atrue%2C%22highlightColor%22%3A%22%23FFC%22%2C%22overlayTrigger%22%3A%22submit%22%2C%22resultFormat%22%3A%22expanded%22%2C%22showPoweredBy%22%3Atrue%2C%22defaultSort%22%3A%22relevance%22%2C%22excludedPostTypes%22%3A%5B%5D%7D%2C%22homeUrl%22%3A%22https%3A%5C%2F%5C%2Fcodesamplez.com%22%2C%22locale%22%3A%22en-US%22%2C%22postsPerPage%22%3A5%2C%22siteId%22%3A18994550%2C%22postTypes%22%3A%7B%22post%22%3A%7B%22singular_name%22%3A%22Post%22%2C%22name%22%3A%22Posts%22%7D%2C%22page%22%3A%7B%22singular_name%22%3A%22Page%22%2C%22name%22%3A%22Pages%22%7D%2C%22attachment%22%3A%7B%22singular_name%22%3A%22Media%22%2C%22name%22%3A%22Media%22%7D%7D%2C%22webpackPublicPath%22%3A%22https%3A%5C%2F%5C%2Fcodesamplez.com%5C%2Fwp-content%5C%2Fplugins%5C%2Fjetpack%5C%2Fjetpack_vendor%5C%2Fautomattic%5C%2Fjetpack-search%5C%2Fbuild%5C%2Finstant-search%5C%2F%22%2C%22isPhotonEnabled%22%3Afalse%2C%22isFreePlan%22%3Atrue%2C%22apiRoot%22%3A%22https%3A%5C%2F%5C%2Fcodesamplez.com%5C%2Fwp-json%5C%2F%22%2C%22apiNonce%22%3A%22155bc22a78%22%2C%22isPrivateSite%22%3Afalse%2C%22isWpcom%22%3Afalse%2C%22hasOverlayWidgets%22%3Afalse%2C%22widgets%22%3A%5B%5D%2C%22widgetsOutsideOverlay%22%3A%5B%5D%2C%22hasNonSearchWidgets%22%3Afalse%2C%22preventTrackingCookiesReset%22%3Afalse%7D"));
Leave a Reply