If you're using Fetch API to send a non-GET requests to a Rails controller, you may bump into the InvalidAuthenticityToken
exception. It's because Rails requires a special CSRF token to validate the request, and you can pass it via X-CSRF-Token
header.
Here is a working example of adding the CSRF token in the headers:
// Grab the CSRF token from the meta tag
const csrfToken = document.querySelector("[name='csrf-token']").content
fetch("/v1/articles", {
method: "POST",
headers: {
"X-CSRF-Token": csrfToken, // πππ Set the token
"Content-Type": "application/json"
},
body: JSON.stringify({ title: "awesome post" })
}).then(response => {
if (!response.ok) { throw response; }
return response.json()
}).then((data) => {
console.log(data)
}).catch(error => {
console.error("error", error)
})
In old-fashioned Rails apps, CSRF token is handled by rails-ujs
transparently so there is no extra work for you.
However, if you're running Rails + React or even vanilla JavaScript where you want to fire the raw requests from the frontend, you'll need to do what the code snippet above shows: grab the CSRF token from the markup and pass it in the headers.
A note for test env
In test env, Rails won't check CSRF token for non-GET requests, and it also won't generate the meta tag for it. So you'll need to do a presence check in your JavaScript before accessing the .content
. Kinda awkward. π³
You may want to put this into a util method.
utils.jsexport function getCSRFToken() {
const csrfToken = document.querySelector("[name='csrf-token']")
if (csrfToken) {
return csrfToken.content
} else {
return null
}
}
credentials: "same-origin"
During my quick research, all examples I found on the internet have included credentials: "same-origin"
in the request parameters, e.g.
fetch("/v1/articles", {
// method, headers, body omitted
credentials: "same-origin"
})
According to the MDN article , the default credentials value of fetch() has been changed from omit
to same-origin
, so we're safe to omit it π:
Since Aug 25, 2017. The spec changed the default credentials policy to same-origin. Firefox changed since 61.0b13.
Alternatives
If you find yourself doing this many times, you may want to consider a more adavanced libraries like axios or ky which supports global defaults, so that you'll only need to configure the CSRF header once.