-
-
Notifications
You must be signed in to change notification settings - Fork 631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider a CSRF replacement for $.ajax #16
Comments
My two cents. I use this: import _ from 'underscore'
/* globals document */
export default class BaseData {
constructor() {
this.defaultParams = {
credentials: 'same-origin'
}
}
getCSRFToken() {
return _.find(document.getElementsByTagName("meta"), (meta) => {
return meta.name === "csrf-token"
}).content
}
defaultHeaders = () => {
return {
'X-CSRF-Token': this.getCSRFToken(),
'Accept': 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json'
}
}
post = (url, props) => {
return fetch(url, {
method: 'post',
headers: this.defaultHeaders(),
body: JSON.stringify(props),
...this.defaultParams
})
.then((response) => {
return response.json()
})
}
put = (url, props) => {
return fetch(url, {
method: 'put',
headers: this.defaultHeaders(),
body: JSON.stringify(props),
...this.defaultParams
})
.then((response) => {
return response.json()
})
}
delete = (url) => {
return fetch(url, {
method: 'delete',
headers: this.defaultHeaders(),
...this.defaultParams
})
.then((response) => {
return response.json()
})
}
} And this module: And in the webpack config: new webpack.ProvidePlugin({
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
}) |
We're using axios here: @alexfedoseev Are we missing the headers for: 'Accept': 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json' Here's our source: https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/client%2Fapp%2Futils%2FcommentsManager.js import request from 'axios';
const API_URL = 'comments.json';
const CommentsManager = {
/**
* Retrieve comments from server using AJAX call.
*
* @returns {Promise} - result of ajax call.
*/
fetchComments() {
return request({
method: 'GET',
url: API_URL,
responseType: 'json',
});
},
/**
* Submit new comment to server using AJAX call.
*
* @param {Object} comment - Comment body to post.
* @returns {Promise} - result of ajax call.
*/
submitComment(comment) {
return request({
method: 'POST',
url: API_URL,
responseType: 'json',
headers: {
'X-CSRF-Token': this.getCSRFToken(),
},
data: { comment },
});
},
/**
* Get CSRF Token from the DOM.
*
* @returns {String} - CSRF Token.
*/
getCSRFToken() {
const metas = document.getElementsByTagName('meta');
for (let i = 0; i < metas.length; i++) {
const meta = metas[i];
if (meta.getAttribute('name') === 'csrf-token') {
return meta.getAttribute('content');
}
}
return null;
},
};
export default CommentsManager; |
No, since we don't use Vendor MIME Type to match the api version (we use URL instead). And example of import http from 'axios';
import getCSRFToken from './getCSRFToken';
const apiURLPrefix = '/api/v1';
// Transforming Immutable data to plain JS
http.defaults.transformRequest.unshift(data => (
data && data.toJS === 'function' ? data.toJS() : data
));
http.interceptors.request.use(config => {
// If it's not remote call, adding api prefix
if (!config.remote) {
config.url = `${apiURLPrefix}${config.url}`;
}
// Adding CSRF Token header
config.headers = {
'X-CSRF-Token': getCSRFToken(),
};
return config;
});
export default params => http(params); |
👍 I'm using that because I'm using a jsonapi-resources backend, which requires that MIME type |
@alexfedoseev @josiasds @mapreal19 How about we release a tiny npm module to encapsulation axios and the ability to get the CRSF token from Rails? Then we can put the inclusion of the tiny npm module in the template setup. |
I'd rather made Reasons:
|
This is probably the one we want to to support: https://github.com/matthew-andrews/isomorphic-fetch |
@jbhatab @alexfedoseev Once we put in the NPM library, I'll try to merge in the code from the: https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/client/app/libs/metaTagsManager.js |
@justin808 @alexfedoseev @jbhatab Regardless though, I vote we do not put this in the npm module for the same reason that Alex chose not to use Axios's implementation. Some people may not even be doing sessions this way. |
Just to leave another implementation here for the sake of brainstorm. We only use helper methods that don't wrap fetch, but aid in parameter setting. Implementation in this gist. For regular non-safe json requests:
With just the Rails CSRF headers:
For safe calls (includes CSRF and credentials & same-origin header):
|
We'll soon be adding a JS helper method for the CRSF to the ReactOnRails npm module for this. We probably want to be agnostic about the choice of fetch vs. axios. |
agreed |
We simply need to add this helper method to ReactOnRails. import _ from 'lodash';
export default {
/**
* Get CSRF Token from the DOM.
*
* @returns {String} - CSRF Token.
*/
getCSRFToken() {
const token = _.find(document.querySelectorAll('meta'), ['name', 'csrf-token']);
return token ? token.content : null;
},
}; To:
We should also remove the dependency on lodash. We need some docs and tests. This gist from @Fletcher91 also looks good: https://gist.github.com/fletcher91/c62b865c6aa9f710531e |
I'll probably add @Fletcher91's code to the default package for ReactOnRails. Yes, we could have a separate node module. It's a tiny enough amount of code that I don't deem that necessary. @Fletcher91 Any changes to your gist? I copied below. /**
*
* A regular non-safe get request:
* fetch('/profiles/foobar.json', jsonHeader());
*
* How this would look in a safe fetch request:
* fetch('/profiles.json', safeCredentials({
* method: 'POST',
* body: JSON.stringify({
* q: input,
* thing: this.props.thing
* })
* }));
*
*
*/
/**
* For use with window.fetch
*/
export function jsonHeader (options) {
options = options || {};
return Object.assign(options, {
'Accept': 'application/json',
'Content-Type': 'application/json'
});
}
/**
* Lets fetch include credentials in the request. This includes cookies and other possibly sensitive data.
* Note: Never use for requests across (untrusted) domains.
*/
export function safeCredentials (options) {
options = options || {};
return Object.assign(options, {
credentials: 'include',
mode: 'same-origin',
headers: Object.assign((options['headers'] || {}), authenticityHeader(), jsonHeader())
});
}
// Additional helper methods
export function authenticityHeader (options) {
options = options || {};
return Object.assign(options, {
'X-CSRF-Token': getAuthenticityToken(),
'X-Requested-With': 'XMLHttpRequest'
});
}
export function getAuthenticityToken () {
return getMetaContent('csrf-token');
}
export function getMetaContent (name) {
let header = document.querySelector(`meta[name="${name}"]`);
return header && header.content;
} |
@justin808 Aside from some minor things like |
@Fletcher91, @jbhatab does this make sense to go into React on Rails from the perspective that the CRSF thing is so standard for Rails apps? or more of a doc thing? |
I'd be happy to have this as part of react_on_rails. It's really a very common requirement. |
@anpr With the direction were going, I think this shouldn't be covered with our gem and can be covered in a docs if even needed at all. it depends on your architecture style. We can relook into this when we redo the docs stuff. |
@jbhatab, we're going to provide a default for this. I really think this is so common that it makes sense to include in our library, if it's non-obtrusive. @anpr I can help you with this. Please email me at [email protected] and I can add you to our Slack room. |
We're working on restructuring our front-end (splitting into packages), so I can publish these helpers publicly to NPM if that helps |
I basically copied the code above to my project and it solved the problem (with small adjustments/additions). Still, I see this being a common requirement, so I'd publish it either as part of the gem or as a separate npm package (I don't really care which of the two). |
@anpr I think part of the standard npm package for react-on-rails is fine if it's very small. |
@dzirtusss Please take a look at this one. |
We're using:
and we don't have many of the headers in https://gist.github.com/fletcher91/c62b865c6aa9f710531e Can we abstract this into ReactOnRails? Reference for Rails: http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf CC: @alexfedoseev @robwise |
@Fletcher91 @anpr @alexfedoseev @dphaener @samnang @dzirtusss: we'd like to know if the headers here: https://gist.github.com/fletcher91/c62b865c6aa9f710531e |
Actually I think we should not need to include
And everything works fine. Maybe it will not work with |
I think it will be ok if we use
|
Resolved by #517 |
See:
shakacode/react-webpack-rails-tutorial#73 (comment)
We could provide a something helpful in this gem that facilitates true promises via 'fetch'.
The text was updated successfully, but these errors were encountered: