17

A javascript application running on 10.0.0.1 tries to authenticate it's users with cross-domain ajax calls.

The request looks like:

function test(again){
  $.ajax({
    type: 'GET',
    url: 'http://example.com/userinfo',
    dataType: 'json',
    success: function(userinfo){
      if(again)
        test(false);}});}
test(true);

The first response from the server tries to set a cookie:

Access-control-allow-origin:http://10.0.0.1
Set-Cookie:PHPSESSID=uuj599r4k1ohp48f1poobil665; expires=Sat, 28-Jan-2012 17:10:40 GMT; path=/

But the second request does not include this cookie, nor do any other ajax requests to that domain.

I am not trying to read the cookie for another domain, I just want the application on the other domain to be able to set and read its own cookie.

Is this possible?

I have tested in Chrome and Firefox 9.

Etienne Laurin
  • 6,731
  • 2
  • 27
  • 31

5 Answers5

11

server should set header:

response.Headers.Add("Access-Control-Allow-Credentials", "true");

client set to:

xhrFields: {
  withCredentials: true
}
Danpe
  • 18,668
  • 21
  • 96
  • 131
Teddy
  • 787
  • 5
  • 13
10

As long as you are using a browser which supports CORS, cookies on the AJAX request should work. But you must set withCredentials on the XMLHttpRequest to true.

See: The withCredentials attribute

I don't use JQuery but here's a question that deals specifically with setting withCredentials via JQuery.

Sending credentials with cross-domain posts?

Community
  • 1
  • 1
Debby Mendez
  • 691
  • 10
  • 16
  • 1
    For clarification, the documentation at [developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) states this about using requests with credentials: "when responding to a credentialed request, server must specify a domain, and cannot use wild carding." So make sure your `Access-Control-Allow-Origin` header contains the actual domain where the request is coming from, and not an `*`. – John Washam May 28 '15 at 14:45
2

No, cookies cannot be shared cross domain. The same origin policy could be circumvented for AJAX calls using the Access-Control-* headers assuming the browser supports them, but for cookies there's no way.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Thanks for your input. However I am not trying to share cookies cross-domain. I just want to share cookies between requests to the same domain. If that is what you mean, please clarify. – Etienne Laurin Jan 13 '12 at 17:56
  • @atnnn it that case it should work, are you 100% sure it doesn't? Make sure that your subsequent request is to the exact same domain, and not a subdomain or superdomain. – DaveRandom Jan 13 '12 at 18:00
  • If you look at my code, you will see that it is the exact same request, and there is no race condition. – Etienne Laurin Jan 13 '12 at 18:02
  • @atnnn, I am looking at your code and I see 2 different domains: `http://10.0.0.1` and `http://example.com/userinfo`. You cannot possibly expect to share cookies between those domains. – Darin Dimitrov Jan 13 '12 at 18:04
  • 1
    The application running on 10.0.0.1 does two ajax requests to another domain. The second request to that other domain does not include the cookie for that domain. I am not trying to share cookies between domains at all. – Etienne Laurin Jan 13 '12 at 18:09
  • 1
    @atnnn, I suspect that the cookie is not saved by the browser because it comes from another domain than the one hosting the page which is at the origin of this call. That's the reason why it is not sent along with the second request. – Darin Dimitrov Jan 13 '12 at 18:10
0

I needed to pass cookies from multiple subdomains to a single API domain using AJAX and PHP.

This was the challenge and solution:

1 - Backend PHP on api.example.com.

2 - Multiple JS front ends such as one.example.com, two.example.com etc.

3 - Cookies needed to be passed both ways.

4 - AJAX call from multiple front-ends to PHP backend on api.example.com

5 - In PHP, I do not prefer to use $_SERVER["HTTP_ORIGIN"], not always reliable/safe in my opinion (I had some browsers where HTTP-ORIGIN was always empty).

The normal way to do this in PHP with single front end domain is starting PHP code with:

header('Access-Control-Allow-Origin: https://one.example.com');  
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');  
header('Access-Control-Allow-Credentials: true');  

