Taking advantage of subdomains for refresh token rotation in SPAs


Say I have three components in a system:

  1. An identity service, hosted at identity.mydomain.com
  2. A single page application, served from app.mydomain.com
  3. An API, protected by requiring a bearer token signed by identity.mydomain.com

In the single page application, would it be considered secure to keep an access token in memory, and a rotating refresh token (set by identity.mydomain.com, marked with all the expected security attributes as well as SameSite=strict) in a cookie? The refresh token would rotated similarly to this auth0 article here: https://auth0.com/docs/tokens/concepts/refresh-token-rotation

My thinking for the flow would be as follows:

  1. User visits app.mydomain.com
  2. The SPA sends a request to the token endpoint of identity.mydomain.com
  3. identity.mydomain.com returns 401 because there is no refresh token cookie
  4. SPA redirects user to identity.mydomain.com
  5. User authenticates
  6. identity.mydomain.com sets a refresh token cookie (with HttpOnly, Secure, SameSite=Strict) valid for .mydomain.com (all subdomains)
  7. User is redirected back to app.mydomain.com
  8. app.mydomain.com sends a request to the token endpoint of identity.mydomain.com
  9. identity.mydomain.com receives the cookie, because it is on the same overall domain.
  10. identity.mydomain.com sets a new refresh token cookie, invalidates the old one, and returns a very short-lived access token
  11. app.mydomain.com can then store that access token in memory and use it to call the API at service.mydomain.com.
  12. access token expires, so the SPA sends another request to identity.mydomain.com/token to refresh the tokens and the cycle continues.

I can’t see a way this would be particularly vulnerable – the refresh token wouldn’t be available to JS due to its protected attributes, and even if it is retrieved somehow the rotation should ensure it’s not used more than once. The SameSite=true attributes should also protect against CSRF. I’d make the refresh token also a signed JWT so the identity service can validate it and make sure it is issued by the correct authority as well.

If this is insecure, I’ve definitely misunderstood something somewhere down the line – so please could you explain why?