I have two subdomais "api.domain.com" and "web.domain.com".
Now "web.domain.com" is web page written in html/javascript and "api.domain.com" is a simple restful API server written in php.
"api.domain.com" sets certain cookies in the header as follows
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Credentials: true");
setcookie("TestCookie", "Some Value", time()+3600, "/", ".domain.com", 0);
Now, when I make an ajax call (using jQuery.ajax() ) from "web.domain.com" to "api.domain.com", the response headers contain
Set-Cookie:abc=802691344656c1d0899c4a74.87956617; expires=Mon, 16-May-2016 21:00:09 GMT; path=/; domain=domain.com,
so i guess a cookie should be set in the client browser at "web.domain.com".
The next time I make another request to "api.domain.com" from "web.domain.com", shouldn't this cookie go as part of the request headers?
However, when I inspect the $_COOKIE array at "api.domain.com", i don't see this cookie! Does that mean the cookie never got set in the client ("web.domain.com") at the first place? What am I doing wrong?
Using the withCredentials header (as suggested by #charlietfl) worked for me. I had to make one more modification in the server as well.
So here's what I did.
In web.domain.com , while maqking the Ajax request, I added withCredentials: true , like this
$.ajax({
// The Url for the request
url : ajaxUrl,
// The data to send (will be converted to a query string)
data : ajaxData,
xhrFields: {
// To allow cross domain cookies
withCredentials: true
},
...
});
In api.domain.com , I set some headers like this :
header("Access-Control-Allow-Origin: *");
However, I was still unable to get any response. I got this error instead
Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
So i simply set the header to the origin domain, like so :
$http_origin = $_SERVER['HTTP_ORIGIN'];
if (substr($input, -10) == 'domain.com') { // To check if request is always from a subdomain of 'domain.com'
header("Access-Control-Allow-Origin: $http_origin");
}
That fixed the issue.
Related
I have the following http get , that I would like to send a cookie with a language on the request, but turns out with $cookieStore, it sends the cookie outside of the request
$cookieStore.put("language", "pt-PT");
I tried this as well
var myObject = {
headers: { 'language': 'pt-PT'}
}//ignored
return $http.get(comm.endpoints.getEntityFinancialPosition, myObject );
In my debug I can see that this just created a new header that is not sent inside the cookie
The scope of the cookie is restricted with the domain option. If page domain is different than API domain cookie will not be sent when making a request. To fix that set the right domain for the cookie $cookieStore.put("language", "pt-PT", {domain: 'requestdomain.com'}).
I am working on an internal web application at work. In IE10 the requests work fine, but in Chrome all the AJAX requests (which there are many) are sent using OPTIONS instead of whatever defined method I give it. Technically my requests are "cross domain." The site is served on localhost:6120 and the service I'm making AJAX requests to is on 57124. This closed jquery bug defines the issue, but not a real fix.
What can I do to use the proper http method in ajax requests?
Edit:
This is in the document load of every page:
jQuery.support.cors = true;
And every AJAX is built similarly:
var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
url: url,
dataType: "json",
data: json,
async: true,
cache: false,
timeout: 30000,
headers: { "x-li-format": "json", "X-UserName": userName },
success: function (data) {
// my success stuff
},
error: function (request, status, error) {
// my error stuff
},
type: "POST"
});
Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. If you're doing this cross-domain, you will simply have to deal with it or else find a way to make the request non-cross-domain. This is why the jQuery bug was closed as won't-fix. This is by design.
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP request by the OPTIONS method to the resource on the
other domain, in order to determine whether the actual request is safe
to send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
Based on the fact that the request isn't sent on the default port 80/443 this Ajax call is automatically considered a cross-origin resource (CORS) request, which in other words means that the request automatically issues an OPTIONS request which checks for CORS headers on the server's/servlet's side.
This happens even if you set
crossOrigin: false;
or even if you ommit it.
The reason is simply that localhost != localhost:57124. Try sending it only to localhost without the port - it will fail, because the requested target won't be reachable, however notice that if the domain names are equal the request is sent without the OPTIONS request before POST.
I agree with Kevin B, the bug report says it all. It sounds like you are trying to make cross-domain ajax calls. If you're not familiar with the same origin policy you can start here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript.
If this is not intended to be a cross-domain ajax call, try making your target url relative and see if the problem goes away. If you're really desperate look into the JSONP, but beware, mayhem lurks. There really isn't much more we can do to help you.
If it is possible pass the params through regular GET/POST with a different name and let your server side code handles it.
I had a similar issue with my own proxy to bypass CORS and I got the same error of POST->OPTION in Chrome. It was the Authorization header in my case ("x-li-format" and "X-UserName" here in your case.) I ended up passing it in a dummy format (e.g. AuthorizatinJack in GET) and I changed the code for my proxy to turn that into a header when making the call to the destination. Here it is in PHP:
if (isset($_GET['AuthorizationJack'])) {
$request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
In my case I'm calling an API hosted by AWS (API Gateway). The error happened when I tried to call the API from a domain other than the API own domain. Since I'm the API owner I enabled CORS for the test environment, as described in the Amazon Documentation.
In production this error will not happen, since the request and the api will be in the same domain.
I hope it helps!
As answered by #Dark Falcon, I simply dealt with it.
In my case, I am using node.js server, and creating a session if it does not exist. Since the OPTIONS method does not have the session details in it, it ended up creating a new session for every POST method request.
So in my app routine to create-session-if-not-exist, I just added a check to see if method is OPTIONS, and if so, just skip session creating part:
app.use(function(req, res, next) {
if (req.method !== "OPTIONS") {
if (req.session && req.session.id) {
// Session exists
next();
}else{
// Create session
next();
}
} else {
// If request method is OPTIONS, just skip this part and move to the next method.
next();
}
}
"preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Consider using axios
axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {
if(res.data.error) {
} else {
doAnything( res.data )
}
}).catch(function (error) {
doAnythingError(error)
});
I had this issue using fetch and axios worked perfectly.
I've encountered a very similar issue. I spent almost half a day to understand why everything works correctly in Firefox and fails in Chrome. In my case it was because of duplicated (or maybe mistyped) fields in my request header.
Use fetch instead of XHR,then the request will not be prelighted even it's cross-domained.
$.ajax({
url: '###',
contentType: 'text/plain; charset=utf-8',
async: false,
xhrFields: {
withCredentials: true,
crossDomain: true,
Authorization: "Bearer ...."
},
method: 'POST',
data: JSON.stringify( request ),
success: function (data) {
console.log(data);
}
});
the contentType: 'text/plain; charset=utf-8', or just contentType: 'text/plain', works for me!
regards!!
I make AJAX calls to CORS server, I make many attempts to be able to read cookies returned on response using javascript client side, however, in vain .
1. First Attempt :
- Server Side (node.js powered by express) :
response.header('Access-Control-Allow-Origin', '*');
response.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Set-Cookie');
response.header('Access-Control-Expose-Headers', "Set-Cookie");
//------SET COOKIES
response.cookie('SessionId', GeneratorId(64), {
maxAge:3600000,
httpOnly:flase // i disable httpOnly ==> does not work
});
- client Side :
var xhttp=new XMLHttpRequest();
xhttp.open("POST", "http://localhost:9090/api/map", true);
xhttp,send(`{layer:1}`);
2. Second Attempt: (withCredentials)
-Server Side :
//Append another response' header
response.header('Access-Control-Allow-Credentials','true');
-Client Side :
// Before xhttp.send , I add another instruction :
xhttp.withCredentials=true;
3. Third Attempt :
- Server Side :
//Avoid the wildcard on Access-Control-Allow-Origin =>Replace the first header by :
response.header('Access-Control-Allow-Origin', request.get('Origin'));
- Client Side :
// Nothing is appended
Conclusion :
With all those attempts , xhttp.getResponseHeader('Set-Cookie') still returns null even :
Set-Cookie is assigned to the response header : Access-Control-Expose-Headers .
I saw cookies on the browser console (Inspector) :
TL;DR: the Set-Cookie header is entirely off-limits: you can't access it, even when you include it in Access-Control-Expose-Headers. However, once it's set, and the cookie isn't marked httpOnly, you should be able to access it through document.cookie.
TMI follows:
As documented here,
A response will typically get its CORS-exposed header-name list set by parsing the Access-Control-Expose-Headers header. This list is used by a CORS filtered response to determine which headers to expose.
What a CORS filtered response is, is documented here:
A CORS filtered response is a filtered response whose type is "cors", header list excludes any headers in internal response's header list whose name is not a CORS-safelisted response-header name, given internal response's CORS-exposed header-name list, and trailer is empty.
And safelisted headers are subsequently documented here:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
Any value in list that is not a forbidden response-header name.
Finally, the list of forbidden response-header names is listed here:
Set-Cookie
Set-Cookie2
I am trying to make a POST request to the server (Which is a REST service)via javascript,and in my request i want to send a cookie.My below code is not working ,as I am not able to receive cookie at the server side.Below are my client side and server side code.
Client side :
var client = new XMLHttpRequest();
var request_data=JSON.stringify(data);
var endPoint="http://localhost:8080/pcap";
var cookie="session=abc";
client.open("POST", endPoint, false);//This Post will become put
client.setRequestHeader("Accept", "application/json");
client.setRequestHeader("Content-Type","application/json");
client.setRequestHeader("Set-Cookie","session=abc");
client.setRequestHeader("Cookie",cookie);
client.send(request_data);
Server Side:
public #ResponseBody ResponseEntity getPcap(HttpServletRequest request,#RequestBody PcapParameters pcap_params ){
Cookie cookies[]=request.getCookies();//Its coming as NULL
String cook=request.getHeader("Cookie");//Its coming as NULL
}
See the documentation:
Terminate these steps if header is a case-insensitive match for one of the following headers … Cookie
You cannot explicitly set a Cookie header using XHR.
It looks like you are making a cross origin request (you are using an absolute URI).
You can set withCredentials to include cookies.
True when user credentials are to be included in a cross-origin request. False when they are to be excluded in a cross-origin request and when cookies are to be ignored in its response. Initially false.
Such:
client.withCredentials = true;
This will only work if http://localhost:8080 has set a cookie using one of the supported methods (such as in an HTTP Set-Cookie response header).
Failing that, you will have to encode the data you wanted to put in the cookie somewhere else.
This can also be done with the more modern fetch
fetch(url, {
method: 'POST',
credentials: 'include'
//other options
}).then(response => console.log("Response status: ", response.status));
This is the story of a bird who wants to work for the post but fails during his preflight test...
App built with Laravel being used as a RESTful API and AngularJS/ionic.
My API calls were working fine until...for an unknown reason it stopped.
Although I set the withCredentials for the angularJS side of the call, the preflight OPTIONS are not sending a cookie but I am receiving one back from Laravel. How can we disable OPTIONS to return a cookie laravel_session?
It messes up the CORS as it sets a new session which will obviously be different on every POST.
For Laravel side I use the package Laravel/CORS from #barryvdh with the following configuration:
'*' => array(
'supportsCredentials' => true,
'allowedOrigins' => array('*'),
'allowedHeaders' => array('*'),
'allowedMethods' => array('POST', 'PUT', 'GET', 'PATCH', 'OPTIONS', 'DELETE'),
'maxAge' => 36000,
'hosts' => array('api.*'),
)
On the Angular side I have the following:
$http({
method: 'POST',
url: 'http://api.blabla.local/banana',
data: data,
withCredentials: true
})
My GET calls work fine and I have one running at start of the app to fetch the CSRF from laravel that I send back when needed.
Right now the following happens:
1. Preflight OPTIONS > request has no cookies for the session. Reponse = 200 with a different session cookie which will cause the CSRF to cause all the time. [thoughts: the withCredentials does not work with the OPTIONS call]
2. POST > fails with 500, in the headers I see no response but it did send the cookie/session [thoughts: credentials are passed to it but they are also the wrong ones since they have changed on server side because of the preflight option]. Error message says it is not authorized origin.
What's going on? I've been trying for hours now and checked a lot of other posts but nothing seems to help! Can I get rid of the preflight, how? Or is the problem somewhere else (server side I'm using Laravel Homestead)?
I feel that the real issue is that the OPTIONS returns a session cookie or simply that the request does include one!
Thanks for your help, I've been stuck for hours and I'm going crazzy on that...
In the filters.php under L4.2 I ended up using this:
The problem is old so not sure it's the only thing I did but looks like it:
App::before(function($request)
{
//
// Enable CORS
// In production, replace * with http://yourdomain.com
header("Access-Control-Allow-Origin: http://mydomain.local");
header('Access-Control-Allow-Credentials: true'); //optional
if (Request::getMethod() == "OPTIONS") {
// The client-side application can set only headers allowed in Access-Control-Allow-Headers
$headers = [
'Access-Control-Allow-Methods'=> 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers'=> 'Content-Type'
];
return Response::make('You are connected to the API', 200, $headers);
}
});
App::after(function($request, $response)
{
//
});
JWT could be good for ionic and angular..
Check http://packalyst.com/packages/package/tymon/jwt-auth
also https://www.youtube.com/watch?v=vIGZxeQUUFU