pouchdb-authentication: Chrome Update 52: Response for preflight has invalid HTTP status code 405

My coworker just updated to Chrome 52 automatically.

After a lot of debugging we found out that this update apparently has an issue with pouchdb-authentication.

How to reproduce:

Use the login method of a remote db. This will result in the following exceptions:

index-browser.js:219 OPTIONS http://couchdb.garedu.com/_session xhRequest @ index-browser.js:219ajax$1 @ index-browser.js:238ajaxCore @ index-browser.js:332ajax @ index-browser.js:389(anonymous function) @ index.js:92(anonymous function) @ utils.js:66(anonymous function) @ utils.js:54(anonymous function) @ utils.js:35loginRemote @ DatabaseManager.js:116(anonymous function) @ DatabaseManager.js:150F @ _export.js:35initUserDbs @ DatabaseManager.js:147LoginView._this.login @ AuthenticationPage.jsx:47ReactErrorUtils.invokeGuardedCallback @ ReactErrorUtils.js:70executeDispatch @ EventPluginUtils.js:89executeDispatchesInOrder @ EventPluginUtils.js:112executeDispatchesAndRelease @ EventPluginHub.js:44executeDispatchesAndReleaseTopLevel @ EventPluginHub.js:55forEachAccumulated @ forEachAccumulated.js:25processEventQueue @ EventPluginHub.js:221runEventQueueInBatch @ ReactEventEmitterMixin.js:18handleTopLevel @ ReactEventEmitterMixin.js:29handleTopLevelImpl @ ReactEventListener.js:73perform @ Transaction.js:138batchedUpdates @ ReactDefaultBatchingStrategy.js:63batchedUpdates @ ReactUpdates.js:98dispatchEvent @ ReactEventListener.js:150 localhost/:1 XMLHttpRequest cannot load http://couchdb.garedu.com/_session. Response for preflight has invalid HTTP status code 405 reducers.js:16 new gareduMiner state: Object {settings: Object, status: Object, session: Object}

Would love to see if someone has the same issue.

As of now I do not know which change in Chrome breaks authentication. What I know is that replication with a database that does not need authentication still works fine.

In FF everything works fine as well. Downgrading Chrome alsow works.

I lack the in depth knowledge of PouchDB to efficiently fix this issue. I would love if someone would look into it.

Best regards

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 2
  • Comments: 31 (10 by maintainers)

Commits related to this issue

Most upvoted comments

As a workaround, you can install nginx as a reverse proxy in front of CouchDB and get it to strip out the header. You would then access CouchDB on 6984 instead of 5984 (this config also implements SSL):

server {

  listen 6984 ssl;

  server_name myserver.com;
  ssl_certificate /whereveriputmykeys/myserver.crt;
  ssl_certificate_key /whereveriputmykeys/myserver.key;

  location / {
    proxy_set_header Access-Control-Request-Headers "";
    proxy_pass http://127.0.0.1:5984;
  }

}

Many thanks to trolldx on the #couchdb Freenode channel for taking the time to help me figure out this annoying issue 👍

and a PR at https://github.com/apache/couchdb-chttpd/pull/135. I’d guess this is a low priority for CouchDB so please comment on the Jira ticket if it’s causing you problems.

Yes, I can also confirm that this is a bug on Chrome 52.0.2743.82 m, running on Windows 10 (64-bit). Firefox (47.0.1) also on the same platform works well, so this appears to be an issue with Chrome.

I found a workaround for my use case, so even though it’s incredibly hacky, I thought I’d share it.

The first step is to edit your CouchDB configuration. I added the custom x-hello header to my cors.headers. Why x-hello? Because I’m pretty sure it isn’t used for anything important.

Now, for any calls that are having this issue, you just need to pass in some ajax options:

{
  ajax: {
    headers: {
      'X-Hello': 'World'
    }
  }
}

Per-Database

Getting that pesky 405 a lot? In theory, this should fix the whole database! Unfortunately, PouchDB Authentication ignores these ajax options, so if your woes are auth-specific, you’ll have to sprinkle in some of the per-call magic in too.

var db = new PouchDB('http://localhost:5984/mydb', {
  ajax: {
    headers: {
      'X-Hello': 'World'
    }
  }
})

Per-Call

Don’t want to include a useless header all the time? Just add it when you need it! Most PouchDB calls take an optional options argument.

db.login('myuser', 'mypass', {
  ajax: {
    headers: {
      'X-Hello': 'World'
    }
  }
})

The Crazy Forking Option

It took a silly amount of debugging before I realized that PouchDB Authentication ignored the ajax options for the associated database. Since I didn’t want to pass in options all the time, I forked PouchDB Authentication. To get the db.login() method working (it was the one giving me 405 grief), I only had to change one line. I stopped meddling after that.

// Module: pouchdb-authentication@0.5.3
// File: lib/index.js
// Line: 91

// Before
}, opts.ajax || {});

// After
}, opts.ajax || {}, db.__opts.ajax || {});


Note: I’ve only tested this on Chrome 52.0.2743.116 on Mac 10.10.5. Your mileage may vary.

I’ve been looking into this a lot, and the problem is that as of Chrome 52, there is an empty Access-Control-Request-Headers header being sent along with the OPTIONS request. This causes CouchDB to deny the request as it can’t parse the contents (https://github.com/apache/couchdb-chttpd/blob/ab299ebfcb833a819ccc9ddff3a2074ee25e0523/src/chttpd_cors.erl#L102).

I recently stumbled accross this thing as well and have been playing around, trying to find a way to get around it. It seems to me that sending the login credentials as JSON already fixes the problem; it’d be nice if someone could confirm or refute this solution.

I have no idea why it actually makes a difference, but anyway, here we go:

// File: lib/index.js
// modify lines 89 and 90

86:   var ajaxOpts = utils.extend(true, {
87:     method : 'POST',
88:     url : utils.getSessionUrl(db),
89: *   headers : {'Content-Type': 'application/json'},
90: *   body : JSON.stringify({name: username, password: password})
91:   }, opts.ajax || {});
92:   utils.ajax(ajaxOpts, wrapError(callback));

Edit: tested in Chrome 52.0.2743.116 m, Windows 10, 1607 Still working in: FF 47.0.1 and Edge 38.14393.0.0

Thanks by the way for all your work, everybody!

We’ve hacked around this using the following nginx reverse proxy config. We tried @ccapndave’s header pass but weren’t able to get replication working, so decided to take the kitchen sink approach. This seems to be working for now. Eager to see couchdb fix this, given that we have no control over users’ browser updates.

location / {
      proxy_pass http://couchdb:5984;
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      if ($request_method = OPTIONS ) {
        add_header 'Access-Control-Allow-Origin' '$http_origin';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, HEAD, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Origin,Referer,X-Csrf-Token,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
      }
    }

Can someone please provide a very simple test case to reproduce this? I am unable to reproduce using Chrome 52 on a Mac.

The very best test here would be a Docker script to run the right version of CouchDB, then an HTML page containing PouchDB and demonstrating the error. Right now I’m not sure what steps I need to do to reproduce this.

The CouchDB code can certainly be changed to accept an empty header here (although not by me!) I’m not quite sure if anything can be done from the client side. Its possible that using the Fetch API to do the requests might have different/more reliable behaviour? Its also possible that its actually a Chrome bug and will be fixed at some point.