This is the follow-up part to part 1 of Auth With JWT story. Highly recommended to go over that first if you haven’t already.
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 and be a teacher in another course, be TA in another course etc. So, we needed to store information about a user and all their courses with the corresponding role in it.
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 have any adverse effect on the request latency.
The mitigation strategy for this was:
- Limit the authorization information for the user. So, here 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 activity of that user in those contexts. Going with the student/course example, the user’s client would receive a token with the latest N courses the user access. 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 in those contexts for the user.
This challenge of auth with JWT was mitigated by identifying those certain use cases where course role changes happen. And business logic behind those use cases would request and force a refresh of the token. Certain approaches were:
- Whenever an event occurs that would result in a change of authorization, the response will include specific metadata that would indicate a need for a token refresh.
- If the event occurs on the client’s end, the client will invalidate the token and make the “refresh token” API call immediately.
Other lessons learned:
Client-side support overhead:
While the team was already aware of the required support on the client side (web/mobile), their overheads of them 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 right from the very beginning. Suppose we were to make the authorization process more granular and with individual permission; this would significantly increase the payload that would become 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 re-thinking this architecture altogether.
Security- No context-based check:
In some areas, the code was checking a specific role without checking context-based authorization checks. Example:
As you can see, logic was written with the assumption of two roles only, as well as no explicit check was done to verify those users’ authorization for specific context requested (e.g course).
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.
Despite the challenges explained, this was a suitable choice for us to build a good enough solution at a faster pace 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 the encryption of data. It simply cares about validation through signature verification.