Getting right to the point: storing a token in LocalStorage is insecure.
It’s getting more and more common to use token based authentication, specially on Single Page Applications (SPA) that need to communicate with an API. That is a good thing, and I really like the idea of JWT tokens.
Why localStorage is bad
You can also set the cookie as secure, which means that the cookie is being sent only over the HTTPS connections.
But sometimes cookies are not the solution…
When cookies are not the solution
If you have an application that doesn’t have a backend (which is not that common) you can’t generate cookies. In this case you have two options:
- You have a Single Page Application (SPA) and you can keep the token in memory. This maybe a have some usability problems, like if the user opens a new tab of closes and reopens the page, of even if refreshes it
- LocalStorage – This is the bad solution. Unfortunately it has a better user experience. But again an app without a backend is quite uncommon, so my recommendation would just be to create one.
If your login system is in a different domain besides the withCredentials that you need to set in the XHR requests you also need to change the CORS rules to allow the requests from the domain of the frontend.
Don’t forget CSRF
Not using cookies had its advantages and one of them was that you didn’t have CSRF attack vectors (unless using BasicAuth). If you get back to cookies you may have this old attack vector back. But not always.
If you have a specific set of conditions you may be safe from CSRF using cookies. Let’s take a look at them:
- Not using GET method to do state changing actions – This is the most important rule and an important best practice.
- Have a restrictive CORS policy – Do not allow requests from URL’s others then the one from your frontend
- Accept only JSON and XML content – An attacker has two ways to ‘trick’ a victim into doing a request. Using an html form tag, or a XHR request. The XHR request is already covered because of the CORS policy. The form tag can only send text, url encoded or form encoded content.
But have in mind that most of frameworks accepts text/plain content type and then convert it to the expected one, so always make sure plain content type is not enabled.
if you meet these 3 conditions you don’t need to worry with CSRF again, Otherwise don’t forget to add the the protection.
To wrap up: do not store auth token in localstorage.