In our project library, AXIos is used as the network request library. In the development environment, after widthCredentials = true is configured, it is found that no cookies are carried in the network request header

The normal situation would be something like this

Request Headers GET /api.json HTTP/1.1 Host: api.topLevelDomain.com Cookie: XXXXXXCopy the code

About the XMLHttpRequest. WithCredentials: if before sending XHR requests from other domain, not set witCredentials = true, so can’t set the cookie value for its own domain. If it is set to true, third-party cookies obtained will still enjoy the same origin policy. Specific information is available for reference

First, I reviewed the code

Axios.defaults.withCredentials = true
// To be on the safe side, the request is initiated with the configuration item
await axiosInstance.get(url, {withCredentials: true})
Copy the code

The code is fine

Then confirm the domain name configured in the development environment

Dev.topLevelDomain.com and interface domain name are in the same level 1 domain name, so there is no problem with the domain name itself (why do you want to confirm that in the same level 1 domain, in fact, even if the relevant cross-domain policy is configured, IOS platform will probably fail? I will write a separate blog about this problem next)

Then check whether the server interface is configured across domains

After investigation, it is also correct

Response Headers HTTP/1.1 200 OK Server: nginx Content-Type: application/json; charset=utf-8 Access-Control-Allow-Origin: http://dev.topLevelDomain.com Access-Control-Allow-Credentials: trueCopy the code

In an uncertain situation,debugger

When the code is executed to the following number of lines, the configuration item does not take effect. The config. WithCredentials to false

Is it a bug in Axios itself?

    // axios/lib/adapters/xhr.js line 140
    // Add withCredentials to request if needed
    if (config.withCredentials) {
      request.withCredentials = true
    }
Copy the code

Is it the order of code execution?

Let’s take the top three lines of code up to the top

module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    var requestData = config.data
    var requestHeaders = config.headers

    if (utils.isFormData(requestData)) {
      delete requestHeaders['Content-Type'] // Let the browser set it
    }

    var request = new XMLHttpRequest()
    // Move here
    if (config.withCredentials) {
      request.withCredentials = true;
    }
    // HTTP basic authentication
Copy the code

After refreshing the page, this time it’s right, there’s a cookie in The Request Headers

Shouldn’t you? If there is a problem with the code order, someone has given feedback in the official github library issue. I have checked and found nothing

Is it the version? The current version of Axios is 0.18.0. Try upgrading to the latest version 0.21.1

Still can’t…….

This is strange, so I decided to try it out directly with XHR requests

usexhrtest

  const xhr = new XMLHttpRequest()
  xhr.open('GET',url)
  xhr.withCredentials = true
  xhr.send()
Copy the code

No, there is still no Cooke in the request header

Should the withCredentials configuration be incorrect after xhr.open?

Try changing the code order

  const xhr = new XMLHttpRequest()
  xhr.withCredentials = true
  xhr.open('GET',url)
  xhr.send()
Copy the code

Such, unexpectedly can!!

I am deeply skeptical of Chrome’s XHR at the moment ——–

Try the above two requests again in a different browser, same result for Firefox, Same result for IE11, same result for ios and Safari on mobile,

Impossible, so many browser manufacturers have not found this problem ~~~~

If you look at the MDN documentation, the official example also does not emphasize the issue of sequencing

At the moment I am in deep self-denial…….

I’ll give it a shot and start my own KOA service

Out of the current development environment, newkoaservice

  • With two host
172.0.0.1 dev.phillyx.com dev2.phillyx.com
Copy the code
  • akoaservice
module.exports = {
  'GET /credentials': async (ctx, next) => {
    try {
      ctx.set('Access-Control-Allow-Credentials'.true)
      ctx.set('Access-Control-Allow-Origin'.'http://dev2.phillyx.com')
      ctx.response.body = {
        code:1.msg:'widthCredentials: true'}}catch (error) {
      console.log(error)
      ctx.response.body = error
    }
  }
}
Copy the code
  • A separate request localhost: 3000 / credentials, ok code validation to pass

  • With the nginx proxy

