florianheinemann / passwordless-mongostore

Token store for Passwordless using MongoDB
https://passwordless.net
MIT License
34 stars 29 forks source link

Error creating index on uid: MongoError: not authorized for query on passwordless.system.indexes #9

Closed tilowestermann closed 9 years ago

tilowestermann commented 9 years ago

NB: I'm not 100% sure if this is really an issue with passwordless-mongostore

I've set up Passwordless according to the docs and called init with passwordless.init(new MongoStore(config_db.url + '/passwordless' ,options));

options is the same object I'm using for my mongoose connection:

var options = {
      server: { socketOptions: { keepAlive: 1 } },
      user: config_db.user,
      pass: config_db.pass,
      auth: { authdb: config_db.authdb }
    }

Calling a protected route, I'm getting the following error:

2015-03-12 22:55:12.228363500 /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/connection/base.js:246 2015-03-12 22:55:12.228366500 throw message;
2015-03-12 22:55:12.228367500 ^ 2015-03-12 22:55:12.228367500 Error: Error creating index on uid: MongoError: not authorized for query on passwordless.system.indexes 2015-03-12 22:55:12.228368500 at /home/user/project/node_modules/passwordless-mongostore/lib/mongostore.js:229:10 2015-03-12 22:55:12.228368500 at /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/db.js:1497:46 2015-03-12 22:55:12.228432500 at /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/db.js:1630:20 2015-03-12 22:55:12.228433500 at /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/cursor.js:170:22 2015-03-12 22:55:12.228434500 at /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/cursor.js:714:39 2015-03-12 22:55:12.228445500 at Cursor.close (/home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/cursor.js:1009:5) 2015-03-12 22:55:12.228446500 at commandHandler (/home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/cursor.js:714:21) 2015-03-12 22:55:12.228446500 at /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/db.js:1903:9 2015-03-12 22:55:12.228460500 at Server.Base._callHandler (/home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/connection/base.js:453:41) 2015-03-12 22:55:12.228462500 at /home/user/project/node_modules/passwordless-mongostore/node_modules/mongodb/lib/mongodb/connection/server.js:487:18

florianheinemann commented 9 years ago

Hm... not seen that one yet. What might cause trouble is that this adapter has to be able to set certain indexes. Are those rights set? Florian

tilowestermann commented 9 years ago

Rights are properly set, but it seems as if the authentication doesn't work. Doing the init with passwordless.init({ mongooseConnection: mongoose.connection }); seems to work

tilowestermann commented 9 years ago

Cheered to soon, next issue:

