diff --git a/browserid/lib/db.js b/browserid/lib/db.js index 3caf1969f0e7609ff09343f1a3822628090e6e94..8181f9041c10ee1da658d3d16665d5e08814d237 100644 --- a/browserid/lib/db.js +++ b/browserid/lib/db.js @@ -1,6 +1,5 @@ const sqlite = require('sqlite'), - path = require('path'), - bcrypt = require('bcrypt'); + path = require('path'); var VAR_DIR = path.join(path.dirname(__dirname), "var"); @@ -13,7 +12,7 @@ var ready = false; var waiting = []; // async break allow database path to be configured by calling code -// a touch tricky cause client must set dbPath before releasing +// a touch tricky cause client must set dbPath before releasing // control of the runloop setTimeout(function() { db.open(exports.dbPath, function (error) { @@ -200,7 +199,7 @@ exports.stageEmail = function(existing_email, new_email, pubkey) { return secret; }; -/* invoked when a user clicks on a verification URL in their email */ +/* invoked when a user clicks on a verification URL in their email */ exports.gotVerificationSecret = function(secret, cb) { if (!g_staged.hasOwnProperty(secret)) return cb("unknown secret"); @@ -257,23 +256,17 @@ exports.gotVerificationSecret = function(secret, cb) { } }; -exports.checkAuth = function(email, pass, cb) { +// check authentication credentials for a given email address. This will invoke the +// users callback with the authentication (password/hash/whatever - the database layer +// doesn't care). callback will be passed undefined if email cannot be found +exports.checkAuth = function(email, cb) { db.execute("SELECT users.password FROM emails, users WHERE users.id = emails.user AND emails.address = ?", [ email ], function (error, rows) { - cb(rows.length === 1 && bcrypt.compare_sync(pass, rows[0].password)); + cb(rows.length !== 1 ? undefined : rows[0].password); }); }; -exports.checkAuthHash = function(email, hash, cb) { - db.execute("SELECT users.password FROM emails, users WHERE users.id = emails.user AND emails.address = ? AND users.password = ?", - [ email, hash ], - function (error, rows) { - cb(rows.length === 1); - }); -}; - - /* a high level operation that attempts to sync a client's view with that of the * server. email is the identity of the authenticated channel with the user, * identities is a map of email -> pubkey. @@ -291,7 +284,7 @@ exports.getSyncResponse = function(email, identities, cb) { key_refresh: [ ] }; - // get the user id associated with this account + // get the user id associated with this account emailToUserID(email, function(userID) { if (userID === undefined) { cb("no such email: " + email); @@ -320,7 +313,7 @@ exports.getSyncResponse = function(email, identities, cb) { // #3 // XXX todo - cb(undefined, respBody); + cb(undefined, respBody); } }); }); diff --git a/browserid/lib/wsapi.js b/browserid/lib/wsapi.js index 53c2096c4ab365acdd7de3d352a5a3d603f87386..b304843c87e4472ede7fbf9187896d8af3059000 100644 --- a/browserid/lib/wsapi.js +++ b/browserid/lib/wsapi.js @@ -87,10 +87,10 @@ function setup(app) { }; httputils.jsonResponse(resp, true); - + // let's now kick out a verification email! email.sendVerificationEmail(stageParams.email, stageParams.site, secret); - + } catch(e) { // we should differentiate tween' 400 and 500 here. httputils.badRequest(resp, e.toString()); @@ -105,14 +105,14 @@ function setup(app) { httputils.badRequest(resp, "api abuse: registration_status called without a pending email addition/verification"); return; } - + // Is the current session trying to add an email, or register a new one? if (req.session.pendingAddition) { // this is a pending email addition, it requires authentication if (!isAuthed(req, resp)) { return httputils.badRequest(resp, "requires authentication"); } - + // check if the currently authenticated user has the email stored under pendingAddition // in their acct. db.emailsBelongToSameAccount(req.session.pendingAddition, @@ -128,10 +128,10 @@ function setup(app) { } else { // this is a pending registration, let's check if the creds stored on the // session are good yet. - + var v = req.session.pendingVerification; - db.checkAuthHash(v.email, v.hash, function(authed) { - if (authed) { + db.checkAuth(v.email, function(hash) { + if (hash === v.hash) { delete req.session.pendingVerification; req.session.authenticatedUser = v.email; httputils.jsonResponse(resp, "complete"); @@ -141,28 +141,30 @@ function setup(app) { }); } }); - - + + app.get('/wsapi/authenticate_user', checkParams(["email", "pass"]), function(req, resp) { - db.checkAuth(req.query.email, req.query.pass, function(rv) { - if (rv) { + db.checkAuth(req.query.email, function(hash) { + var success = bcrypt.compare_sync(req.query.pass, hash); + + if (success) { if (!req.session) req.session = {}; req.session.authenticatedUser = req.query.email; } - httputils.jsonResponse(resp, rv); + httputils.jsonResponse(resp, success); }); }); - + // FIXME: need CSRF protection app.get('/wsapi/add_email', checkAuthed, checkParams(["email", "pubkey", "site"]), function (req, resp) { try { // upon success, stage_user returns a secret (that'll get baked into a url // and given to the user), on failure it throws var secret = db.stageEmail(req.session.authenticatedUser, req.query.email, req.query.pubkey); - + // store the email being added in session data req.session.pendingAddition = req.query.email; - + httputils.jsonResponse(resp, true); // let's now kick out a verification email! diff --git a/browserid/tests/db-test.js b/browserid/tests/db-test.js index 7135eb811bcaf9c3a1e0ad460e58358bdfce5888..c749770c8d97e2ea2a4e20077f3698dc77c15da7 100755 --- a/browserid/tests/db-test.js +++ b/browserid/tests/db-test.js @@ -142,10 +142,9 @@ suite.addBatch({ assert.strictEqual(r.length, 3); } } -}); - +}); -// XXX: remaining APIs to test +// XXX: remaining APIs to test // exports.addEmailToAccount // exports.cancelAccount // exports.checkAuth