Trying the new Refresh Token Rotation in a React SPA. Are 3rd party cookies supposed to be required?

Nice to see Refresh Token Rotation shipped today, congrats Auth0 team!

So I’m trying to test it out using @auth0/auth0-spa-js v1.7.0 on a minimal React SPA (deploy, repo) carefully following what I think should be the relevant quickstart from the docs. I used create-react-app + TypeScript and things are pretty vanilla, here’s the refresh token opt-in

OK, so here’s the GIF of what’s going on:

TL, DR: 3rd party cookies appear to be required for ‘silent auth’ aka the refresh button. Is that right? I thought one of the main selling points of refresh token rotation was to avoid needing to enable 3rd party cookies, given the browser vendors are pushing that hard these days. But maybe I’ve missed something/some new config perhaps.
Could anyone help me understand please?

@randynasson @steve.hobbs thanks for your efforts on shipping this!

Hi @onpaws,

Thanks for trying out the new feature! In that default configuration with refresh tokens turned on, you will be storing the refresh token in memory. So when you come to the app fresh or refresh the page, you won’t have a refresh token.

In any event that you don’t have a refresh token, the SDK falls back to the legacy iframe method to try and get you an access token and a refresh token based on your existing session. This unfortunately has all the pitfalls of requiring 3rd-party cookies to work, so if they are blocked, getting a new refresh token won’t work in this case.

We added another new feature to this SDK to get around this somewhat, which is an opt-in to storing tokens in local storage. This can be configured by setting cacheLocation to localstorage, which means the tokens can be persisted across page refreshes.

Give that a try and let me know how you get on. I will make sure this detail is covered properly in the readme :+1:

Hi @steve.hobbs,
Brilliant, thanks for the quick response.

I went ahead and tried it and the refresh is now working with 3rd party cookies disabled, as you said, thanks! One-liner lives here in case anyone else reading this is interested.

Have a question about local storage, I realize this situation in general is a complex issue with multiple tradeoffs and I’m probably ignorant on the reasoning here. What I’m hoping to understand: why local storage vs a cookie?

Based on implementing a JWT-based auth system last year I was under the impression it was considered desirable from an XSS risk mitigation perspective to use cookies with the HttpOnly flag set. So on that project we set the refresh_token as a cookie with the usual security flags and a path, and then the server subsequently returns ephemeral access_tokens that live in browser memory only. Obviously the spec reflects a lot of smart people’s efforts, and my above solution was built before that spec was finalized, so I may be ignorant of a problem with the above approach.

I do realize refresh token rotation means rotating refresh tokens frequently :slight_smile: and I’m guessing there are reasons the token has to live local storage, but I’m curious to understand why. What about this system precludes using cookie(s)? If I understand right, on SPA page init we’re making a request to the Auth0 endpoint anyway. Seems like the endpoint could ostensibly respond to a cookie-presented token then, no?

Are there plans in the works to support cookies in the future? Or is this whole idea fundamentally Not A Thing.

Thanks again, appreciate your insight!

@onpaws One big reason is that one of the reasons we introduced this feature is because cookies are being blocked by enhancements in browser privacy technology, such as Safari’s ITP or by default in Brave.

Strictly on the client side, we assume that this SDK is being used with a SPA architecture, meaning you probably won’t have a backend to set cookies. If you did, you would most likely be doing authentication on your backend and wouldn’t be using this library. In any case, the cookie in this scenario could not be HttpOnly as it would have to be read by JavaScript.

In the end, whether you store tokens in a cookie or in local storage doesn’t make too much difference as they both suffer from the same issues regarding XSS vulnerabilities and so on.

I do realize refresh token rotation means rotating refresh tokens frequently

You’re correct, but they also have built-in reuse detection - if a refresh token is leaked and used, a subsequent exchange will kick in the detection and the entire “family” of refresh tokens will be invalidated.

2 Likes

Hi @steve.hobbs reading through this thread and I think I have a good grasp on the implementation. So as I understand, storing tokens or other sensitive data in localStorage is generally a bad practice as it makes your application vulnerable to XSS. I have a few questions I was hoping you could help with that are related to this post but if need be, I can create a separate thread.

  1. With the Rotating Refresh token approach, it’s okay to store the refresh and access tokens in local storage as the Auth0 team has built in the reuse detection feature, correct?
  2. When the reuse detection is alarmed and all the refresh tokens are invalidated, will this just essentially render the session deactive for the legitimate client and thus require them to login?
  3. I have a backend Node/Express API which my React SPA uses to get non-user related data. When user logs in on client and get the refresh, access, and id token, I’m thinking that I store the refresh token in local storage on client while relying on refresh token rotation to render that token useless if need be and providing the user with a secure session through page refreshes. When I need to make an auth call to get user-related data, should I make the call that gets a new access and refresh token directly from client-side or should I make a call to my Express API first which then makes the call to Auth0 to get new tokens? I would think the former would work but wasn’t sure if there were vulnerabilities I could encounter doing it that way.

Apologies for the block of questions. Any help will be greatly appreciated!

Hi Steve, looks like the cacheLocation stuff isn’t in the docs? I was trying to get this working from the documentation at Configure Refresh Token Rotation. Thanks!

CC: @steve.hobbs for visibility.

Thanks @konrad.sopala :bowing_man:

@paulaspireiq Using cacheLocation to configure token storage is not a prerequisite of using Refresh Token Rotation, the two features are independent of each other. You can find more information about the cache setting on our GitHub repository, in the API reference, or in the library docs.

Hope that helps!

1 Like

Thanks for helping here Steve!

Hi @steve.hobbs , I have a question, using refresh token rotation mechanism, how could I detect suspicious refresh token usage if legitimate client does not communicate with server again i.e closes tab and does not reopen for a while? In that time interval, could the attacker who leaked the token continue to generate tokens until the client reopens the application and the server detects the reuse?
Taking into account that the access token is stored in the application memory and the refresh token in local storage. Would it also be advisable to invalidate the entire token family when the user closes the tab or how would this process be controlled for the attacker?
Thanks in advance for your attention, greetings from Colombia :wave:

1 Like

Thanks for the question @fredycorts7, and welcome to the Auth0 Community forums!

Your analysis of the scenario is correct; if the legitimate client does not use their refresh token for a while, and the token is stolen and used, then it’s difficult to detect that and mitigate against it. It’s important to realise that, while Reuse Detection increases the chance of detecting malicious use, it’s not a silver bullet for protecting against it as you’ve described in your scenario. The only real bullet-proof mitigation here involves the use of sender-constrained tokens, which is not something we have right at the moment.

One thing to do right now to help is ensure that your application is mitigated in the best way possible from XSS attacks from being successful in the first instance:

  • Keep the amount of third-party JavaScript being used to a minimum, and only from trusted sources
  • Use as short an expiry time on your access tokens as is practical

Hope that helps!

2 Likes

Thank you very much for the clarification and your recommendations. @steve.hobbs

1 Like

We are here for you!

1 Like