And in JS on one.example.com domain:

jQuery.ajax({
    url: myURL,
    type: "POST",
    xhrFields: {withCredentials: true},
    dataType: "text",
    contentType: "text/xml; charset=\"utf-8\"",
    cache: false,
    headers: "",
    data: myCallJSONStr,
    success: function(myResponse) {.....}

However, this is not workable as I am using multiple subdomains to call my API domain.

And this solution will NOT work as I want to pass on cookies:

header('Access-Control-Allow-Origin: *');  

It conflicts with the pass on cookie setting on the JS site:

xhrFields: {withCredentials: true}

Here is what I did:

1 - use GET parameter to pass the Subdomain.

2 - Hardcode the Main domain in PHP so only (all) Subdomains are allowed.

This is the JS/JQuery AJAX part of my solution:

function getSubDomain(){

let mySubDomain = "";

let myDomain = window.location.host;
let myArrayParts = myDomain.split(".");
if (myArrayParts.length == 3){
    mySubDomain = myArrayParts[0];
}

return mySubDomain;

}

And in the AJAX call:

let mySubDomain = getSubDomain();
if (mySubDomain != ""){
    myURL += "?source=" + mySubDomain + "&end"; //use & instead of ? if URL already has GET parameters
}

jQuery.ajax({
    url: myURL,
    type: "POST",
    xhrFields: {withCredentials: true},
    dataType: "text",
    contentType: "text/xml; charset=\"utf-8\"",
    cache: false,
    headers: "",
    data: myCallJSONStr,
    success: function(myResponse) {.....}

Finally, the PHP part:

<?php

$myDomain = "example.com";
$mySubdomain = "";

if (isset($_GET["source"])) {
    $mySubdomain = $_GET["source"].".";
}

$myDomainAllowOrigin = "https://".$mySubdomain.$myDomain;
$myAllowOrigin = "Access-Control-Allow-Origin: ".$myDomainAllowOrigin;

//echo $myAllowOrigin;

header($myAllowOrigin);  
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');  
header('Access-Control-Allow-Credentials: true');

IMPORTANT, don't forget to set the cookies for all subdomains, in this case the domain for the cookie would be: .example.com (so with a dot in front of the main domain):

<?php

    //////////////// GLOBALS /////////////////////////////////
    
    $gCookieDomain = ".example.com";
    $gCookieValidForDays = 90;
    
    //////////////// COOKIE FUNTIONS /////////////////////////////////
    
    function setAPCookie($myCookieName, $myCookieValue, $myHttponly){
        global $gCookieDomain;
        global $gCookieValidForDays;
        
        $myExpires = time()+60*60*24*$gCookieValidForDays;
        setcookie($myCookieName, $myCookieValue, $myExpires, "/", $gCookieDomain, true, $myHttponly);   
        
        return $myExpires;
    }

This solution allows me to call the API on api.example.com from any subdomains on example.com.

NB. for situation where there is only a single calling subdomain, I prefer using .htaccess for setting CORS instead of PHP. Here is an example of .htaccess (linux/apache) for only one.example.com calling api.example.com:

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "https://one.example.com"
    Header set Access-Control-Allow-Headers "Origin, Content-Type, X-Auth-Token"
    Header set Access-Control-Allow-Credentials "true"
</IfModule>

And place this .htaccess in the root of api.example.com.

Al-Noor Ladhani
  • 2,413
  • 1
  • 22
  • 14
0

+Darin Dimitrov suspects that "the cookie is not saved by the browser because it comes from another domain than the one hosting the page which is at the origin of this call".

However, the cookie gets set as desired when using JSONP, but JSONP is only for GET requests.

My solution is to retrieve the cookie (a PHP session id) by loading the following php file in a <script>:

<? echo $_GET['callback'] . '("' . session_id() . '")'; ?>

And to pass the session id as a request variable in all cross-domain POST requests.

Etienne Laurin
  • 6,731
  • 2
  • 27
  • 31