cloudant / nodejs-cloudant

Cloudant Node.js client library
Apache License 2.0
255 stars 90 forks source link

401 from Cloudant when require_valid_user=true #409

Closed smhdfdl closed 4 years ago

smhdfdl commented 4 years ago

Please read these guidelines before opening an issue.

Bug Description

Two related problems.

Switching on require_valid_user=true has stopped our clients connecting to Cloudant via the client library in this repo. The POST _session call always returns a 401, despite the correct username and password being provided (in the url or as separate params). A CURL equivalent works ok and returns 200. Here is the code for our call.

that.cloudantInit({ url: CDB_URL, plugins: cloudantRequestRetry }, function (err, cloudant) {...})

Given that CURL works, it must be something the client is doing. Note that we do not provide cookieauth as a plugin. However, when we switched on client debug, we could see that the cookieauth plugin was always being added, whatever value we specified for the plugins parameter (a function, empty array, omitted). It turns out that the following line of code is always executed, and is what is adding cookieauth...

https://github.com/cloudant/nodejs-cloudant/blob/master/cloudant.js#L300

This is in contradiction to the readme. When that line is disabled, clients connect ok with 200. But are unable to take advantage of session cookies.

So two problems: 1) cookieauth is always added, no way to stop that. 2) When cookieauth is added, clients get 401 when require_valid_user=true.

Catch-22.

We are approaching a code ship deadline, so urgently need a resolution to 1) and preferably 2) as well.

1. Steps to reproduce and the simplest code sample possible to demonstrate the issue

Set require_valid_user=true in Cloudant .ini file, start Cloudant, connect using the client, do not specify cookieauth plugin.

2. What you expected to happen

The client should connect with 200.

3. What actually happened

Client gets 401.

Environment details

ricellis commented 4 years ago

require_valid_user is not enabled/supported in the Cloudant service so I assume by Cloudant you really mean Cloudant Local if you've configured that option?

In any case:

  1. cookieauth is always added, no way to stop that.

That is true in the case of the initialization callback functionality. Historically that function was one way of enabling cookie auth (before that became the default), so for legacy compatibility it retains that behaviour. Sorry that the documentation is unclear around that function - we'll tidy it up to make it clear that there are additional side-effects to it.

So don't pass a callback when initializing the client and the cookieauth plugin won't get added to your plugins. If you want the equivalent check to validate your creds and connection, just call the ping function directly to get the root endpoint after initializing e.g.

const c = new Cloudant(opts);
c.ping()
  .then(resp => console.log(resp))
  .catch(err => console.log(err));
  1. When cookieauth is added, clients get 401 when require_valid_user=true

This is because that setting requires auth on all endpoints (including bizarrely the _session one, which you ought to be able to use to auth...). Cloudant doesn't support this option so we never engineered a workaround to this into the client libraries and really a solution is required in CouchDB. Given the solution to 1, disabling cookieauth and falling back to Basic auth should get you past this problem.

domstorey commented 4 years ago

Thanks @ricellis :-) The workaround is looking to be working for us. If you let us do some more local and cloud testing we can then close this issue.

Do you think there is any improvement you can make here? As you say maybe doc updates will be good enough here so no one else trips up.

ricellis commented 4 years ago

I'll leave this ticket open for documenting the side-effect of that initialization callback - I think that is a must.

Beyond that I poked the issue in Apache CouchDB, so I'm hopeful we'll see a resolution upstream.

davidrac commented 4 years ago

I'm using local cloudant without the callback functionality and I'm getting this error:

const cloudant = new Cloudant({
  account,
  password,
  url
});

I'm getting:

Error: Failed to get cookie. Status code: 401

Any workaround available?

ricellis commented 4 years ago

@davidrac - presumably you are also using require_valid_user=true in your server configuration.

The cookieauth plugin is enabled by default - disable it by declaring an empty plugins array as described in Using multiple plugins.

davidrac commented 4 years ago

Thanks, it worked!

ghost commented 4 years ago

@ricellis I don't understand. Without require_valid_user=true then any requests are available to the public correct? How do you require authentication without using require_valid_user?

ricellis commented 4 years ago

Without require_valid_user=true then any requests are available to the public correct?

Not exactly, the exact semantics are disallowing requests from anonymous users, but some actions are only available to users with certain roles. So "any requests" only applies in the case that require_valid_user=false and the server is running in "admin party" (i.e. no configured server admins so any user is an admin).

How do you require authentication without using require_valid_user?

At the most basic level you configure server administrators and configure database security. Only server administrators have access to endpoints like creating databases, I think CouchDB still leaves some endpoints exposed that might not be considered "safe" (possibly _all_dbs). If you are exposing CouchDB directly then require_valid_user is probably a good idea. I think it is more typical to put CouchDB behind a proxy and in those cases require_valid_user might be inconvenient when, for example, anonymous access to / or /_up for healthchecks is useful.

FWIW Admin party is going away in CouchDB 3.0 and all databases will be created as admin_only by default - this stops as much configuration being necessary to get to a more secure position.

If you have more questions about securely configuring CouchDB I suggest the couch-users mailing list or slack (links from https://couchdb.apache.org/).

This is all really outside the scope of nodejs-cloudant, but just to bring it back into relevance here I will say that the issue in CouchDB that prevented users authenticating to the _session endpoint without presenting the auth details twice has now been resolved and will be included in CouchDB 3.0 so it should be no problem to use both the cookieauth plugin and require_valid_user=true from that release.