Skip to content

Commit

Permalink
(#268) - pouchdb-auth update
Browse files Browse the repository at this point in the history
Switches from a session db to a cookie based session system, making user
document changes invalidate the session.
  • Loading branch information
marten-de-vries authored and nolanlawson committed Feb 13, 2016
1 parent 96a0422 commit ef495aa
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 56 deletions.
1 change: 1 addition & 0 deletions lib/config-infrastructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ CouchConfig.prototype._changed = function (section, key, prevVal, callback) {

// run event handlers
self.emit(section + "." + key);
self.emit(section);

callback(null, prevVal);
});
Expand Down
80 changes: 42 additions & 38 deletions lib/routes/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,39 @@
var cookieParser = require('cookie-parser'),
basicAuth = require('basic-auth'),
utils = require('../utils'),
uuids = require('../uuids'),
Promise = require('bluebird');
Promise = require('bluebird'),
Auth = require('pouchdb-auth');

var SECTION = 'couch_httpd_auth';
var KEY = 'authentication_db';

module.exports = function (app) {
var usersDBPromise, refreshUsersDBImpl;

utils.requires(app, 'config-infrastructure');
utils.requires(app, 'logging-infrastructure');

app.couchConfig.registerDefault(SECTION, KEY, '_users');
app.couchConfig.registerDefault(SECTION, 'authentication_db', '_users');
app.couchConfig.registerDefault(SECTION, 'timeout', 600);
app.couchConfig.registerDefault(SECTION, 'secret', Auth.generateSecret());
app.couchConfig.registerDefault(SECTION, 'iterations', 10);

// explain how to activate the auth db logic.
app.dbWrapper.registerWrapper(function (name, db, next) {
if (name === getUsersDBName()) {
return db.useAsAuthenticationDB();
return db.useAsAuthenticationDB({
isOnlineAuthDB: false,
timeout: app.couchConfig.get(SECTION, 'timeout'),
secret: app.couchConfig.get(SECTION, 'secret'),
iterations: app.couchConfig.get(SECTION, 'iterations'),
admins: app.couchConfig.getSection('admins')
});
}
return next();
});

app.daemonManager.registerDaemon({
start: function (PouchDB) {
PouchDB.plugin(require('pouchdb-auth'));
PouchDB.plugin(Auth);

refreshUsersDBImpl = function () {
usersDBPromise = utils.getUsersDB(app, PouchDB);
Expand All @@ -44,6 +52,7 @@ module.exports = function (app) {
var getUsersDBName = utils.getUsersDBName.bind(null, app);

function getUsersDB() {
// calls itself until usersDBPromise is a available
if (!usersDBPromise) {
return new Promise(function (resolve) {
setImmediate(function () {
Expand All @@ -66,15 +75,19 @@ module.exports = function (app) {
}

// ensure there's always a users db
app.couchConfig.on(SECTION + '.' + KEY, refreshUsersDB);
app.couchConfig.on(SECTION + '.authentication_db', refreshUsersDB);
app.couchConfig.on(SECTION + '.timeout', refreshUsersDB);
app.couchConfig.on(SECTION + '.secret', refreshUsersDB);
app.couchConfig.on(SECTION + '.iterations', refreshUsersDB);
app.couchConfig.on('admins', refreshUsersDB);

// routing
app.use(cookieParser());

app.use(function (req, res, next) {
// TODO: TIMING ATTACK
Promise.resolve().then(function () {
return buildCookieSession(req);
return buildCookieSession(req, res);
}).catch(function (err) {
return buildBasicAuthSession(req);
}).then(function (result) {
Expand All @@ -86,22 +99,21 @@ module.exports = function (app) {
});
});

function buildCookieSession(req) {
var opts = {
sessionID: (req.cookies || {}).AuthSession,
admins: app.couchConfig.getSection("admins")
};
if (!opts.sessionID) {
function buildCookieSession(req, res) {
var sessionID = (req.cookies || {}).AuthSession;
if (!sessionID) {
throw new Error("No cookie, so no cookie auth.");
}
return getUsersDB().then(function (db) {
return db.session(opts);
}).then(function (result) {
if (result.info.authenticated) {
result.info.authenticated = 'cookie';
logSuccess('cookie', result);
return db.multiUserSession(sessionID);
}).then(function (session) {
if (session.info.authenticated) {
res.cookie('AuthSession', session.sessionID, {httpOnly: true});
delete session.sessionID;
session.info.authenticated = 'cookie';
logSuccess('cookie', session);
}
return result;
return session;
});
}

Expand All @@ -112,33 +124,25 @@ module.exports = function (app) {

function buildBasicAuthSession(req) {
var userInfo = basicAuth(req);
var opts = {
sessionID: uuids(1)[0],
admins: app.couchConfig.getSection("admins")
};
var db;
var initializingDone = getUsersDB().then(function (theDB) {
db = theDB;
});
if (userInfo) {
initializingDone = initializingDone.then(function () {
return db.logIn(userInfo.name, userInfo.pass, opts);
return db.multiUserLogIn(userInfo.name, userInfo.pass);
});
}
var result;
return initializingDone.then(function () {
return db.session(opts);
}).then(function (theSession) {
result = theSession;

// Cleanup
return db.logOut(opts);
}).then(function () {
if (result.info.authenticated) {
logSuccess('http basic', result);
result.info.authenticated = 'default';
return initializingDone.then(function (info) {
return db.multiUserSession((info || {}).sessionID);
}).then(function (session) {
delete session.sessionID;

if (session.info.authenticated) {
session.info.authenticated = 'default';
logSuccess('http basic', session);
}
return result;
return session;
});
}
};
23 changes: 6 additions & 17 deletions lib/routes/session.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use strict";

var utils = require('../utils'),
uuids = require('../uuids');
var utils = require('../utils');

module.exports = function (app) {
utils.requires(app, 'routes/authentication');
Expand All @@ -18,14 +17,11 @@ module.exports = function (app) {
function postHandler(req, res, next) {
var name = req.body.name;
var password = req.body.password;
var opts = {
sessionID: uuids(1)[0],
admins: app.couchConfig.getSection("admins")
};
getUsersDB(req).then(function (db) {
return db.logIn(name, password, opts);
return db.multiUserLogIn(name, password);
}).then(function (resp) {
res.cookie('AuthSession', opts.sessionID, {httpOnly: true});
res.cookie('AuthSession', resp.sessionID, {httpOnly: true});
delete resp.sessionID;
if (req.query.next) {
utils.setLocation(res, req.query.next);
return res.status(302).end();
Expand All @@ -38,14 +34,7 @@ module.exports = function (app) {
app.post('/_session', utils.jsonParser, utils.urlencodedParser, postHandler);

app.delete('/_session', function (req, res, next) {
var sessionID = (req.cookies || {}).AuthSession;

getUsersDB(req).then(function (db) {
//no error handler necessary for log out
return db.logOut({sessionID: sessionID});
}).then(function (resp) {
res.clearCookie('AuthSession');
utils.sendJSON(res, 200, resp);
});
res.clearCookie('AuthSession');
utils.sendJSON(res, 200, {ok: true});
});
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"node-uuid": "1.4.1",
"on-finished": "^2.1.1",
"pouchdb-all-dbs": "^1.0.0",
"pouchdb-auth": "^1.0.2",
"pouchdb-auth": "^2.0.0",
"pouchdb-find": "^0.1.0",
"pouchdb-list": "^1.1.0",
"pouchdb-replicator": "^2.1.0",
Expand Down

0 comments on commit ef495aa

Please sign in to comment.