The first (and probably one of the most important) parts of every multi-tenant SaaS application is identity management.
The main idea of multi-tenancy is that a user belongs to one (or more) tenants. The separation of the user-tenant concept is one of the most important aspects of SaaS applications, where several users (sometimes with different roles and permissions) can control the same application (tenant).
While this is great for SaaS, it creates several tiers of complexity for identity management. So as we approach the authentication “challenge”, we’re usually quickly reduced to a checklist of important items (both on the product side and the technical side) that need to be handled:
- What kind of authentication methods do we support? Do we allow social logins? Do we allow SSO and SAML?
- Do we allow the user to login without activating the account via email?
- What is our password policy? Do we allow each tenant to set a different password policy?
- Do we support MFA? Which methods do we allow? Email? SMS? Authenticator app? Do we allow the tenant to force MFA for it’s admins?
- Do we support server logout?
- What is the defined session timeout?
- How do we implement team management and invites?
- Do we allow a user to be associated with more than one tenant?
WOW! That’s a lot to think about and to handle…
This series of posts will tackle (using code samples) the main multi-tenant authentication SaaS capabilities, from the very basic to the most advanced.
In this first post we will focus on the very basis of the entire multi-tenant SaaS authentication — JWT authentication. We’ll discuss what JWTs are, their content and token persistence — what you need to know to get a feel of how they work.
The rise of JWT
JWT (JSON Web Tokens) is the new and de facto authentication method (loved by developers) for several, rather important, reasons.
In a nutshell, JWT is a token generated by the server which includes several claims and payload which represents the user and the characteristics of the token (such as when it was issued, when it expires etc.)
The structure of JWT tokens is fixed and made up from Header, dynamic Payload and Signature
That token is sent to the client upon login and needs to be sent back to each of the servers (or microservices) upon each request which requires valid authentication.
But Wait… If the token is visible on the client side (and the client can decode it), what about security??? What stops the client from generating tokens and impersonating another user???
The most important attribute of JWT tokens is their signature. Each JWT token is signed on the back end by a private key that can be verified by clients (and other microservices) by the public key.
This basic asymmetric method of signatures allows us to easily verify that the authenticated user is actually authenticated and not impersonated without the need for maintaining a central tokens database / session management.
And this is one of the reasons we like JWT so much. The rise of microservices generated the need to decentralize authentication and verification mechanisms — and JWT based authentication matches this need like a glove.
The contents of JWT tokens for Multi Tenant SaaS
When a user logs into the application, we would want to add to the JWT the notion of the tenant in order to make sure that after the verification of the JWT token, we are in the correct context of the tenant and we aren’t exposing our application to cross-tenant security issues.
So the flow is:
Another issue we need to consider is how to handle the JWT tokens on our client application.
Multi-tenant SaaS applications are built today using web technologies (and most of them are Single Page Applications as well).
So we know that we got the JWT token from the authentication endpoint after the login… We know that we need to pass it on each request… And we probably want it to last after a page refresh… Why not save it to the local storage?
In this case, the attacker (using a malicious code) can hijack the stored token from the localstorage, use it for calling the back end on behalf of the user and actually breach the system…
So. How should we protect ourselves?
- How about limiting the expiration of JWT tokens? Setting expiry to 5 minutes? Well, it solves a part of the problem but still leaves you exposed.
- How about saving the token in memory? While this does solve the security problem, it breaks usability as we cannot handle scenarios such as page refresh / new tab.
- Maybe saving the JWT token as an http-only cookie? That totally works but exposes us to CSRF attacks without any additional CSRF protection.
Persisting through refresh tokens and http-only cookies
The use of refresh tokens allows us to continuously refresh the sessions and to get new JWT tokens. Using refresh tokens allows us to shorten the lifetime of the actual JWT tokens while the refresh tokens can actually long live.
Using this approach we can store the JWT tokens in-memory while saving the refresh tokens using http-only cookies. The refresh tokens are not vulnerable to CSRF attacks on form submits because the attacker cannot get the value of the JWT which was returned from the endpoint.
What does the flow look like in this case?
So how will the full flow look upon page load? Because the http-only cookie is persisted via the browser, the flow looks similar by using the same refresh tokens alternative
On this post we discussed how to handle JWT tokens based authentication in your multi-tenant SaaS application and how to leverage the use of refresh tokens in order to safely persist your users’ sessions.
On the next post we will see how to start building a capable back end for securely storing user details and maintaining the user’s activation flows.