08/02/2019

Rails CSRF Protection with React

A simple approach to handling CSRF tokens with Ruby on Rails and React when using the webpacker gem

Rails has built in CSRF protection by default, so when you first try to make a post request from your React code, initially your request will fail and you'll probably see a message like this in your console:


Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity
ActionController::InvalidAuthenticityToken
        

The Quick & Dirty Solution

The easier but more reckless fix you may want to do is to simply disable CSRF protection in your rails controller, which can be done like so:


skip_before_action :verify_authenticity_token
        

This will 'fix' the issue, but it will also potentially open your app up to non-authentic requests.

An Alternate Solution?

Let's assume you have an App component in your React code that looks like this:


import React from 'react'
import axios from 'axios'

class App extends React.Component {
  handleSubmit = (e) => {
    e.preventDefault()

    axios.post(url, payload)
    .then( (resp) => console.log(resp))
    .catch( (error) => console.log(error))
  }

  render(){
    return(
      <form onSubmit={this.handleSubmit}>
        <button type="submit">Click Me</button>      
      </form>      
    )
  }
}

export default App

What we can try to do is tweak this code to add our CSRF token to the headers in our request by modifying axios' default headers:


import React, { Component } from 'react'
import axios from 'axios'

class App extends React.Component {
  handleSubmit = (e) => {
    e.preventDefault()

    const csrfToken = document.querySelector('[name=csrf-token]').content
    axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken

    axios.post(url, payload)
    .then( (resp) => console.log(resp))
    .catch( (error) => console.log(error))
  }

  render(){
    return(
      <form onSubmit={this.handleSubmit}>
        <button type="submit">Click Me</button>      
      </form>      
    )
  }
}

Adding that to your code should resolve the CSRF issues in your app and allow you to now make post requests from your React code to your Rails controllers.

Conclussion

The above example is specific to a rails app using the webpacker gem, and uses axios for the solution -- if you are not using webpacker and/or axios, YMMV with this.