2015-03-13 10:43:33.419858500 /home/user/project/node_modules/passwordless/lib/passwordless/passwordless.js:455 2015-03-13 10:43:33.419860500 self._tokenStore.storeOrUpdate(token, uid.toString(), ttl, origin, functio 2015-03-13 10:43:33.419861500 ^ 2015-03-13 10:43:33.419862500 TypeError: undefined is not a function 2015-03-13 10:43:33.419862500 at /home/user/project/node_modules/passwordless/lib/passwordless/passwordless.js:455:22 2015-03-13 10:43:33.419863500 at Promise. (/home/user/project/routes/authors.js:19:19) 2015-03-13 10:43:33.419916500 at Promise. (/home/user/project/node_modules/mongoose/node_modules/mpromise/lib/promise.js:177:8) 2015-03-13 10:43:33.419916500 at Promise.emit (events.js:107:17) 2015-03-13 10:43:33.419916500 at Promise.emit (/home/user/project/node_modules/mongoose/node_modules/mpromise/lib/promise.js:84:38) 2015-03-13 10:43:33.419916500 at Promise.fulfill (/home/user/project/node_modules/mongoose/node_modules/mpromise/lib/promise.js:97:20) 2015-03-13 10:43:33.419926500 at /home/user/project/node_modules/mongoose/lib/query.js:1400:13 2015-03-13 10:43:33.419927500 at Model.Document.init (/home/user/project/node_modules/mongoose/lib/document.js:254:11) 2015-03-13 10:43:33.419929500 at completeOne (/home/user/project/node_modules/mongoose/lib/query.js:1398:10) 2015-03-13 10:43:33.419930500 at Immediate.cb (/home/user/project/node_modules/mongoose/lib/query.js:1155:11)

florianheinemann commented 9 years ago

The 2nd way how you initialize passwordless can't work - maybe you want to share a bit more about your code so that we can try to dive deeper into the problem?

tilowestermann commented 9 years ago

Sure (thanks for your guidance!): I'm trying to set up a multi-tenant (one database per tenant) application where one route should be protected.

app.js contains the following to set the right db for a request:

// basic db connection
var options = {
      server: { socketOptions: { keepAlive: 1 } },
      user: config_db.user,
      pass: config_db.pass,
      auth: { authdb: config_db.authdb }
    }

var connect = function () {          
    mongoose.connect(config_db.url + '/' + config_db.base_db, options);    
};
connect();

// set db according to request 
function setclientdb() {
    return function(req, res, next){
        // set activedb according to the request
        return next();
    }
}
app.use(setclientdb());

app.use(function db(req, res, next) {
    req.db = {
        Author: activedb.model('author'),
        Paper: activedb.model('paper'),
        Session: activedb.model('session'),
        User: activedb.model('user')
    };
    return next();
});

In order to request tokens, I've implemented the following (which finds a user and does the callback where the error occurs)

router.post('/sendtoken', 
    passwordless.requestToken(
        function(user, delivery, callback, req) {
            req.db.User.findOne({ email : user }, function(err, ret) {
               if(ret)
                  callback(null, ret.id)
               else
                  callback(null, null)
          })
        }),
    function(req, res) {
       // success!
          res.render('sent');
});
tilowestermann commented 9 years ago

Ok, this works now (can't use my admin account on a shared server with a different database (which is not the authentication database)).

However, I can't get the routes to work properly. I enabled allowTokenReuse for stateless operation and set app.use(passwordless.acceptToken()); (without app.use(passwordless.sessionSupport());)

router.get('/auth', passwordless.acceptToken(), 
    function(req, res) {
        res.status(200).send({'result': req.user});
});

This works as expected GET /4/auth/auth?uid=UID&token=TOKEN 200 498.064 ms - 3

router.get('/restricted', passwordless.restricted(),
 function(req, res) {
     res.status(200).send('Yep, works!');
});

This doesn't GET /4/auth/restricted?uid=UID&token=TOKEN 401 39.118 ms - -, telling me to Provide a token.

Did I miss something? (This question surely doesn't belong the original this issue anymore)

florianheinemann commented 9 years ago

Hi,

router.use('/auth', passwordless.acceptToken()) should work. You basically have to tell the router that for all paths under auth this middleware should be used - not only for a specific one.

Hope that helps!

tilowestermann commented 9 years ago

Hey, thanks a lot!

One more question regarding the multi-tenant setup. Currently, the setup manages one database holding all tokens. This would allow a verified user of tenant A to access restricted sources of tenant B. Is it possible to provide a mechanism for this scenario (e.g. having one collection of tokens in each tenant's database)?

florianheinemann commented 9 years ago

Hi,

I think there are several approaches. Passwordless just ensures authentication (is the person the one we think he is?). Authorization (what is the user allowed to do?) is a different element and could be implemented by an additional middleware checking in the database what the currently logged in user is allowed to do and either allow or reject a request.

Otherwise, you could probably write a custom TokenStore (https://github.com/florianheinemann/passwordless-tokenstore-test) that is storing tokens in different databases depending e.g. on the user ID. However, I'm not sure if that helps with your specific problem as this wouldn't do much about authorization.

Cheers

Florian

martin-fogelman commented 7 years ago

FYI, I also had this error and was unable to get mongoStore options to be accepted properly, so I just added my options (in my case authentication) to the url (see https://docs.mongodb.com/manual/reference/connection-string/ and http://mongoosejs.com/docs/connections.html)