Search code examples
djangoservice-workerdjango-csrfbackground-sync

POSTing to a Django form much later than it was generated with Service Worker background sync: CSRF issues


I have a Django form which I make available offline via service worker. If the user completes the form while offline, the data to submit is stored in IndexedDB and then a background-sync is registered; when the browser comes back online, the sync event is fired in the service worker which can then read the data from IndexedDB and submit the form to the back end via fetch().

However, I have Django's CSRF protection turned on. This requires that I submit the form with a legitimate CSRF token, stored in a cookie, but also in the body of the form (or better in an X-CSRFToken header) as well as part of the fetch() request. Unfortunately, because this is happening in a service worker, I can't read the cookie to get the CSRF token. There was a proposal to allow service workers to read cookies but it's not completed and is currently paused, so is not available.

I thought about using postMessage from the service worker to the main page to ask that main page to read the cookie and send it back, but background-sync might run at a time at which there is no main page to postMessage to.

It may be possible to store the CSRF token which is valid at the time the form is generated and cached by the service worker, but I don't know how long a CSRF token remains valid for, or what validation Django does on it.

Is there a sensible way to solve this problem? Obviously I do not want to disable CSRF protection.


Solution

  • By default, the Django CSRF token is valid for one year: https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-CSRF_COOKIE_AGE . Django performs e.g. referer validation, and then compares the two tokens for equivalence.

    You could store the CSRF token (either when the form is cached as you say, or save it at the point you capture the form submission when offline), and then, if the submission of that form later on fails CSRF valdiation and returns an error page, make sure that error page includes a valid CSRF token in it, that the background/offline script can then read, and resubmit the same data, which would then hopefully succeed.