Resolving the ActionController::InvalidAuthenticityToken Error in a Rails/React App
Tags: rails, react, activerecord, cors
Published: 2020-12-23 02:11:45


You've just started building a Rails (backend) and React (frontend) application. You've configured Webpack. You've set up your database. You've connected your backend and your frontend. 

You've built out your first model, controller and view. You go to test your first POST request and it happens...

```
ActionController::InvalidAuthenticityToken
```

and/or

```
Can't verify CSRF token authenticity.
```
 You Google. You weep. You half-heartedly try to use rack-cors.

Never fear! Here is the simple solution:

## 1. Take Note of application.html.erb
First open /app/views/layouts/application.html.erb. This view should've been generated when you ran the `$rails new` command. 

You should see this tag: `<%= csrf_meta_tags %>`

What the heck are these, you say? From the [Ruby docs](https://api.rubyonrails.org/classes/ActionView/Helpers/CsrfHelper.html):
>These are used to generate the dynamic forms that implement non-remote links with :method.

> For AJAX requests other than GETs, extract the “csrf-token” from the meta-tag and send as the “X-CSRF-Token” HTTP header.

## 2. Grab the CSRF Token in Relevant Component
For me, the offending controller action was user#create, meaning the relevant 'view' component was my signup form.

I added a line of JavaScript to my component to capture the token:
*/app/javascript/components/Signup.jsx*
```
const token = document.querySelector('meta[name="csrf-token"]').content;
```

Here we are querying the DOM, finding the meta tag with the name value "csrf-token", and assigning it to the variable token.

## Pass the Token on Submit
Then I passed the new variable to my signup function (passed as a prop from the parent component), so that my signup action would have access to it.

*/app/javascript/components/Signup.jsx*
```
const handleOnSubmit = (e) => {
    e.preventDefault()
    signup({username, email, password}, token)
}
```
This won't work yet, since we haven't altered our signup action to accept the token.

## Accept the Token in Your Action
Back in my userActions, we make a few tweaks to the signup action.
*/app/javascript/actions/userActions.js*
```
export const signup = (credentials, token) => {
    return dispatch => {
        return fetch('http://localhost:3000/signup', {
            credentials: 'include',
            method: "POST",
            headers: {
                "X-CSRF-Token": token,
              "Content-Type": "application/json"
            },
            body: JSON.stringify(credentials)
          })
        .then(response => response.text())
        .then(userData => {
            if(userData.error){
                console.log(userData.errors)
            } else {
                dispatch(addUser(userData.data))
            }
            
        })
        .catch(console.log())
    }
}
```
First we change signup() to accept a second parameter: token. 
`export const signup = (credentials, token) => {`

Then we pass the token under headers.
```
            headers: {
                "X-CSRF-Token": token,
              "Content-Type": "application/json"
            },
```

And... that's it! We're now able to successfully complete the POST request to our backend.

<img width="100%" style="width:100%" src="https://media.giphy.com/media/Q8IYWnnogTYM5T6Yo0/giphy.gif">

To recap:
- Use JS to capture the token, outputted by application.html.erb, in the component where the POST happens:
*/app/javascript/components/Signup.jsx*
```
const token = document.querySelector('meta[name="csrf-token"]').content;
```

- Send the token to your POST action on submit:
*/app/javascript/components/Signup.jsx*
```
const handleOnSubmit = (e) => {
    e.preventDefault()
    signup({username, email, password}, token)
}
```

- Send the token to your backend, in your POST action:
*/app/javascript/actions/userActions.js*
```
export const signup = (credentials, token) => {
    return dispatch => {
        return fetch('http://localhost:3000/signup', {
            credentials: 'include',
            method: "POST",
            headers: {
                "X-CSRF-Token": token,
              "Content-Type": "application/json"
            },
            body: JSON.stringify(credentials)
          })
       ....
    }
}
```