Document loading request occasionally drops custom headers

WebViewer Version: “@pdftron/webviewer”: “10.11.1”

Do you have an issue with a specific file(s)? No
Can you reproduce using one of our samples or online demos? No
Are you using the WebViewer server? No
Does the issue only happen on certain browsers? Insufficient sample size
Is your issue related to a front-end framework? No
Is your issue related to annotations? No

Please describe your issue and provide steps to reproduce it:

We see logs from our document service that indicate that requests are being made to it without the proper authorization headers. We added some markers to all requests from our client to the document service in the form of extra URL parameters we’d see in the logs.

Through this, we know that:

  • the only place this happens is from a webviewerInstance.UI.loadDocument() call
  • the authorization headers (an Access-Token header, specifically) are being supplied to the call
  • the authorization headers are missing from the network traffic (checked with a ServiceWorker)

This tells us that the problem is somewhere inside the Webviewer, and not us.

Please provide a link to a minimal sample where the issue is reproducible:

We haven’t been able to reproduce the problem in a testing environment ourselves. We only see this in the logs from production, where it happens 10-20 times out of an estimated 200k document loads per day.

Hello duncan,

Thank you for reaching out!

Can you provide the code snippet youre using to modify the headers?
Additionally, have you disabled other forms of loading? Such as the UI file picker?

Best regards,
Tyler

Here’s the call:

    const customHeaders = {
      ...(authToken ? { [authHeaderName]: authToken } : {}),
      ...(params.token ? { [externalAuthHeaderName]: params.token } : {}),
    };
    const customHeadersAreEmpty = Object.keys(customHeaders).length === 0;
    const authHeaderIsEmpty = !customHeaders[authHeaderName];
    instance.UI.loadDocument(
      annotateURLViaQueryString(
        docUrl,
        'request_source',
        `${formatTime(new Date())}|pdfTronViewer^loadDocument;url:${baseurl.length > 20 ? baseurl.substring(0, 20) : baseurl};${serializeActivityLog()};headers_empty:${customHeadersAreEmpty};auth_empty:${authHeaderIsEmpty}`
      ),
      {
        filename: docName?.includes('/')
          ? docName?.split('/').reverse()[0]
          : '',
        cacheKey: crypto.randomUUID(),
        customHeaders: customHeaders,
      }
    );

Here’s the service worker:

function fetchListener(event) {
  let url = new URL(event.request.url);
  if (!url.pathname.startsWith('/document/v1/field/')) return;

  let searchParams = url.searchParams;
  const headers = event.request.headers;

  console.debug(url);
  console.debug(Array.from(event.request.headers.entries()));

  const tokenHeaderValue = headers.get('access-token');
  if (tokenHeaderValue) {
    console.debug('saw access token header');
    searchParams.set(
      'sw_witnessed_token_header',
      tokenHeaderValue.split('.')[1] // the user-info part of the token
    );
  } else {
    searchParams.set('sw_witnessed_token_header', 'none');
  }

  url.search = searchParams.toString();

  console.debug('new request URL', url.toString());
  event.respondWith(fetch(new Request(url.toString(), event.request)));
}

self.addEventListener('fetch', fetchListener);

self.addEventListener('install', () => {
  self.skipWaiting();
});

self.addEventListener('activate', (event) => {
  event.waitUntil(self.clients.claim());

  console.debug('service worker activated', origin);
});

And here’s the URL we see in logs from a request that errors:
/document/v1/field/5a89bac7-f18e-4015-83c2-a5a331bd1d52/e8be2cb2-7e9b-470d-adc3-7ab8e7101f01/43242b4c-aff4-45af-98cd-2998e1cfb6d7.pdf?request_source=13:26:18:483|pdfTronViewer^loadDocument;url:/process-execution/5;activity:;headers_empty:false;auth_empty:false&sw_witnessed_token_header=none

And in this case, there is no other way for a user to load a document.