laravel/precognition

Method for setting CSRF token on client

jasonvarga opened this issue · 4 comments

Laravel Precognition Plugin Version

0.5.2

Laravel Version

10.35.0

Plugin

Alpine

Description

We have a setup where the CSRF token or cookie may not be available. (We're serving statically cached html).

We need a way to set the X-CSRF-Token header somehow.

I see that there's a way to set the url argument using a function. I think if we could set the config argument with a function, that'll probably work too.

We can grab the token via an ajax request to a route that just returns it.

Route::get('token', fn () => csrf_token());
const getConfig = function () {
  return { headers: { 'X-CSRF-TOKEN': getTokenViaAjax() } };
}

// ...

$form('post', '/the-url', {}, getConfig);

Or maybe a way of modifying the client after the fact.

document.addEventListener('got-csrf-token', () => {
  $form.setHeaders(...);
});

Or maybe there's already a way to do this. Thanks!

Steps To Reproduce

  • Create a route outside of your web routes, so that there is no session.
  • Output {{ csrf_token() }} to the page, see that its blank.
  • Create a form with precognition. The requests will give 419 responses.

Someone found a solution for us that seems to work.

In the custom event I mentioned, we would update the token on an html element.

Then we can provide an object with a toString method to the header, which will grab the token from the element.

$form('post', '/the-url', {}, {
  headers: {
    'X-CSRF-Token': {
      toString: () => document.getElementById('the-element').value
    }
  }
});

@jasonvarga sorry I didn't see your issue here.

Sounds like you have it solved, but for anyone seeing this I would also recommend doing this globally somewhere via the Axios client configuration...

import { client } from 'laravel-precognition-{package_flavour}';

getCsrfToken().then(csrfToken => {
    client.axios().defaults.headers.common['X-CSRF-TOKEN'] = csrfToken;
})

Thanks Tim.

In our case, we are injecting some JS into the html response via a package. We don't have access to their JS build process so we can't import { client } but this is good to know anyway!

Gotcha! Another option which might feel a little cleaner is to set the _token input on the form, either by injecting a hidden HTML input into the form like @csrf does or adding it in the form data. This would require the the-element to be available on load.

$form('post', '/the-url', {
    _token: document.getElementById('the-element').value,
});

Either way, a few options depending on what you need.