358

After logging in via $.ajax() to a site, I am trying to send a second $.ajax() request to that site - but when I check the headers sent using FireBug, there is no session cookie being included in the request.

What am I doing wrong?

Syscall
  • 16,959
  • 9
  • 22
  • 41
user345625
  • 3,691
  • 3
  • 14
  • 6
  • 2
    Cookie of ajax might come after the web cookie and FireBug might catch the first page cookie. – Chris May 20 '10 at 00:48
  • 1
    I did not get what u mean but I can say if I paste the request url in browser address bar and check Firebug again, I can see the cookie in headres sent to the server. Any solutions? – user345625 May 20 '10 at 01:04
  • So, I think ajax will also handle same way browser does – user345625 May 20 '10 at 01:05
  • What is the code you're using? – Dean Harding May 20 '10 at 01:06
  • the browser will still create cookies set by the server during a ajax request, jquery or otherwise. Did you check the response to the ajax request and ensure cookies came back from the server to be set? There could be a problem with the server code such that it is not even setting the cookie, etc. – David May 20 '10 at 01:27
  • to be honest, I had an "issue" like this last week, turns out I was using start_session; instead of start_session(); :( – Mark Robinson May 20 '10 at 07:56
  • Your question save my coding life ^^ - I have google search 'flask ajax request has separate session' and it leads me here. Thank you! – Nam G VU Sep 24 '17 at 13:30

11 Answers11

414

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

Kangur
  • 7,175
  • 3
  • 22
  • 27
  • 8
    Great ! I add to use this + set Access-Control-Allow-Credentials header to true on server side – Frédéric Camblor Nov 29 '12 at 12:26
  • and where do I set those Credentials ?, at the Header Autorization ?, at the request body ? – Francisco Corrales Morales Mar 20 '14 at 21:25
  • 3
    Thanks for the answer :) just a quick addition, it might be worth mentioning `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: *` https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials – Pebbl Aug 27 '14 at 16:52
  • None of this worked for me unfortunately. If I run the same request from AngularJS it works, but from jQuery, even with these suggestions the session Cookie is not passed. (jQuery v2.1.1) – geoidesic May 14 '16 at 18:29
  • (O.O) You saved me from various painful hours. What a perfect answer! Thanks! I needed to add these in public root .htaccess of my website: Header set Access-Control-Allow-Origin "http://localhost" Header set Access-Control-Allow-Credentials "true" – Vinay Vissh Jun 04 '17 at 19:10
  • I suppose, it is worth mentioning that if you override and provide your own $.ajaxSetup configuration in your code; it is potentially the cause why this might stop working all of a sudden. As others suggested here; then you need to add yourself `xhrFields` in ajax setup or in your jquery calls. HTH. – Senthu Sivasambu May 20 '18 at 15:18
234

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.

lord_t
  • 2,142
  • 2
  • 23
  • 48
flu
  • 13,059
  • 7
  • 69
  • 68
  • 21
    It's also worth noting that cookies can be set to a specific path so if you cookie was set with `path=/something` and you are requesting the page `/another` then the cookie will not be sent. When you request the page `/something` the cookie will be sent as expected. So check the code that sets the cookie as well. – styfle Jul 30 '13 at 20:45
  • 2
    does a jsonp request send coockies? – albanx Mar 23 '14 at 11:25
  • 1
    @albanx Yes, if the requirements I mentioned are set. It's just a normal request like any other and as such sends cookies. – flu Mar 24 '14 at 15:31
  • 1
    @albanx this [other related question](http://stackoverflow.com/a/14462615/251509) includes an example of how to do that JSONP request with custom cookies – AntonioHerraizS May 13 '14 at 16:17
  • 4
    According to [JSONP](https://en.wikipedia.org/wiki/JSONP) on Wikipedia > this approach was abandoned in favour of CORS – Peter Dotchev Nov 05 '15 at 09:05
  • Does the cookie-sending functionality is only specific to $.ajax() or it will also send the cookies when using vanilla js's ajax request (like XMLHttpRequest)? My understanding is you have to manually set the cookies header while preparing XMLHttpRequest. Please feel free to correct me if I'm wrong? – Humble Learner Nov 05 '15 at 21:16
  • styfle's comment is elaborated in http://stackoverflow.com/questions/12755499/how-to-change-jsessionid-cookie-path-to-server-root-in-spring-app-on-jetty. The solution 4 given there isn't dependent on Spring. – TAM Sep 04 '16 at 14:32
  • 1
    "The cookie/session info was NOT sent" making my login code stop working. I get stuck for hours without knowing that I'm browsing my web at `0.0.0.0:5000` and making AJAX request to `localhost:5000` and thinking they are the same. In fact they are NOT the same! Thank you for saving my coding-life – Nam G VU Sep 24 '17 at 13:32
50

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.

43

Put this in your init function:

$.ajaxSetup({
  xhrFields: {
    withCredentials: true
  }
});

It will work.

Daniel Kmak
  • 16,209
  • 7
  • 65
  • 83
Alex Athlan
  • 439
  • 4
  • 3
  • 1
    You saved my day! On the method level withCredentials didn't work for me. But globaly like this it finally works! Thanks. – Paulius Matulionis Apr 10 '15 at 10:54
  • be careful with it, because it will send cookies to all requests, ether for other domains(which is not expect this and fail request by require Access-Control-Allow-Credentials) – gdbdable Aug 23 '19 at 14:06
14

There are already a lot of good responses to this question, but I thought it may be helpful to clarify the case where you would expect the session cookie to be sent because the cookie domain matches, but it is not getting sent because the AJAX request is being made to a different subdomain. In this case, I have a cookie that is assigned to the *.mydomain.com domain, and I am wanting it to be included in an AJAX request to different.mydomain.com". By default, the cookie does not get sent. You do not need to disable HTTPONLY on the session cookie to resolve this issue. You only need to do what wombling suggested (https://stackoverflow.com/a/23660618/545223) and do the following.

1) Add the following to your ajax request.

xhrFields: { withCredentials:true }

2) Add the following to your response headers for resources in the different subdomain.

