The application is a SPA that is hosted on static storage (conceptually similar to S3, although it's not S3) and has no backend server at all. Assume this is https://static.example.com/app.html
When users visit the page, they can authenticate with an external provider like Auth0 and Azure AD. They complete the authentication flow and are sent back to the SPA with an id_token
on the URL fragment. For example, https://static.example.com/app.html#id_token=XX
. That id_token
is used to call an external API server, passed in the Bearer
authorization header.
The issue is where to store the JWT in the client.
- It's a known fact that storing the JWT in
sessionStorage
could lead to the tokens being stolen with XSS attacks (or malicious code added in a dependency, etc). - The recommended approach would be storing the JWT in a cookie that is set to
HttpOnly
, or at least part of it (see the "Cookie Split" section). However, this is not doable in my case, as there is no backend server, and after being authenticated users are redirected to the SPA directly, so I can't create aHttpOnly
cookie.
A variant of this method is what OWASP recommends: using a "fingerprint cookie". This has the same issues since I can't set a cookie that isHttpOnly
. - Another approach, as for example suggested by the Auth0 documentation, is to keep the JWT in memory. While this should prevent theft with most (if not all?) XSS attacks, it's unpractical because the session would be limited to the current tab, and would not survive a page reload.
I see two different options, all with serious or potentially serious drawbacks:
- Store the token in
sessionStorage
anyways, assuming the risk that in case of XSS attacks (or a malicious dependency injected via NPM) could lead to sessions being stolen. This could be mitigated by setting a short lifespan to tokens (e.g. 1 hour). While the app I'm working on doesn't store critical information (it's not banking or similar), a mistake in the code that let sessions to be stolen via XSS would not be nice. - Implement a backend server to move the authentication flow there, maybe even replacing JWT's with session tokens entirely. This would make the application not static anymore, however, and it's undesirable.
- (The third option, keeping the JWT in memory, is excluded because of the poor user experience)
What am I missing?