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.
sessionStorage
could lead to the tokens being stolen with XSS attacks (or malicious code added in a dependency, etc).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 a HttpOnly
cookie. HttpOnly
.I see two different options, all with serious or potentially serious drawbacks:
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.What am I missing?
Security is a compromise - you can choose to accept the risk of XSS, or to take the burden of a backend server for more security, or to sacrifice user experience for a level of security probably inbetween the two. You can't have everything.
One usual solution is to have very short-lived tokens in sessionStorage, and an httpOnly cookie for the identity provider to get a new token when needed. This way stealing a session token provides less value for the attacker, and as XSS needs user interaction, it may sometimes be difficult for an attacker to get hold of a new one (or easy, depending on where the XSS is). Also this solution needs more graceful error-handling and results in slightly more complex code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With