Access-Control-Allow-Origin : http://original.mydomain.com
Access-Control-Allow-Credentials : true
Community
  • 1
  • 1
munchbit
  • 511
  • 5
  • 10
11

After trying out the other solutions and still not getting it to work, I found out what the problem was in my case. I changed contentType from "application/json" to "text/plain".

$.ajax(fullUrl, {
    type: "GET",
    contentType: "text/plain",
    xhrFields: {
         withCredentials: true
    },
    crossDomain: true
});
Janno Teelem
  • 164
  • 1
  • 6
4

I was having this same problem and doing some checks my script was just simply not getting the sessionid cookie.

I figured out by looking at the sessionid cookie value in the browser that my framework (Django) was passing the sessionid cookie with HttpOnly as default. This meant that scripts did not have access to the sessionid value and therefore were not passing it along with requests. Kind of ridiculous that HttpOnly would be the default value when so many things use Ajax which would require access restriction.

To fix this I changed a setting (SESSION_COOKIE_HTTPONLY=False) but in other cases it may be a "HttpOnly" flag on the cookie path

wiwa
  • 59
  • 5
  • 3
    Do NOT do this. It allows client side script access to the session cookie which is the most common XSS attack vector. https://www.owasp.org/index.php/HttpOnly – Jason Elkin May 09 '17 at 14:42
  • Even when I do this, the browser won't send the session cookie lmao. – Displee May 25 '21 at 14:10
1

If you are developing on localhost or a port on localhost such as localhost:8080, in addition to the steps described in the answers above, you also need to ensure that you are not passing a domain value in the Set-Cookie header.
You cannot set the domain to localhost in the Set-Cookie header - that's incorrect - just omit the domain.

See Cookies on localhost with explicit domain and Why won't asp.net create cookies in localhost?

Community
  • 1
  • 1
jitin
  • 714
  • 7
  • 16
0

Just my 2 cents on setting PHPSESSID cookie issue when on localhost and under dev environment. I make the AJAX call to my REST API endpoint on the locahost. Say its address is mysite.localhost/api/member/login/ (virtal host on my dev environment).

  • When I do this request on Postman, things go fine and PHPSESSID is set with the response.

  • When I request this endpoint via AJAX from the Browsersync proxied page (e.g. from 122.133.1.110:3000/test/api/login.php in my browser address line, see the domain is different vs mysite.localhost) PHPSESSID does not appear among cookies.

  • When I make this request directly from the page on the same domain (i.e. mysite.localhost/test/api/login.php) PHPSESSID is set just fine.

So this is a cross-origin origin request cookies issue as mentioned in @flu answer above

Valentine Shi
  • 3,093
  • 2
  • 25
  • 35
0

Adding my scenario and solution in case it helps someone else. I encountered similar case when using RESTful APIs. My Web server hosting HTML/Script/CSS files and Application Server exposing APIs were hosted on same domain. However the path was different.

web server - mydomain/webpages/abc.html

used abc.js which set cookie named mycookie

app server - mydomain/webapis/servicename.

to which api calls were made

I was expecting the cookie in mydomain/webapis/servicename and tried reading it but it was not being sent. After reading comment from the answer, I checked in browser's development tool that mycookie's path was set to "/webpages" and hence not available in service call to

mydomain/webapis/servicename

So While setting cookie from jquery, this is what I did -

$.cookie("mycookie","mayvalue",{**path:'/'**});
Codeek
  • 1,606
  • 1
  • 9
  • 19
-5

Perhaps not 100% answering the question, but i stumbled onto this thread in the hope of solving a session problem when ajax-posting a fileupload from the assetmanager of the innovastudio editor. Eventually the solution was simple: they have a flash-uploader. Disabling that (setting

var flashUpload = false;   

in asset.php) and the lights started blinking again.

As these problems can be very hard to debug i found that putting something like the following in the upload handler will set you (well, me in this case) on the right track:

$sn=session_name();
error_log("session_name: $sn ");

if(isset($_GET[$sn])) error_log("session as GET param");
if(isset($_POST[$sn])) error_log("session as POST param");
if(isset($_COOKIE[$sn])) error_log("session as Cookie");
if(isset($PHPSESSID)) error_log("session as Global");

A dive into the log and I quickly spotted the missing session, where no cookie was sent.

  • I don't think the example above would work, because when there's no session cookie, what would the value of $sn be? (a random one, or maybe null), alternatively users could set session_name from a GET value for example `session_name(isset($_GET['sess']) ? $_GET['sess'] : null);session_start();` this way, they'd get a working thing – Steel Brain Dec 21 '14 at 02:30
  • That is exactly how i found the problem: no session when posting from this flash uploader thingy. As using a GET variable session identifier is a bad idea, and the cookie was not working, i threw it out. Who cares, flash is a thing of the past anyways. – Ellert van Koperen Dec 22 '14 at 12:22