Why is the browser not setting cookies after an AJAX request returns?

前端 未结 5 1483
走了就别回头了
走了就别回头了 2020-11-29 00:35

I am making an ajax request using $.ajax. The response has the Set-Cookie header set (I\'ve verified this in the Chrome dev tools). However, the browser does

相关标签:
5条回答
  • 2020-11-29 01:09

    This may help somebody randomly falling across this question.

    I found forcing a URL with https:// rather than http:// even though the server hasn't got a certificate and Chrome complains will fix this issue.

    0 讨论(0)
  • 2020-11-29 01:19

    If you're using the new fetch API, you can try including credentials:

    fetch('/users', {
      credentials: 'same-origin'
    })
    

    That's what fixed it for me.

    In particular, using the polyfill: https://github.com/github/fetch#sending-cookies

    0 讨论(0)
  • 2020-11-29 01:20

    @atomkirk's answer didn't quite apply to me because

    1. I don't use the fetch API
    2. I was making cross-site requests (i.e. CORS)

    But the answer helped me learn these points:

    fetch API CORS requests needs {credentials:'include'} for both sending & receiving cookies

    For CORS requests, use the "include" value to allow sending credentials to other domains:

    fetch('https://example.com:1234/users', {   
                credentials: 'include' 
    })
    

    ... To opt into accepting cookies from the server, you must use the credentials option.


    {credentials:'include'} just sets xhr.withCredentials=true

    Check fetch code

    if (request.credentials === 'include') {
          xhr.withCredentials = true
     } 
    

    So plain Javascript/XHR.withCredentials is the important part.


    If you're using jQuery, you can set withCredentials using $.ajaxSetup(...)

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

    If you're using AngularJS, the $http service config arg accepts a withCredentials property:

    $http({
        withCredentials: true
    });
    

    If you're using Angular (Angular IO), the common.http.HttpRequest service options arg accepts a withCredentials property:

    this.http.post<Hero>(this.heroesUrl, hero, {
        withCredentials: true
    });
    

    As for the request, when xhr.withCredentials=true; the Cookie header is sent

    Before I changed xhr.withCredentials=true

    1. I could see Set-Cookie name & value in the response, but Chrome's "Application" tab in the Developer Tools showed me the name and an empty value
    2. Subsequent requests did not send a Cookie request header.

    After the change xhr.withCredentials=true

    1. I could see the cookie's name and the cookie's value in the Chrome's "Application" tab (a value consistent with the Set-Cookie header).
    2. Subsequent requests did send a Cookie request header with the same value, so my server treated me as "authenticated"

    As for the response: the server may need certain Access-Control-* headers

    For example, I configured my server to return these headers:

    • Access-Control-Allow-Credentials:true
    • Access-Control-Allow-Origin:https://{your-origin}:{your-port}

    Until I made this server-side change to the response headers, Chrome logged errors in the console like

    Failed to load https://{saml-domain}/saml-authn: Redirect from https://{saml-domain}/saml-redirect has been blocked by CORS policy:

    The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin https://{your-domain} is therefore not allowed access.

    The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

    After making this Access-* header change, Chrome did not log errors; the browser let me check the authenticated responses for all subsequent requests.

    0 讨论(0)
  • 2020-11-29 01:28

    In my case, the cookie size exceeded 4096 bytes (Google Chrome). I had a dynamic cookie payload that would increase in size.

    Browsers will ignore the set-cookie response header if the cookie exceeds the browsers limit, and it will not set the cookie.

    See here for cookie size limits per browser.

    I know this isn't the solution, but this was my issue, and I hope it helps someone :)

    0 讨论(0)
  • 2020-11-29 01:29

    OK, so I finally figured out the problem. It turns out that setting the Path option is important when sending cookies in an AJAX request. If you set Path=/, e.g.:

    Set-Cookie:SessionId=foo; Path=/; HttpOnly
    

    ...then the browser will set the cookie when you navigate to a different page. Without setting Path, the browser uses the "default" path. Apparently, the default path for a cookie set by an AJAX request is different from the default path used when you navigate to a page directly. I'm using Go/Martini, so on the server-side I do this:

    session.Options(session.Options{HttpOnly: true, Path:"/"})
    

    I'd guess that Python/Ruby/etc. have a similar mechanism for setting Path.

    See also: cookies problem in PHP and AJAX

    0 讨论(0)
提交回复
热议问题