DEV Community

Liz Laffitte
Liz Laffitte

Posted on

7 1

Resolving the ActionController::InvalidAuthenticityToken Error in a Rails/React App

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
Enter fullscreen mode Exit fullscreen mode

and/or

Can't verify CSRF token authenticity.
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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())
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
            },
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode
  • Send the token to your POST action on submit: /app/javascript/components/Signup.jsx
const handleOnSubmit = (e) => {
    e.preventDefault()
    signup({username, email, password}, token)
}
Enter fullscreen mode Exit fullscreen mode
  • 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)
          })
       ....
    }
}
Enter fullscreen mode Exit fullscreen mode

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (1)

Collapse
 
tfantina profile image
Travis Fantina

Thank you so much! Really helped out when trying to implement Stripe!

Neon image

Set up a Neon project in seconds and connect from a Ruby application

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay