Passing CSRF token to REACT/FLUX from node

Saad picture Saad · Mar 11, 2015 · Viewed 9.1k times · Source

I'm using nodejs and usually pass down csrf token like the following:

util.js

module.exports.csrf = function csrf(req, res, next){
     res.locals.token = req.csrfToken();
     next();
};

app.js

app.use(csrf());
app.use(util.csrf);

and then in the ejs page I would do

<input type="hidden" name="_csrf" value="<%= token %>">

However, now I'm using flux/react for my front end and need to pass a csrf token for a form submission and not to sure how to do this. There was a similar answer here using jade:

How to implement CSRF protection in Ajax calls using express.js (looking for complete example)?

However, I'm using ejs (with jsx)(or just html) and don't want to use jade

Answer

Joe McBride picture Joe McBride · Mar 16, 2015

I have found the best way to do this in React is to add the csrf token to a store, or pass it to the component context.

You can see how its done by slightly altering the Yahoo Fluxible react-router example.

context.executeAction(setTokenAction, req.csrfToken(), function(){});

This executes a flux action with the csrf token as a parameter. The Yahoo flux architecture serializes the values of the store to the client via:

var exposed = 'window.App=' + serialize(app.dehydrate(context)) + ';';

This gets written out to the page in a script tag, which can then be accessed on in the client-side javascript. It will look something like this:

<script>
window.App = {
  context: {
    dispatcher: {
      stores: {
        ApplicationStore: {
          csrf: "1234abcd",
        }
      }
    }
  }
};
</script>

Here is the Html.jsx component in the Flux example.

If you are not creating an isomorphic application (the React components are run on the sever as well as the client) then I would suggest just to write out a script tag that contains the csrf token.

For Fluxible that value is then rehydrated on the client.

var dehydratedState = window.App; // Sent from the server
var app = require('./app');

app.rehydrate(dehydratedState, function (err, context) {
  ...
});

Leaving you with a populated store on the client without an additional http request. You can then access the csrf token from anywhere by accessing the store.

You can pass it via the context by doing something like this:

var componentContext = context.getComponentContext();
componentContext.csrf = req.csrfToken();

...

var markup = React.renderToString(Component({context: componentContext}))

You can then access it via the component's props.

this.props.context.csrf

If you are using Fluxible and want to pass it via the context I would maybe do it in a plugin, but you get the idea.

Full sever code:

/**
 * Copyright 2014, Yahoo! Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
require('babel/register');
var express = require('express');
var favicon = require('serve-favicon');
var serialize = require('serialize-javascript');
var navigateAction = require('./actions/navigate');
var setCsrfTokenAction = require('./actions/setCsrfToken');
var debug = require('debug')('Example');
var React = require('react');
var app = require('./app');
var HtmlComponent = React.createFactory(require('./components/Html.jsx'));
var Router = require('react-router');

var server = express();
server.use(favicon(__dirname + '/../favicon.ico'));
server.use('/public', express.static(__dirname + '/build'));

server.use(function (req, res, next) {
    var context = app.createContext();

    debug('Executing navigate action');
    Router.run(app.getComponent(), req.path, function (Handler, state) {
        context.executeAction(setCsrfTokenAction, req.csrfToken(), function(){});
        context.executeAction(navigateAction, state, function () {
            debug('Exposing context state');
            var exposed = 'window.App=' + serialize(app.dehydrate(context)) + ';';

            debug('Rendering Application component into html');
            var Component = React.createFactory(Handler);
            var html = React.renderToStaticMarkup(HtmlComponent({
                state: exposed,
                markup: React.renderToString(Component({context:context.getComponentContext()}))
            }));

            debug('Sending markup');
            res.send(html);
        });
    });
});

var port = process.env.PORT || 3000;
server.listen(port);
console.log('Listening on port ' + port);