Don Giulio
Don Giulio

Reputation: 3304

react with Rails 5, getting CSRF error for post with axios

I'm trying to use react_on_rails to build my first example with react and rails. I'm trying to save some data to the rails backend, using axios for the ajax.

here's my code:

import store from "../store/helloWorld";
import axios from "axios";

export const SAVE_NAME = "SAVE_NAME";

export function saveNameAction(name) {
  return {
    type: SAVE_NAME,
    name
  };
}

export function saveName(name) {
  axios
    .post("/hello_world", saveNameAction(name))
    .then(function(response) {
      console.log(response);
    })
    .catch(function(error) {
      console.log(error);
    });
}

and the component:

import PropTypes from "prop-types";
import React from "react";
import * as actions from "../actions/helloWorld";

export default class HelloWorld extends React.Component {
  static propTypes = {
    name: PropTypes.string.isRequired // this is passed from the Rails view
  };

  /**
   * @param props - Comes from your rails view.
   */
  constructor(props) {
    super(props);
    this.state = { name: this.props.name };
  }

  updateName(name) {
    this.setState({ name: name });
  }

  handleSubmit(event) {
    event.preventDefault();
    actions.saveName(this.state.name);
  }

  render() {
    return (
      <div>
        <h3>
          Hellopp, {this.state.name}!
        </h3>
        <hr />
        <form>
          <label htmlFor="name">Say hello to:</label>
          <input
            id="name"
            type="text"
            value={this.state.name}
            onChange={e => this.updateName(e.target.value)}
          />

          <input
            type="submit"
            value="Submit"
            onClick={event => this.handleSubmit(event)}
          />
        </form>
      </div>
    );
  }
}

The problem is that when I click the submit, my backend reports

Started POST "/hello_world" for ::1 at 2017-07-07 15:30:44 +0200
Processing by HelloWorldController#create as HTML
  Parameters: {"type"=>"SAVE_NAME", "name"=>"Stranger", "hello_world"=>{"type"=>"SAVE_NAME", "name"=>"Stranger"}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms)

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

For one, I don't understand why the parameters seem to be passed twice, but that's not even generating a warning, so don't care for now.

The problem is that I don't see a way to obtain the CSRF tokens in my react code to use in the post requests

should I just disable CSRF? or is there a better way?

Upvotes: 8

Views: 4265

Answers (3)

amliving
amliving

Reputation: 390

react_on_rails deals with this issue by providing two helpers. From the react_on_rails documentation:

Rails has built-in protection for Cross-Site Request Forgery (CSRF), see Rails Documentation. To nicely utilize this feature in JavaScript requests, React on Rails provides two helpers that can be used as following for POST, PUT or DELETE requests:

import ReactOnRails from 'react-on-rails';

// reads from DOM csrf token generated by Rails in <%= csrf_meta_tags %>
csrfToken = ReactOnRails.authenticityToken();

// compose Rails specific request header as following { X-CSRF-Token: csrfToken, X-Requested-With: XMLHttpRequest }
header = ReactOnRails.authenticityHeaders(otherHeader);

Upvotes: 2

Don Giulio
Don Giulio

Reputation: 3304

I found that react_on_rails has a helper system to handle CSRF tokens,

it basically uses:

  <%= csrf_meta_tags %>

to add the csrf_token to the headers in the page as a meta

and then you can use:

import ReactOnRails from "react-on-rails";

export function saveNameAction(name) {
  console.log("creating action " + name);
  return {
    authenticity_token: ReactOnRails.authenticityToken(),
    type: SAVE_NAME,
    name
  };
}

to fetch it and use it.

Upvotes: 0

Pavan
Pavan

Reputation: 33542

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

Rails handles CSRF attacks by appending authenticity_token to every Non-GET requests(POST,PUT/PATCH and DELETE). The error means you are not sending authencity_token in the request params, You should append an unique authenticity_token to the params, something like "authuenticity_token" => "BsfdgtZ1hshxgthjj" which should resolve the issue.

Upvotes: 2

Related Questions