Advice on re authenticate flow

After some battle we got a deal with chatgpt..
Seems the code you share is not working because App Connect uses XMLHttpRequest instead of fetch

This is what I got working:

(function() {
  const originalOpen = XMLHttpRequest.prototype.open;
  const originalSend = XMLHttpRequest.prototype.send;

  let isRefreshing = false;
  let refreshQueue = [];

  function retryQueue() {
    refreshQueue.forEach(cb => cb());
    refreshQueue = [];
  }

  XMLHttpRequest.prototype.open = function(method, url) {
    this._url = url;
    return originalOpen.apply(this, arguments);
  };

  XMLHttpRequest.prototype.send = function() {
    const xhr = this;

    const originalOnReadyStateChange = xhr.onreadystatechange;

    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 401) {
        console.log('Interceptado 401 en XHR desde:', xhr._url);

        if (!isRefreshing) {
          isRefreshing = true;

          fetch('/api/refresh-token', {
            method: 'POST',
            credentials: 'include'
          }).then(res => {
            if (res.ok) {
              console.log('Refresh OK. Reintentando peticiones fallidas...');
              retryQueue();
            } else {
              console.warn('Refresh FAIL. Redirigiendo al login...');
              window.location.href = '/login';
            }
          }).catch(err => {
            console.error('Error durante refresh:', err);
            window.location.href = '/login';
          }).finally(() => {
            isRefreshing = false;
          });
        }

        // Guardamos la función para que se reintente luego
        refreshQueue.push(() => {
          const retryXhr = new XMLHttpRequest();
          retryXhr.open(xhr._method, xhr._url, true);
          retryXhr.withCredentials = true;
          retryXhr.setRequestHeader('Content-Type', 'application/json');
          retryXhr.onload = xhr.onload;
          retryXhr.send(xhr._body);
        });
      }

      if (originalOnReadyStateChange) {
        return originalOnReadyStateChange.apply(this, arguments);
      }
    };

    // Guardamos los datos para reintentar después
    this._method = this._method || this.method || 'GET';
    this._body = arguments[0];

    return originalSend.apply(this, arguments);
  };
})();

API's are called again, but values are not printed on DOM..

Trying to avoid window.location.reload(); but I think I got no other solution..


EDIT: Asked: Can you reload all dmx-serverconnect/dmx-api-action after re-authenticate?

(function() {
  const originalOpen = XMLHttpRequest.prototype.open;
  const originalSend = XMLHttpRequest.prototype.send;

  let isRefreshing = false;
  let refreshQueue = [];

  function retryQueue() {
    refreshQueue.forEach(cb => cb());
    refreshQueue = [];
  }

  XMLHttpRequest.prototype.open = function(method, url) {
    this._url = url;
    return originalOpen.apply(this, arguments);
  };

  XMLHttpRequest.prototype.send = function() {
    const xhr = this;

    const originalOnReadyStateChange = xhr.onreadystatechange;

    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 401) {
        console.warn('Interceptado 401 desde:', xhr._url);

        const wapplerComponent = findWapplerComponent(xhr._url);

        if (!isRefreshing) {
          isRefreshing = true;

          fetch('/api/refresh-token', {
            method: 'POST',
            credentials: 'include'
          }).then(res => {
            if (res.ok) {
              console.log('Refresh OK. Reintentando cargas...');
              retryQueue();

              // Fallback: si en 1.5s no hay cambio visual, recargamos la página
              setTimeout(() => {
                console.warn('No se actualizó el DOM. Recargando...');
                window.location.reload();
              }, 1500);

            } else {
              console.warn('Refresh FAIL. Redirigiendo al login...');
              window.location.href = '/login';
            }
          }).catch(err => {
            console.error('Error durante refresh:', err);
            window.location.href = '/login';
          }).finally(() => {
            isRefreshing = false;
          });
        }

        refreshQueue.push(() => {
          if (wapplerComponent) {
            console.log('Reintentando:', wapplerComponent.id);
            wapplerComponent.load();
          } else {
            console.warn('No se encontró componente Wappler para:', xhr._url);
          }
        });
      }

      if (originalOnReadyStateChange) {
        return originalOnReadyStateChange.apply(this, arguments);
      }
    };

    return originalSend.apply(this, arguments);
  };

  // Detecta tanto dmx-api-action como dmx-serverconnect
  function findWapplerComponent(url) {
    const components = document.querySelectorAll('dmx-api-action, dmx-serverconnect');
    for (const comp of components) {
      const actualUrl = comp.dmxConnect?.settings?.url;
      if (actualUrl && url.startsWith(actualUrl)) {
        return comp;
      }
    }
    return null;
  }
})();

And all is working now :slight_smile:

Amazing...
I really keep some distance with AI, but I have to say it would have taken me a long time without that..

Thanks @Mozzi again :slight_smile:

1 Like