Skip to content

Commit

Permalink
added CSRF helpers authenticityToken and authenticityHeaders
Browse files Browse the repository at this point in the history
  • Loading branch information
dzirtusss committed Aug 16, 2016
1 parent e293a94 commit 426adaa
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Contributors: please follow the recommendations outlined at [keepachangelog.com]
- React on Rails server rendering now supports contexts outside of browser rendering, such as ActionMailer templates [#486](https://github.com/shakacode/react_on_rails/pull/486) by [eacaps](https://github.com/eacaps).
- React on Rails now correctly parses single-digit version strings from package.json [#491](https://github.com/shakacode/react_on_rails/pull/491) by [samphilipd ](https://github.com/samphilipd ).
- Fixed assets symlinking to correctly use filenames with spaces. Begining in [#510](https://github.com/shakacode/react_on_rails/pull/510), ending in [#513](https://github.com/shakacode/react_on_rails/pull/513) by [dzirtusss](https://github.com/dzirtusss)
- Added authenticityToken() and authenticityHeaders() javascript helpers for easier use when working with CSRF protection tag generated by Rails [#517](https://github.com/shakacode/react_on_rails/pull/517) by [dzirtusss](https://github.com/dzirtusss)

## [6.0.5] - 2016-07-11
##### Added
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,22 @@ Components are created as [stateless function(al) components](https://facebook.g
## ReactOnRails JavaScript API
See [ReactOnRails JavaScriptAPI](docs/api/javascript-api.md).

#### Using Rails built-in CSRF protection in JavaScript

Rails has built-in protection for Cross-Site Request Forgery (CSRF), see [Rails Documentation](http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf). To nicely utilize this feature in JavaScript requests, React on Rails is offerring two helpers that can be used as following for POST, PULL 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);
```

If you are using [jquery-ujs](https://github.com/rails/jquery-ujs) for AJAX calls, than these helpers are not needed and [jquery-ujs](https://github.com/rails/jquery-ujs) library updates header automatically, see [jquery-ujs documentation](https://robots.thoughtbot.com/a-tour-of-rails-jquery-ujs#cross-site-request-forgery-protection).

## React Router
[React Router](https://github.com/reactjs/react-router) is supported, including server side rendering! See:

Expand Down
23 changes: 23 additions & 0 deletions docs/api/javascript-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,27 @@ The best source of docs is the main [ReactOnRails.js](../../node_package/src/Rea
* `traceTurbolinks: true|false Gives you debugging messages on Turbolinks events
*/
setOptions(options)

/**
* Allow directly calling the page loaded script in case the default events that trigger react
* rendering are not sufficient, such as when loading JavaScript asynchronously with TurboLinks:
* More details can be found here:
* https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/turbolinks.md
*/
reactOnRailsPageLoaded()

/**
* Returns CSRF authenticity token inserted by Rails csrf_meta_tags
* @returns String or null
*/

authenticityToken()

/**
* Returns header with csrf authenticity token and XMLHttpRequest
* @param {*} other headers
* @returns {*} header
*/

authenticityHeaders(otherHeaders = {})
```
14 changes: 14 additions & 0 deletions node_package/src/Authenticity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {

authenticityToken() {
const token = document.querySelector('meta[name="csrf-token"]');
return token ? token.content : null;
},

authenticityHeaders(otherHeaders = {}) {
return Object.assign(otherHeaders, {
'X-CSRF-Token': this.authenticityToken(),
'X-Requested-With': 'XMLHttpRequest'
});
},
};
26 changes: 26 additions & 0 deletions node_package/src/ReactOnRails.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import StoreRegistry from './StoreRegistry';
import serverRenderReactComponent from './serverRenderReactComponent';
import buildConsoleReplay from './buildConsoleReplay';
import createReactElement from './createReactElement';
import Authenticity from './Authenticity';
import ReactDOM from 'react-dom';
import context from './context';

Expand Down Expand Up @@ -68,10 +69,35 @@ ctx.ReactOnRails = {
}
},

/**
* Allow directly calling the page loaded script in case the default events that trigger react
* rendering are not sufficient, such as when loading JavaScript asynchronously with TurboLinks:
* More details can be found here:
* https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/turbolinks.md
*/
reactOnRailsPageLoaded() {
ClientStartup.reactOnRailsPageLoaded();
},

/**
* Returns CSRF authenticity token inserted by Rails csrf_meta_tags
* @returns String or null
*/

authenticityToken() {
return Authenticity.authenticityToken();
},

/**
* Returns header with csrf authenticity token and XMLHttpRequest
* @param {*} other headers
* @returns {*} header
*/

authenticityHeaders(otherHeaders = {}) {
return Authenticity.authenticityHeaders(otherHeaders);
},

////////////////////////////////////////////////////////////////////////////////
// INTERNALLY USED APIs
////////////////////////////////////////////////////////////////////////////////
Expand Down
30 changes: 30 additions & 0 deletions node_package/tests/Authenticity.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import test from 'tape';
import ReactOnRails from '../src/ReactOnRails.js';

test('authenticityToken and authenticityHeaders', (assert) => {
assert.plan(4);

assert.ok(typeof ReactOnRails.authenticityToken == 'function',
'authenticityToken function exists in ReactOnRails API');

assert.ok(typeof ReactOnRails.authenticityHeaders == 'function',
'authenticityHeaders function exists in ReactOnRails API');

const testToken = 'TEST_CSRF_TOKEN';

var meta = document.createElement('meta');
meta.name = 'csrf-token';
meta.content = testToken;
document.head.appendChild(meta);

var realToken = ReactOnRails.authenticityToken();

assert.equal(realToken, testToken,
'authenticityToken can read Rails CSRF token from <meta>');

var realHeader = ReactOnRails.authenticityHeaders();

assert.deepEqual(realHeader, { 'X-CSRF-Token': testToken, 'X-Requested-With': 'XMLHttpRequest' },
'authenticityHeaders returns valid header with CFRS token'
);
});
1 change: 1 addition & 0 deletions node_package/tests/ReactOnRails.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,4 @@ test('setStore and getStore', (assert) => {

assert.deepEqual(ReactOnRails.stores(), expected);
});

0 comments on commit 426adaa

Please sign in to comment.