Reading Token from LocalStorage for E2E Use Case

Hey there,

Say you’ve got an SPA that uses @auth0/auth0-spa-js. The token is ordinarily stored in memory as a JavaScript variable, and updated with getTokenSilently as needed.

let cachedToken;

export function authorize() {
  return await auth0.authorize();
}

function doFetch(url, opts = {}) {
  opts.headers ??= new Headers();
  opts.headers.set('Authorization', `Bearer ${cachedToken}`); 
  return fetch(url, opts).then(x => x.json);
}

export function fetchFromAPI(endpoint, opts) {
  try {
    return doFetch(`/api/v1/${endpoint}`, opts);
  } catch (error) {
    // if it's an auth error...
    cachedToken = await auth0.getTokenSilently();
    return doFetch(url, opts);  
  }
}

Fine.

We’d like to implement a new use case: Replicating the user’s page server-side via Playwright (or puppeteer). In this case, the user makes a request to a certain endpoint, which then runs playwright on the same URL the user requested from. Think end-to-end testing, but unlike typical E2E, instead of some generic “test” user, It’s important that playwright be able to load the page as the requesting user. To that end, the token is passed to the playwright endpoint in the Authorization header.

In that case, Playwright could set the token in sessionStorage. Then the app, if it finds a stored token could bypass login:

let cachedToken = sessionStorage.getItem('set_by_e2e');

export function authorize() {
  if (sessionStorage.getItem('set_by_e2e'))
    return;
  else
    return await auth0.authorize();
}

But this opens the user up to XSS, right?

We’ve considered even going so far as to deploy a second, sessionStorage-reading version of the SPA to a secure server, but that seems a bit overkill for not much payoff.

Our requirements for this feature are:

  • To replicate the parrticular user’s session on the server (more or less)
  • We don’t need to persist the session, i.e. it’s entirely transactional
  • We don’t mind rotating the tokens immediately upon completion, however access_tokens can’t be revoked, right?

Is there a better way, given the user’s access_token (or by some other method), to replicate their session for a one-time transaction on the playwright server?

2 Likes