http{ server{ listen 80; location / { root ./code; index index.html index.htm; } location /credentials { proxy_set_header X-Forwarded-For $proxy_add_x_Forwarded_for; Proxy_pass http://127.0.0.1:3000/credentials; proxy_set_header Host $host; }}}Copy the code
  • in/codeCreate a new one in the directorytest-credentials.html
<body>
  <script>
    (() = > {
      const xhr = new XMLHttpRequest()
      xhr.open('GET'.'http://dev.phillyx.com/credentials')
      xhr.withCredentials = true
      xhr.send()
    })()
  </script>
</body>
Copy the code
  • The initiatinghttp://dev2.phillyx.com/test-credentials.html
  • After the test andxhr.withCredentials = trueThe order has nothing to do
Response Headers HTTP/1.1 200 OK Server: nginx/1.19.2 Date: XXXX Content-Type: Application /json; charset=utf-8 Content-Length: 41 Connection: keep-alive Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://dev2.phillyx.com X-Response-Time: 1ms Cache-Control: No-store Request Headers GET /credentials HTTP/1.1 Host: dev.phillyx.com Connection: keep-alive Pragma: no-cache Cache-Control: no-cache User-Agent: xxxxx Accept: */* Origin: http://dev2.phillyx.com Referer: http://dev2.phillyx.com/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh; Q = 0.9 cookies: XXXXXXXCopy the code
  • There’s something wrong with the code

Why not start with the code call stack

Open the console ->initiator- > check the order of the calls, and sure enough

Are allmock.jsThe pot

The ghost found out, at the moment I do not know is the happy or sad ~

Why didn’t I think of that at first!!

  • The mock.js version in the project is1.1.0
  • Mock.js intercepts XHR globally
// line 9
var XHR
if (typeof window! = ='undefined') XHR = require('./mock/xhr')
// node_modules/mockjs/src/mock/mock.js
Mock.mock = function(rurl, rtype, template) {
    // ...
    // line 58
    / / intercept XHR
    if (XHR) window.XMLHttpRequest = XHR
    // ...
    return Mock
}

module.exports = Mock

// node_modules/mockjs/src/mock/xhr/xhr.js line244
setRequestHeader: function(name, value) {
    // ...
    // line 257
    withCredentials: false.// https://xhr.spec.whatwg.org/#the-send()-method
    // Initiates the request.
    send: function send(data) {
        / / native XHR
        if (!this.match) {
            this.custom.xhr.send(data)
            return
        }
Copy the code
  • Mock /issues/300 is also reported in official issues

  • The fix is as simple as deleting or adding a global configuration

Mock.XHR.prototype.withCredentials = true
Copy the code

But, why are xhr.withcredentials valid before the xhr.open() method

fix mock.js

  • As mentioned abovemock.jsGlobal hostingxhrWhen wevar xhr = new XMLHttpRequest(), the new isMockXMLHttpRequestobject
  • We performxhr.withCredentials = trueIn fact, the relationship is as follows
Obj. WithCredentials = true / / but obj. The prototype. The withCredentials = = = false / / this is in node_modules/mockjs/SRC/mock/XHR XHR. Js Line 257 withCredentials: false, // This. A > this.prototype.aCopy the code
  • The reasonxhr.withCredentials = trueWritten in the bookxhr.open()Previously valid because of line226 belowThe for loopAt work
  • By the same token,xhr.withCredentials = trueWritten in the bookxhr.open()After that, the process of assigning a custom attribute to a native is not performed
  • So fixing this bug is easy. MoveThe for looptosendMethod within the related condition branch
  // node_modules/mockjs/src/mock/xhr/xhr.js
  var XHR_REQUEST_PROPERTIES = 'timeout withCredentials'.split(' ')
  // line 167
  Util.extend(MockXMLHttpRequest.prototype, {
    // https://xhr.spec.whatwg.org/#the-open()-method
    // Sets the request method, request URL, and synchronous flag.
    open: function(method, url, async, username, password) {
    // ...
    // line211
    // If no matching data template is found, the request is sent using native XHR.
      if(! item) {// Create a native XHR object, call native open(), listen for all native events
        var xhr = createNativeXMLHttpRequest()
        this.custom.xhr = xhr
        // ...
        // line226 
        // Synchronize attribute MockXMLHttpRequest => NativeXMLHttpRequest
        // this.obj
        for (var j = 0; j < XHR_REQUEST_PROPERTIES.length; j++) {
            try {
                xhr[XHR_REQUEST_PROPERTIES[j]] = that[XHR_REQUEST_PROPERTIES[j]]
            } catch (e) {}
        }

        return}}Copy the code
  • Submitted to officialpull request