Symfony 3, "ajax request" with fetch API, and CSRF - ajax

In twig i generate a csrf token ({{ csrf_token('my_intention') }}).
In Javascript i call a controller with ajax, in fact with the Fetch API (Ajax xmlHttpRequest tried too), POST request. Argument name containing the token passed in the request is 'token=abcdef...'.
AJAX:
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function (data) {
console.log(data);
};
httpRequest.open('POST', el.getAttribute("data-url"));
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send(.......);
Fetch API:
fetch(el.getAttribute('data-url'), {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: 'token=' + encodeURIComponent(el.getAttribute('data-token'))
}).then(data => data.text()).then(data => {...}
In the controller action called i get the token sent as data from the POST request. I check the token like this in the controller:
$token = $request->request->get('token');
if (!$this->isCsrfTokenValid('my_intention', $token)) {
throw new InvalidCsrfTokenException("error csrf 2");
}
But Symfony say the token is not valid.
I'm not sure but i think token is not found in session variable. In isTokenValid() $this->storage->hasToken($token->getId()) return false.
In the browser, if i call the url directly, it's ok.
In twig i set the url to call in a data attribute like this data-url="{{ path('_check', {'id': transaction.id}) }}", then i read this data attribute from javascript and pass it to ajax/fetch function.
I tried ajax with jQuery $.post(... and it works. The only difference is Cookie:PHPSESSID... in the request header with jQuery not on my original code.
I don't understand, what is wrong with my code ?
Symfony 3.1.3
EDIT: resolved: i didn't pass credentials in headers request, so, no way for Symfony to find session and check token:
fetch(el.getAttribute('data-url'), {
method: 'post',
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest"
},
body: 'token=' + el.getAttribute('data-token'),
credentials: 'include'
}).then(data => data.text()).then(data => {

Even if you found an answer to your issue, I recommend you to take a look at this bundle which handles the token verification based on a Cookie which is defined server-side and that you should pass in each asynchronous request.
https://github.com/dunglas/DunglasAngularCsrfBundle

Related

Remove some cookie from ajax call with Axios

I use Axios in browser to call ajax request. Now I have a problem with some cookie that has high priority than some header. Per request I send a header as AUTHTOKEN but in cookie SESSIONID key stored that high priority than AUTHTOKEN header. In some scenario, I need to ignore cookie. This is my code:
axios({
url:`${sdpBasicUrl}/api/v3/requests/27363`,
method: 'get',
headers: {
'Content-Type': 'application/json'
'AUTHTOKEN': 'GHG23847923HGJ'
}
})
.then(res => {
console.log(res.data);
});
and this is cookie sample:
_z_identity=true; PORTALID=1; csrfcookie=aasdasdjh24234b2bjh4hjl; SESSIONID=ffd68d32a14841c99905e3cf4897e15ec9b4777020854a76821fd7e1eab6db2dcab482eb4cfea2ce7f5a6c47c80271d09f608ed985004e5c85681b2939681b18
What should I do? Do you have any solution to solve my problem?
You are able to pass in cookies through the header like this:
Axios.request({
url: "http://example.com",
method: "get",
headers:{
Cookie: "cookie1=value; cookie2=value; cookie3=value;"
}
}).then...
So if you don't want the value to be there, you could override the values.
https://github.com/axios/axios/issues/943
You can use transformRequest to modify the header for some requests. transformRequest allows changes to the request data and header before it is sent to the server. This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'.
transformRequest: [function (data, headers) {
// Modify the header here and return the header
return data;
}],
You can get more information about it on https://axios-http.com/docs/req_config

Ajax request django rest framework

I've got a problem. Every time I have to clear caches and cookies first and then the AJAX request can be requested successfully. Otherwise I will get 403 response from the server, which is Django RESTful framework.
This is what I request
$.ajax({
url: url_add,
type : 'PATCH',
dataType: 'json',
data: {
'followup_customer': note,
},
statusCode: {
200: function() {
window.location.reload();
}
},
});
You should add a correct HTTP header, containing CSRF token as described in django docs.

javascript: how to make AJAX call based on the avaiable cURL request

Currently in my web app project, I need to parse the content of a web page, and after some searching, I found that Mercury Web Parser API is quite suitable for me.
And I have some experience with such kind of third party APIs, generally speaking I can get my desired result.
But for this API, I can't find documentation about the API usage on the official website.
Based on the my study, it provide two methods:
first is cURL as following:
curl -H "x-api-key: myapikey" "https://mercury.postlight.com/parser?url=https://trackchanges.postlight.com/building-awesome-cms-f034344d8ed"
the myapikey is the API key I get from the website. Then I can get the result in JSON format, which is the main content of the web page specified by the url parameter. It works well for me, I mean the cURL method.
And on the website, it said that the second method is HTTP call, which is just what I need:
GET https://mercury.postlight.com/parser?url=https://trackchanges.postlight.com/building-awesome-cms-f034344d8ed
Content-Type: application/json
x-api-key: myapikey
So based on my understanding, I use jquery AJAX method to do this as following:
var newurl = "https://mercury.postlight.com/parser?url=http://www.businessinsider.com/joel-spolsky-stack-exchange-interview-2016-12&x-api-key=myapikey"
$.ajax({
url: newurl,
dataType: "jsonp",
success: function(data){
console.log(data.title);
}
})
here I made JSONP request because of the Cross origin issue.
But now I face 401 error message (401 Unauthorized. The request has not been applied because it lacks valid authentication credentials for the target resource)
For now my guess is that the apikey is not correctly passed to server. So based on the cURL's successful call, can I get the correct format for AJAX call?
Update:
Based on the following answers ,I tried to set the request header as following:
$.ajax({
url: newurl,
dataType: "jsonp",
beforeSend: function(xhr){
console.log(apiKey);
xhr.setRequestHeader('x-api-key', apiKey);
},
/*
headers: {
"x-api-key": "M1USTPmJMiRjtbjFNkNap9Z8M5XBb1aEQVXoxS5I",
"contentType": 'application/json'
},
*/
success: function(data){
console.log("debugging")
console.log(data.title);
},
error: function (error) {
console.log(error)
}
})
I tried both beforeSend and headers. But still can't work and get the following trackback error message:
send # jquery.js:8698
ajax # jquery.js:8166
addNewArticle # topcontroller.js:18
fn # VM783:4
e # angular.js:281
$eval # angular.js:147
$apply # angular.js:147
(anonymous) # angular.js:281
dispatch # jquery.js:4435
elemData.handle # jquery.js:4121
And for the last send function, still 401 error.
But the ajax error handling part shows that the readyState:4 and status: 404 result. So what's going here.
For your question, the curl request is sending a header which you have attached as part of the query string in your $.ajax request.
Try the following instead (using beforeSend + xhr) :
// broke this string down so you don't have to scroll
var newurl = "https://mercury.postlight.com/parser?" +
"url=http://www.businessinsider.com/" +
"joel-spolsky-stack-exchange-interview-2016-12";
// set your api key
var apiKey = "<your api key>";
$.ajax({
url: newurl,
dataType: "json",
beforeSend: function(xhr){xhr.setRequestHeader('x-api-key', apiKey);},
success: function(data){
console.log(data.title);
}
})

CSRF token mismatch error in laravel 5 on multiple async ajax request?

I'm using Laravel 5.2 for my web application and I have a page with multiple ajax requests by the same event. In $.ajax, I set async: true, Sometimes it shows CSRF token Mismatch error and redirect to login page. However when I set Async: false in ajax, it works fine but it takes lots of time.
Please help me so that it does not show token mismatch error.
in your form create one hidden filed name _token you can use this helper method to generate field
{!! csrf_field() !!}
in javascript you have to fetch this field value
var token = $( "input[name='_token']" ).val();
$.ajax({
method: "POST",
url: "some.php",
data: { name: "John", location: "Boston",_token:token }
});
another way create hidden a span or div add data attribute to it
<div id="token" data-token="{{ csrf_token() }}"></div>
fetch in javascript data value
var token = $( "#token" ).data('token');
$.ajax({
method: "POST",
url: "some.php",
data: { name: "John", location: "Boston",_token:token }
});
You said you use
$.ajaxSetup({ headers: { 'X-CSRF-Token' : $('meta[name=_token]').attr('content') } });
Maybe somewhere headers in request overrides, try to change it on
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token', $('meta[name=_token]').attr('content') );
}
});
Please modify your url variable like so :
url: '/my-route'+'?_token=' + '{{ csrf_token() }}',
I think it is not possible to cater this scenario (handing concurrent async requests) in session based CSRF implementation.
Consider 2 async request, r1 and r2:
both are sync and hit at the same time for same session on the CSRF Filter.
r1 changes the CSRF token value which is stored for session id and completes its implementation.
whereas r2 gets TOKEN_MISMATCH since, it has the same old token in the request header which is now get expired by r1 request completion.
So, for r2, CSRF filter will raise error.

How to provide the CSRF Token in single page application (spring security)?

When I try to load part of a page using ajax I got 403 error
Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
Spring Security FAQ tells us
If an HTTP 403 Forbidden is returned for HTTP POST, but works for HTTP
GET then the issue is most likely related to CSRF. Either provide the
CSRF Token or disable CSRF protection (not recommended).
So, how can a do this?
function getPage(url) {
$.ajax({
type: 'POST',
url: url,
data: {_csrf: "??"},
success: function (data) {
loadPage(url, data);
}
});
}
You can get the token from the cookie which is stored at your client. For that you have to use something like this cookie-service:
https://www.npmjs.com/package/angular2-cookie
write a function like this to get the token:
getCookie(){
return this._cookieService.get("token-name");
}
Finaly add the token to the request header:
doSomething(token){
var json = JSON.stringify({});
var headers = new Headers();
headers.append('Content-Type','application/json');
headers.append('token-name', token);
return this._http.post('http://localhost:8080/doSomething', json, {
headers: headers
}).map(res => res.text()
);
}
This solved the problem for me
Found an answer to my own question here:
http://spring.io/blog/2013/08/21/spring-security-3-2-0-rc1-highlights-csrf-protection/
this - to html
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
this - to js
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});

Resources