Why is jquery's .ajax() method not sending my session cookie?

I am operating in cross-domain scenario. During login remote server is returning Set-Cookie header along with Access-Control-Allow-Credentials set to true.

The next ajax call to remote server should use this cookie.

CORS's Access-Control-Allow-Credentials is there to allow cross-domain logging. Check https://developer.mozilla.org/En/HTTP_access_control for examples.

For me it seems like a bug in JQuery (or at least feature-to-be in next version).

UPDATE:

  1. Cookies are not set automatically from AJAX response (citation: http://aleembawany.com/2006/11/14/anatomy-of-a-well-designed-ajax-login-experience/)

    Why?

  2. You cannot get value of the cookie from response to set it manually (http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-getresponseheader)

    I'm confused..

    There should exist a way to ask jquery.ajax() to set XMLHttpRequest.withCredentials = "true" parameter.

ANSWER: You should use xhrFields param of http://api.jquery.com/jQuery.ajax/

The example in the documentation is:

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
      withCredentials: true
   }
});

It's important as well that server answers correctly to this request. Copying here great comments from @Frédéric and @Pebbl:

Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: *

So when the request is:

Origin: http://foo.example
Cookie: pageAccess=2

Server should respond with:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true

[payload]

Otherwise payload won't be returned to script. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials


AJAX calls only send Cookies if the url you're calling is on the same domain as your calling script.

This may be a Cross Domain Problem.

Maybe you tried to call a url from www.domain-a.com while your calling script was on www.domain-b.com (In other words: You made a Cross Domain Call in which case the browser won't sent any cookies to protect your privacy).

In this case your options are:

  • Write a small proxy which resides on domain-b and forwards your requests to domain-a. Your browser will allow you to call the proxy because it's on the same server as the calling script.
    This proxy then can be configured by you to accept a cookie name and value parameter which it can send to domain-a. But for this to work you need to know the cookie's name and value your server on domain-a wants for authentication.
  • If you're fetching JSON objects try to use a JSONP request instead. jQuery supports these. But you need to alter your service on domain-a so that it returns valid JSONP responds.

Glad if that helped even a little bit.


Using

xhrFields: { withCredentials:true }

as part of my jQuery ajax call was only part of the solution. I also needed to have the headers returned in the OPTIONS response from my resource:

Access-Control-Allow-Origin : http://www.wombling.com
Access-Control-Allow-Credentials : true

It was important that only one allowed "origin" was in the response header of the OPTIONS call and not "*". I achieved this by reading the origin from the request and populating it back into the response - probably circumventing the original reason for the restriction, but in my use case the security is not paramount.

I thought it worth explicitly mentioning the requirement for only one origin, as the W3C standard does allow for a space separated list -but Chrome doesn't! http://www.w3.org/TR/cors/#access-control-allow-origin-response-header NB the "in practice" bit.