pouchdb-community / pouchdb-authentication

User authentication plugin for PouchDB and CouchDB.
Apache License 2.0
775 stars 118 forks source link

Cannot authenticate after page-reload -> Session not working #245

Open juliusge opened 6 years ago

juliusge commented 6 years ago

Expected Behavior

When I log in and reload the page, I should remain logged in.

Current Behavior

I use pouchdb-authentication in my Cordova-App. Login/Register works just fine, but a user cannot stay logged in after a page-reload. So something with this plugin's cookie-auth seems to fail. I use Couch-Per-User functionality with CouchDB 2.2.

When I log in on Chrome, I receive a session-object as expected: {"ok":true,"userCtx":{"name":"julius","roles":[]},"info":{"authentication_db":"_users","authentication_handlers":["cookie","default"],"authenticated":"default"}} Also, I can sync, put docs, etc. When I reload the page, read out the user's name and database-name from the local document, I receive a 401 "unauthorized" error:

pouchdb-7.0.0.min.js:7 GET https://domain.com:6984/userdb-6a756c697573/ 401 (Unauthorized)

pouchdb-7.0.0.min.js:7 GET https://domain.com:6984/userdb-6a756c697573/_local/iT08DXWHT8EcU67wHjmYdA%3D%3D? 401 (Unauthorized)

and the following DB/Session-objects: {"error":"unauthorized","reason":"You are not authorized to access this db.","host":"https://domain.com:6984/userdb-6a756c697573/","db_name":"https://domain.com:6984/userdb-6a756c697573","auto_compaction":false,"adapter":"https"}

{"ok":true,"userCtx":{"name":null,"roles":[]},"info":{"authentication_db":"_users","authentication_handlers":["cookie","default"]}}

Steps to Reproduce (for bugs)

  1. log in
  2. know user's name and his own db-name
  3. reload page and try to access db and/or sync

Context

Here I provide all the code needed for reproduction of the error: `onDeviceReady: function() {

  app.receivedEvent('deviceready');
  db_loc = new PouchDB('db_loc');
  //assumung db_loc already exists locally, and there's a doc with username and userdb called 'usercreds'
  db_loc.get('usercreds').then(function (doc){
      user = {name: doc.username};  
      userdb = doc.userdb;
  }).then(function(){
      db = new PouchDB('https://'+server+userdb, {skip_setup: true});
  }).then(function(){
    //here, the error occurs after page reload
    db.info().then(function (err, response){
      console.log('users online db:'+JSON.stringify(err));
    });
    app.sync();
  });

},`

`login: function(){

  user = {
      name: document.getElementById("loginform").user.value.toLowerCase(),
      password: document.getElementById("loginform").psw.value
  };
  ajaxOpts = {
    ajax: {
      headers: {
        Authorization: 'Basic ' + window.btoa(user.name + ':' + user.password)
      }
    }
  };
  db = new PouchDB('https://'+user.name+':'+user.password+'@'+server+"userdb-"+app.str_to_hex(user.name), ajaxOpts); 
  db.login(user.name, user.password, ajaxOpts, function (err, response) {
    if (err) {
      //handle error
      }
    }else{
      //logged in, yeah
    }
  }).then(function(){
      db.info().then(function (info) {
      console.log("private DB: "+JSON.stringify(info));
      });
  }).then(function(){
      db_loc = new PouchDB('db_loc');
  }).then(function(){
      app.sync();
  });

},`

`sync: function(){

var replicateOptions = {
    live: true,
    retry: true,
};
db.getSession().then(function (response){
    console.log(JSON.stringify(response)+"\n and initiating sync.");
    db_loc.sync(db, replicateOptions).on('change', function (change) {
      console.log("sync: yo, something changed!");
    }).on('paused', function (info) {
      console.log("sync: replication was paused, usually because of a lost connection");
    }).on('active', function (info) {
      console.log("sync: replication was resumed"); 
    }).on('error', function (err) {
      console.log("sync: totally unhandled error (shouldn't happen)");
      document.getElementById("fillme").innerHTML += "Please <a href='#loginpage'>login</a> again.";
    });
});

},`

Here is my CouchDB Configuration: config

And here are is another screenshot: screenshot error

Your Environment

I am quite desperately trying to get this to work. Any hint is very welcome!

juliusge commented 6 years ago

Hey everyone! I figured out, that PouchAuthentication/PouchDB did not set my Cookie with the AuthSession correctly, although it's said that it would do so when POSTing to _session (which is done when I call db.login(..)). In the network-tab of Chrome's DevTools, I was able to see that the Cookie was absent in the response header and could therefore not be set.

By adding x-csrf-token to the headers of cors in the config file (local.ini) of CouchDB and by applying changes to the login-function as follows, I could make the cookie be set. But: It was set empty (no value, "AuthSession=") (although I could see, that the POST request did include the AuthSession value (!)).

` db.login(user.name, user.password, {

            fetch: function (url, opts) {
                opts.headers.set('Authorization', 'Basic ' + window.btoa(user.name + ':' + user.password));
                opts.credentials='same-origin';
                opts.crossDomain='true';
                return PouchDB.fetch(url, opts);
            }
        }, function..handle stuff...

`

In order to set the Cookie successfully with value, I had to call the following function after login:

`getAuthSession: function(options) {

    fetch('https://mydomain.com:6984/_session', {
        method: "POST",
        mode: "cors", 
        credentials: "include",
        withCredentials: "true",
        crossDomain: "true",
        headers: {
            "Content-Type": "application/json; charset=utf-8"
        },
        referrer: "no-referrer", 
        body: JSON.stringify({"name": user.name, "password": user.password})
    })
      .then(function(response) {
        app.log("headers: " +JSON.stringify(response.headers)); //sadly, I can't access the Cookie myself, but works without - at least on android
        return response.json();
      })
      .then(function(myJson) {
        app.log(JSON.stringify(myJson));
      });
}`

Now the Cookie is being set with the correct value and is being sent with every request and authenticates the user successfully.

Tested successfully on Android device, Android emulator and OSX/Chrome with Phonegap

Note for browser-platform/Phonegap-serve: Here, some "io" and "session.sid" Cookies are set, too. This lead to one of them being sent instead of the AuthSession-Cookie in further requests, so that the authentication of the user did not work. This disappears when actually building the app and running it on a device.

I hope this helps! I found quite many threads where the Cookie/Auth-thing did not work out of the box although following the docs.