Where to store refresh tokens in the browser

Like the title says, if using PKCE in the browser, where should the refresh tokens be stored. Should AppAuth be even used in the browser environment?

Okta’s draft on handling recommends limiting refresh tokens in the browser to 24 hours ish, but that isn’t doable per client on hydra AFAIK. So I’m kinda at an stuck on what to do.

There are two answers to this:

a) Use OpenID Connect with silent refresh which does not use actual refresh tokens but a hidden iframe which uses a session cookie.
b) Use Auth Code Flow with PKCE and the following considerations:

Okta’s take doesn’t make sense because the new refresh token will also be stored in the same browser, so you’re still vulnerable to XSS attacks.

You have three options:

  • Store in localStorage or something similar (leveldb, …)
  • Store in a js-readable cookie
  • Store in a httpOnly and secure cookie

Basically this boils down to two approaches:

  • Store the tokens using JavaScript in the browser
  • Store the tokens using a sever

In your threat model you need to consider if you’re likely to be attacked by XSS. I would use questions like:

  • What data could be stolen? Is it valuable enough for someone to attempt an XSS attack?
  • How likely is an XSS attack? Do I display/parse/process any type of user input?

If you are not that likely to be attacked by XSS and/or the scope of such an attack would be very limited you can consider storing it using JavaScript in the browser (localStorage, …). However, keep in mind that:

  • a single XSS 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 XSS can be used to load malicious data into these objects too, so don’t consider objects in these to be trusted.

It’s also a good idea to have very, very strict origin policies in place if you decide to go down this road.

So as you can see, a limited refresh token will not fix the above issues. It really sounds more like some really bad pseudo-solution.

If you decide that storing it in the browser is too dangerous, the concrete implementation depends on your application. Auth0 has some good write ups for this. You can find them here: