Security - Don't store tokens in localStorage

Hi all,

Just had a security audit of our current system, and they flagged that “Browser local storage (or session storage) is not a secure place to store sensitive information”.

( More reference info here: )

Meteor stores the following auth data in local storage:


Question: does Meteor offer any other auth (storage) mechanisms?

e.g. server-side (using the Authorization Code Flow, Authorization Code Flow with Proof Key for Code Exchange (PKCE), or Hybrid Flow) or client side (e.g. in memory storage).

Yes, we setup Content Security Policy to reduce the attack vector.

Kind regards,


Not to my knowledge. You might have to roll your own authentication system if you don’t want to use localStorage. Alternatively, you could implement MFA with TOTP: to reduce attack vector.

Exploitation of localStorage generally requires either your user to be compromised, or your app to be compromised (XSS).

1 Like

This doesn’t answer your question, and I’m sure you already now this, but you could easily prevent the storage of the login tokens with:

Accounts._storeLoginToken = function() {}

anywhere in your client-side code.

Your auditor will be happy but your users wont!


Your first link includes the following:

Use the object sessionStorage instead of localStorage if persistent storage is not needed. sessionStorage object is available only to that window/tab until the window is closed.

It should be quite easy to monkeypatch Accounts._storeLoginToken(), Accounts._unstoreLoginToken() and Accounts._storedLoginToken() to use sessionStorage instead of localStorage.

At least then the user’s logged-in status will survive page reloads.



Which basically says that the accounts packages were implemented using localStorage in order to meet Galaxy PaaS requirements/restrictions.

Your auditors might be satisfied if you use a cookie that is httpOnly=true [i.e., not available to JavaScript XSS], secure=true [i.e., https only]. Perhaps there is a way to adapt Meteor accounts?

1 Like

You could accomplish any of these OAuth flows and still not address the storage issue, which OAuth does not specify.

The staringatlights:fast-render package adds a cookie method, without removing the localStorage method. I bet that could be a good jumping off point to get this working exactly how you want.

Could use that (and make sure it’s setting those secure cookie values) and then monkey patch Accounts._storeLoginToken() , Accounts._unstoreLoginToken() to manage the secure cookie?

I suggested the feature to the package maintainer.

1 Like

One thing that I don’t understand: it says local storage is not safe because javascript can read them?! then you move to using cookies.
javascript can read data of localstorage but not cookies? Can someone please explain. Thank you.


I don’t really understand the security concern here either. JavaScript can read both cookies and localstorage. Maybe there’s an argument that the cookies API has been hardened more over the years? I am sensitive to the idea that having the information in 2 locations (both cookies and localstorage) the way fast-render sets up, doubles the attack surface.

From the OP’s link:

Local Storage

  • Also known as Offline Storage, Web Storage. Underlying storage mechanism may vary from one user agent to the next. In other words, any authentication your application requires can be bypassed by a user with local privileges to the machine on which the data is stored. Therefore, it’s recommended not to store any sensitive information in local storage.
  • Use the object sessionStorage instead of localStorage if persistent storage is not needed. sessionStorage object is available only to that window/tab until the window is closed.
  • A single Cross Site Scripting can be used to steal all the data in these objects, so again it’s recommended not to store sensitive information in local storage.
  • A single Cross Site Scripting can be used to load malicious data into these objects too, so don’t consider objects in these to be trusted.
  • Pay extra attention to “localStorage.getItem” and “setItem” calls implemented in HTML5 page. It helps in detecting when developers build solutions that put sensitive information in local storage, which is a bad practice.
  • Do not store session identifiers in local storage as the data is always accessible by JavaScript. Cookies can mitigate this risk using the httpOnly flag.
  • There is no way to restrict the visibility of an object to a specific path like with the attribute path of HTTP Cookies, every object is shared within an origin and protected with the Same Origin Policy. Avoid host multiple applications on the same origin, all of them would share the same localStorage object, use different subdomains instead.

Just going out on a limb here, but not 100% sure that it is optimal to dismiss the concerns of a customer with money, a professional auditor, and a reasonable security concern. But I could be off-base… I mean security is kind of boring

I am going to start ending my posts with this remark:
“Why isn’t Meteor more popular?”

1 Like

I don’t think anyone has dismissed any concerns. In fact, multiple responses here have offered suggestions to address the primary concern, even without understanding it completely. Admitting you don’t understand an issue, is not dismissal.

From a cursory look, httpOnly flag on cookies means the browser will only allow the server (through http headers) create, read, update and delete (CRUD) the cookie. That would mean it’s not so useful for Meteor, since Meteor needs to use the session token client side for various things (subscriptions and methods).

Switching to sessionStorage has it’s own limitations - the token would go away with every session, which means no auto-login on next visit. But if you really need that level of security, this is a feature that could make sense.

You can also leverage a package like browser-policy to help limit cross-site injections.

1 Like

As I see it, the problem is you have to trust the browser implementation of this security measure.

However, in most cases 100.00% of your users will access your site with the intent to be self-secure, so they will use a browser that respects their privacy and security [i.e., any brand-named browser] .

As for Google and the NSA, I have no idea.

Not only that, but from MDN Docs:

  • Opening a page in a new tab or window creates a new session with the value of the top-level browsing context, which differs from how session cookies work.

I’ve just tested a monkey-patch I wrote which confirms that if you’re in a web-app, and you middle-click a link to open it in a new tab, that new tab is not logged in. That could be annoying. Best to use a secure cookie.


The httpOnly flag tells the browser to disallow the JavaScript code from accessing the cookie information. A malicious user can access the information regardless.

Oh. My mistake – I thought the phrase “I don’t really understand… either” was a dismissal. But now I see that this was merely a lament, as we say in in the Southwest (we don’t do subtext). My apologies.

Anyway, I really don’t understand why Meteor isn’t more popular.

This is a bit opinionated. Once you send information to the client, for the most part you’re trusting that you’ve appropriately authenticated the user identity, and you’ve secured the transportation of this information. Basically, anyone with local access can read what you’ve sent to the client, and there is no “secure place” to store sensitive information.


The marketing issue. I think so.

I posted before in a similar discussion somewhere. The local storage security debacle becomes an issue when your app is already compromised (e.g XSS somewhere). This means that anything your client is doing on the current session is already compromised with or without local storage. Local storage just makes it easier for the attacker to get more persistent data.

So before you worry about local storage, worry first why your app is compromised (e.g. XSS)


You might find these links to be educational:

If your website contains any third party JavaScript code included from a source outside your domain:

  • Links to bootstrap
  • Links to jQuery
  • Links to Vue, React, Angular, etc.
  • Links to any ad network code
  • Links to Google Analytics
  • Links to any tracking code

Then you are currently at risk for having an attacker run JavaScript on your website.

The most prominent functionalities unprotected by CSP are many communication channels, such as … storage and file channels (e.g., localStorage and requestFileSystem )… They are available for attackers to steal data even in the absence of content injection vulnerabilities.


There are thousands of custom plugins running on your clients browsers.


Don’t store tokens in local storage

Actually, I’m thankful for this thread, as I learned something valuable. Might be just me, though :wink: