
This is the follow-up to part 1 of the Auth With JWT story. I would highly recommend you go over that first if you haven’t already done so before going over this article. That being said, in this part 2, we will talk about the following three high-level sections:
So, without further ado, let’s dive in!
Challenges:
JWT Payload Size:
As you might have read in the first part, the business challenges we were trying to solve involved supporting different roles per context(e.g., course). This means a user can be a student in one course, a teacher in another course, a TA in another course, etc. So, we needed to store information about a user and all their courses with the corresponding role in them.
This use case stresses our ability to bake all these data inside the JWT token in terms of size. As this token would be used in every request as an authorization context for the user, we needed to make sure that it doesn’t adversely affect the request latency.
The mitigation strategy for this was:
- Limit the authorization information for the user. In this specific case, the user would have a limitation on how many courses they can enroll in(e.g. 500). It works out fine, as a user would usually need only so many courses for their lifetime of study.
- Let’s say, for some reason, a user needs more courses than the set limitation. The mitigation at this level was to inject authorization info for the latest N set of authorization info for the user instead of everything based on the user’s activity in those contexts. Going with the student/course example, the user’s client would receive a token with the latest N courses the user accesses. In this way, the size limit of the token overhead would never go out of hand.
JWT Refresh Interval:
Another challenge was the refresh interval associated with JWT architecture. To keep the JWT shortlived, it needs to be refreshed at regular intervals. There is an inherent challenge with having a token valid for some time(here, it was specifically for a few minutes or so). If the authorization information changes during that interval(student enrolls in a new course, etc), it’s not going to be reflected in the token. This will cause an inconsistent authorization/permission state for the user in those contexts.
This auth challenge with JWT was mitigated by identifying certain use cases where course role changes happen. The business logic behind those use cases would request and force a token refresh. Certain approaches were:
- Whenever an event occurs that results in a change of authorization, the response will include specific metadata indicating a need for a token refresh.
- If the event occurs on the client’s end, the client will invalidate the token and immediately make the “refresh token” API call.
Lessons learned:
Client-side support overhead:
While the team was already aware of the required support on the client side (web/mobile), their overheads were slightly underestimated, especially around the force refresh need, which ended up in several use cases, along with the nature that triggers request spike.
Doesn’t scale for permission-based auth:
This has been a known challenge from the very beginning. Suppose we were to make the authorization process more granular and with individual permission; this would significantly increase the payload and make it virtually unusable. This risk was calculated, especially due to alignment across stakeholders, to ensure we don’t have such a use case in the foreseeable future. Any permission-based authorization need could trigger a rethinking of this architecture altogether.
Interesting findings(?):
Security- No context-based check:
In some areas, the code was checking a specific role without checking context-based authorization checks. Example:
if(role == "teacher") {
//do stuff assuming this is a teacher
//no check for specific context(course etc)
} else {
//do stuff assuming this is a student role
}
Code language: JavaScript (javascript)
As you can see, logic was written assuming two roles only, and no explicit check was made to verify those users’ authorization for the specific context requested (e.g., course).
IDOR:
IDOR vulnerability was another issue we discovered in the codebase in different places due to a lack of security practices in the early days of the product.
Conclusion:
Despite the challenges explained, this was a suitable choice for us to build a good enough solution faster and with minimal overhead(e.g. no server-side role-check, etc). However, though we found auth with JWT to be a good fit for our platform use cases, it doesn’t necessarily mean JWT might be the right technology choice for you as well. You should do your due diligence. A core fundamental aspect of JWT is probably good to remember: JWT doesn’t care about data encryption(You can use simple tools like JWT Builder and JWT Decoder to build and inspect JWT tokens). It simply cares about validation through signature verification.
Discover more from CODESAMPLEZ.COM
Subscribe to get the latest posts sent to your email.
Leave a Reply