DEV Community

Bjørn Lindholm
Bjørn Lindholm

Posted on • Originally published at

Storing tokens in single-page applications

Single-page applications that use tokens to authenticate users need to implement a strategy for storing the tokens securely.

Don't use localStorage

Tokens should not be stored in localStorage or sessionStorage. These data stores are accessible from any JavaScript code running on the page. Storing tokens in localStorage makes your application vulnerable to XSS attacks.

You might think you control all the JavaScript on a page, but that's not necessarily true. A dependency of a dependency, a 3rd party tracking script, or a chrome plugin are all examples of code that could be malicious without your knowledge.

Don't use browser cookies

Tokens should not be stored in cookies created with JavaScript. JavaScript cookies, similarly to localStorage, can be read by other JavaScript code.

Malicious code running on the client could access the token and make requests on behalf of the user.

Do use memory

Storing a token in memory is better than saving a token in localStorage, as long as it isn't stored in variable accessible by the global scope.

The problem with storing tokens in memory is that the storage isn't persistent when a user refreshes the page or opens your application in a new browser tab.

Do use HTTP cookies

Tokens can be stored in cookies created by the server as long as it has the correct security attributes.

A cookie storing a token should have the HttpOnly attribute. This attribute ensures it cannot be read with JavaScript, thus protecting against XSS attacks.

The cookie should also have the Secure attribute. This attribute ensures the cookie can only be set and read on HTTPS connections. Using an encrypted connection protects against man-in-the-middle attacks.

The Domain attribute should be used to ensure that the cookie is only returned to the server of the application.

Lastly, the SameSite attribute should be either Strict or Lax. This attribute ensures that only the server can store the cookie, which protects against CSRF attacks.

For this method to work, the client and server applications must be hosted on the same domain.

Tired of handling authentication manually? I'm creating starter kits for Laravel and Vue/React for different authentication methods. Check out Titanium to stay updated!

Top comments (11)

gregorip02 profile image
Gregori Piñeres

Please don't use localStorage 🙅🏾‍♂️🙅🏾‍♂️🙅🏾‍♂️🙅🏾‍♂️🙅🏾‍♂️

omaratta212 profile image
Omar Atta

With Http-only you are still vulnerable to self-XSS, any browse extension, for example, could send requests to your server as the authenticated user. How should we deal with it?

paularah profile image
Paul Arah

Embed CRSF tokens your Auth tokens payload and also save the CSRF tokens in local storage. Then on the server, verify the CRSF token in the payload against the CRSF token retrieved from local storage. This completely isolates you from both types of attack.

ndiuel profile image

You pray to your God for protection.

miladr0 profile image
Milad Ranjbar

actually ur vulnerable to CSRF, if using the cookie with HTTP-only but u can use a package csurf to solve this problem.

bjornlindholmdk profile image
Bjørn Lindholm

That's a tricky one. One way could be to store the cookie in memory with a limited scope

boussou profile image
Nadir Boussoukaia • Edited

"JavaScript cookies, similarly to localStorage, can be read by other JavaScript code." => that is wrong. You have to use cookies.

I suppose you call "HTTP cookies" cookies that are generated by the server: right. With the secure flag.

bjornlindholmdk profile image
Bjørn Lindholm

Yeah that what was I was trying to explain. JavaScript cookies in this case are cookies set by document.cookie while HTTP cookies are cookies set by the server. Sorry for confusion!

robencom profile image

Where should I save my token and user data in a Vue-Laravel app??
Coming from a PHP background, PHP saves such info in the session, which is a file in the server. Now that I am doing the front-end with Vue, I'm keeping the token on the localstorage (the app is still in dev) until I find a better solution.
For the user data, I am making an axios call, but I really don't like that, because it is an extra axios call for every page.

Kindly suggest the best way to do this?

anandkashyap profile image
Anand Kashyap

Any way to have client and server have same domain but keeping them hosted separately?

paularah profile image
Paul Arah

that's are a tricky one, does having client and server hosted under different sub-domains of the same domain counts? If yes, then the DNS records just need to point to the different IP addresses of the host. If no, then you might want to configure a reverse proxy to route the traffic based on the URL path to either the client or server. That feels hacky already :)