diff --git a/lib/db/json.js b/lib/db/json.js
index b9a4c64088258d22a75452e88521af511239dc28..50bac38e283c5180ff96f80ea53225267b918e2d 100644
--- a/lib/db/json.js
+++ b/lib/db/json.js
@@ -175,13 +175,14 @@ function addEmailToAccount(userID, email, type, cb) {
   });
 }
 
-exports.stageUser = function(email, cb) {
+exports.stageUser = function(email, hash, cb) {
   secrets.generate(48, function(secret) {
     // overwrite previously staged users
     sync();
     db.staged[secret] = {
       type: "add_account",
       email: email,
+      passwd: hash,
       when: (new Date()).getTime()
     };
     db.stagedEmails[email] = secret;
@@ -190,7 +191,7 @@ exports.stageUser = function(email, cb) {
   });
 };
 
-exports.stageEmail = function(existing_user, new_email, cb) {
+exports.stageEmail = function(existing_user, new_email, hash, cb) {
   secrets.generate(48, function(secret) {
     // overwrite previously staged users
     sync();
@@ -198,6 +199,7 @@ exports.stageEmail = function(existing_user, new_email, cb) {
       type: "add_email",
       existing_user: existing_user,
       email: new_email,
+      passwd: hash,
       when: (new Date()).getTime()
     };
     db.stagedEmails[new_email] = secret;
@@ -250,7 +252,7 @@ exports.verificationSecretForEmail = function(email, cb) {
   }, 0);
 };
 
-exports.gotVerificationSecret = function(secret, hash, cb) {
+exports.gotVerificationSecret = function(secret, cb) {
   sync();
   if (!db.staged.hasOwnProperty(secret)) return cb("unknown secret");
 
@@ -265,6 +267,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
         var emailVal = {};
         emailVal[o.email] = { type: 'secondary' };
         var uid = getNextUserID();
+        var hash = o.passwd;
         db.users.push({
           id: uid,
           password: hash,
@@ -293,7 +296,17 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
     exports.emailKnown(o.email, function(err, known) {
       function addIt() {
         addEmailToAccount(o.existing_user, o.email, 'secondary', function(e) {
-          cb(e, o.email, o.existing_user);
+          var hash = o.passwd;
+          if(e || hash === null) return cb(e, o.email, o.existing_user);
+
+          // a hash was specified, update the password for the user
+          exports.emailToUID(o.email, function(err, uid) {
+            if(err) return cb(err, o.email, o.existing_user);
+
+            exports.updatePassword(uid, hash, function(err) {
+              cb(err || null, o.email, o.existing_user);
+            });
+          });
         });
       }
       if (known) {
diff --git a/lib/db/mysql.js b/lib/db/mysql.js
index b984dcbf438fd4a6bcaf3cb2c26f0666b66b9228..0af86c66156192da22acd8df2f1b0ae90afc5224 100644
--- a/lib/db/mysql.js
+++ b/lib/db/mysql.js
@@ -24,6 +24,7 @@
  *    | bool new_acct          |
  *    | int existing_user      |
  *    |*string email           |
+ *    |*string passwd          |
  *    | timestamp ts           |
  *    +------------------------+
  */
@@ -78,6 +79,7 @@ const schemas = [
     "new_acct BOOL NOT NULL," +
     "existing_user BIGINT," +
     "email VARCHAR(255) UNIQUE NOT NULL," +
+    "passwd CHAR(64)," +
     "ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL," +
     "FOREIGN KEY existing_user_fkey (existing_user) REFERENCES user(id)" +
     ") ENGINE=InnoDB;",
@@ -240,12 +242,12 @@ exports.lastStaged = function(email, cb) {
   );
 };
 
-exports.stageUser = function(email, cb) {
+exports.stageUser = function(email, hash, cb) {
   secrets.generate(48, function(secret) {
     // overwrite previously staged users
-    client.query('INSERT INTO staged (secret, new_acct, email) VALUES(?,TRUE,?) ' +
+    client.query('INSERT INTO staged (secret, new_acct, email, passwd) VALUES(?,TRUE,?,?) ' +
                  'ON DUPLICATE KEY UPDATE secret=VALUES(secret), existing_user=NULL, new_acct=TRUE, ts=NOW()',
-                 [ secret, email ],
+                 [ secret, email, hash ],
                  function(err) {
                    cb(err, err ? undefined : secret);
                  });
@@ -334,7 +336,7 @@ function addEmailToUser(userID, email, type, cb) {
 }
 
 
-exports.gotVerificationSecret = function(secret, hash, cb) {
+exports.gotVerificationSecret = function(secret, cb) {
   client.query(
     "SELECT * FROM staged WHERE secret = ?", [ secret ],
     function(err, rows) {
@@ -349,6 +351,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
         client.query("DELETE LOW_PRIORITY FROM staged WHERE secret = ?", [ secret ]);
 
         if (o.new_acct) {
+          var hash = o.passwd;
           // we're creating a new account, add appropriate entries into user and email tables.
           client.query(
             "INSERT INTO user(passwd) VALUES(?)",
@@ -362,17 +365,25 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
           // address.  the new schema has an 'existing_user' field which is a userid.
           // this is transitional code so outstanding verification links continue working
           // and can be removed in feb 2012 some time.  maybe for valentines day?
-          if (typeof o.existing_user === 'number') doAddEmail(o.existing_user);
+          if (typeof o.existing_user === 'number') doAddEmailSetPassword(o.existing_user);
           else if (typeof o.existing === 'string') {
             exports.emailToUID(o.existing, function(uid) {
               if (err || uid === undefined) return cb('acct associated with staged email doesn\'t exist');
-              doAddEmail(uid);
+              doAddEmailSetPassword(uid);
             });
           }
-          function doAddEmail(uid) {
+          function doAddEmailSetPassword(uid) {
             // we're adding an email address to an existing user account.  add appropriate entries into
             // email table
-            addEmailToUser(uid, o.email, 'secondary', cb);
+            var hash = o.passwd;
+            if (hash) {
+              exports.updatePassword(uid, hash, function(err) {
+                if (err) return cb('could not set user\'s password');
+                addEmailToUser(uid, o.email, 'secondary', cb);
+              });
+            } else {
+              addEmailToUser(uid, o.email, 'secondary', cb);
+            }
           }
         };
       }
@@ -420,12 +431,12 @@ exports.userOwnsEmail = function(uid, email, cb) {
     });
 }
 
-exports.stageEmail = function(existing_user, new_email, cb) {
+exports.stageEmail = function(existing_user, new_email, hash, cb) {
   secrets.generate(48, function(secret) {
     // overwrite previously staged users
-    client.query('INSERT INTO staged (secret, new_acct, existing_user, email) VALUES(?,FALSE,?,?) ' +
+    client.query('INSERT INTO staged (secret, new_acct, existing_user, email, passwd) VALUES(?,FALSE,?,?,?) ' +
                  'ON DUPLICATE KEY UPDATE secret=VALUES(secret), existing_user=VALUES(existing_user), new_acct=FALSE, ts=NOW()',
-                 [ secret, existing_user, new_email ],
+                 [ secret, existing_user, new_email, hash ],
                  function(err) {
                    cb(err, err ? undefined : secret);
                  });
diff --git a/lib/wsapi/complete_email_addition.js b/lib/wsapi/complete_email_addition.js
index 6e7dd2a4df4ce2116142fe5603d848ae43f9cccf..e7c61d66d39b071ed954d72cfd22a55667282625 100644
--- a/lib/wsapi/complete_email_addition.js
+++ b/lib/wsapi/complete_email_addition.js
@@ -17,9 +17,6 @@ exports.args = ['token'];
 exports.i18n = false;
 
 exports.process = function(req, res) {
-  // a password *must* be supplied to this call iff the user's password
-  // is currently NULL - this would occur in the case where this is the
-  // first secondary address to be added to an account
   db.emailForVerificationSecret(req.body.token, function(err, r) {
     if (err === 'database unavailable') {
       return wsapi.databaseDown(res, err);
@@ -51,11 +48,12 @@ exports.process = function(req, res) {
       } else {
         // now do we need to set the password?
         if (r.needs_password && req.body.pass) {
-          // requiring the client to wait until the bcrypt process is complete here
-          // exacerbates race conditions in front-end code.  We'll return success early,
-          // here, then update the password after the fact.  The worst thing that could
-          // happen is that password update could fail (due to extreme load), and the
-          // user will have to reset their password.
+          // requiring the client to wait until the bcrypt process is complete
+          // here exacerbates race conditions in front-end code.  We'll return
+          // success early, here, then update the password after the fact.
+          // The worst thing that could happen is that password update could
+          // fail (due to extreme load), and the user will have to reset
+          // their password.
           wsapi.authenticateSession(req.session, uid, 'password');
           res.json({ success: true });
 
diff --git a/lib/wsapi/complete_user_creation.js b/lib/wsapi/complete_user_creation.js
index dca109d14da9a856d1ab7fc6792ec100a932a37c..720dd3282f073eaad5a0be5a1474e90c729d530e 100644
--- a/lib/wsapi/complete_user_creation.js
+++ b/lib/wsapi/complete_user_creation.js
@@ -11,50 +11,31 @@ logger = require('../logging.js').logger;
 exports.method = 'post';
 exports.writes_db = true;
 exports.authed = false;
-exports.args = ['token','pass'];
+exports.args = ['token'];
 exports.i18n = false;
 
 exports.process = function(req, res) {
-  // issue #155, valid password length is between 8 and 80 chars.
-  var err = wsapi.checkPassword(req.body.pass);
-  if (err) return httputils.badRequest(res, err);
-
   // at the time the email verification is performed, we'll clear the pendingCreation
   // data on the session.
   delete req.session.pendingCreation;
 
-  // We should check to see if the verification secret is valid *before*
-  // bcrypting the password (which is expensive), to prevent a possible
-  // DoS attack.
   db.haveVerificationSecret(req.body.token, function(err, known) {
     if (err) return wsapi.databaseDown(res, err);
 
     if (!known) return res.json({ success: false} );
 
-    // now bcrypt the password
-    wsapi.bcryptPassword(req.body.pass, function (err, hash) {
+    db.gotVerificationSecret(req.body.token, function(err, email, uid) {
       if (err) {
-        if (err.indexOf('exceeded') != -1) {
-          logger.warn("max load hit, failing on auth request with 503: " + err);
-          return httputils.serviceUnavailable("server is too busy");
-        }
-        logger.error("can't bcrypt: " + err);
-        return res.json({ success: false });
+        logger.warn("couldn't complete email verification: " + err);
+        wsapi.databaseDown(res, err);
+      } else {
+        // FIXME: not sure if we want to do this (ba)
+        // at this point the user has set a password associated with an email address
+        // that they've verified.  We create an authenticated session.
+        wsapi.authenticateSession(req.session, uid, 'password',
+                                  config.get('ephemeral_session_duration_ms'));
+        res.json({ success: true });
       }
-
-      db.gotVerificationSecret(req.body.token, hash, function(err, email, uid) {
-        if (err) {
-          logger.warn("couldn't complete email verification: " + err);
-          wsapi.databaseDown(res, err);
-        } else {
-          // FIXME: not sure if we want to do this (ba)
-          // at this point the user has set a password associated with an email address
-          // that they've verified.  We create an authenticated session.
-          wsapi.authenticateSession(req.session, uid, 'password',
-                                    config.get('ephemeral_session_duration_ms'));
-          res.json({ success: true });
-        }
-      });
     });
   });
 };
diff --git a/lib/wsapi/stage_email.js b/lib/wsapi/stage_email.js
index 6ffe560eefd98102908f86946076a0643f99d083..c11b99823b0d828b7e3075d9845bd807468eb746 100644
--- a/lib/wsapi/stage_email.js
+++ b/lib/wsapi/stage_email.js
@@ -23,6 +23,10 @@ exports.args = ['email','site'];
 exports.i18n = true;
 
 exports.process = function(req, res) {
+  // a password *must* be supplied to this call iff the user's password
+  // is currently NULL - this would occur in the case where this is the
+  // first secondary address to be added to an account
+
   // validate
   try {
     sanitize(req.body.email).isEmail();
@@ -42,23 +46,58 @@ exports.process = function(req, res) {
       return httputils.throttled(res, "Too many emails sent to that address, try again later.");
     }
 
-    try {
-      // on failure stageEmail may throw
-      db.stageEmail(req.session.userid, req.body.email, function(err, secret) {
-        if (err) return wsapi.databaseDown(res, err);
+    db.checkAuth(req.session.userid, function(err, hash) {
+      var needs_password = !hash;
 
-        var langContext = wsapi.langContext(req);
+      if (!err && needs_password && !req.body.pass) {
+        err = "user must choose a password";
+      }
+      if (!err && !needs_password && req.body.pass) {
+        err = "a password may not be set at this time";
+      }
+      if (!err && needs_password) err = wsapi.checkPassword(req.body.pass);
 
-        // store the email being added in session data
-        req.session.pendingAddition = secret;
+      if (err) {
+        logger.info("stage of email fails: " + err);
+        return res.json({
+          success: false,
+          reason: err
+        });
+      }
 
-        res.json({ success: true });
-        // let's now kick out a verification email!
-        email.sendAddAddressEmail(req.body.email, req.body.site, secret, langContext);
-      });
-    } catch(e) {
-      // we should differentiate tween' 400 and 500 here.
-      httputils.badRequest(res, e.toString());
-    }
+      if (needs_password) {
+        wsapi.bcryptPassword(req.body.pass, function(err, hash) {
+          if (err) {
+            logger.warn("couldn't bcrypt password during email verification: " + err);
+            return res.json({ success: false });
+          }
+          completeStage(hash);
+        });
+      }
+      else {
+        completeStage(null);
+      }
+
+      function completeStage(hash) {
+        try {
+          // on failure stageEmail may throw
+          db.stageEmail(req.session.userid, req.body.email, hash, function(err, secret) {
+            if (err) return wsapi.databaseDown(res, err);
+
+            var langContext = wsapi.langContext(req);
+
+            // store the email being added in session data
+            req.session.pendingAddition = secret;
+
+            res.json({ success: true });
+            // let's now kick out a verification email!
+            email.sendAddAddressEmail(req.body.email, req.body.site, secret, langContext);
+          });
+        } catch(e) {
+          // we should differentiate tween' 400 and 500 here.
+          httputils.badRequest(res, e.toString());
+        }
+      }
+    });
   });
 };
diff --git a/lib/wsapi/stage_user.js b/lib/wsapi/stage_user.js
index 0408b7e76a53de41a380254feed56c9ae8282773..6d40868a6f313d0b691bccf2cd90c75f9bf2d905 100644
--- a/lib/wsapi/stage_user.js
+++ b/lib/wsapi/stage_user.js
@@ -19,7 +19,7 @@ sanitize = require('../sanitize');
 exports.method = 'post';
 exports.writes_db = true;
 exports.authed = false;
-exports.args = ['email','site'];
+exports.args = ['email','pass','site'];
 exports.i18n = true;
 
 exports.process = function(req, resp) {
@@ -32,12 +32,19 @@ exports.process = function(req, resp) {
   try {
     sanitize(req.body.email).isEmail();
     sanitize(req.body.site).isOrigin();
+    if(!req.body.pass) throw "missing pass";
   } catch(e) {
     var msg = "invalid arguments: " + e;
     logger.warn("bad request received: " + msg);
     return httputils.badRequest(resp, msg);
   }
 
+  var err = wsapi.checkPassword(req.body.pass);
+  if (err) {
+    logger.warn("invalid password received: " + err);
+    return httputils.badRequest(resp, err);
+  }
+
   db.lastStaged(req.body.email, function (err, last) {
     if (err) return wsapi.databaseDown(resp, err);
 
@@ -47,28 +54,40 @@ exports.process = function(req, resp) {
       return httputils.throttled(resp, "Too many emails sent to that address, try again later.");
     }
 
-    try {
-      // upon success, stage_user returns a secret (that'll get baked into a url
-      // and given to the user), on failure it throws
-      db.stageUser(req.body.email, function(err, secret) {
-        if (err) return wsapi.databaseDown(resp, err);
+    // now bcrypt the password
+    wsapi.bcryptPassword(req.body.pass, function (err, hash) {
+      if (err) {
+        if (err.indexOf('exceeded') != -1) {
+          logger.warn("max load hit, failing on auth request with 503: " + err);
+          return httputils.serviceUnavailable("server is too busy");
+        }
+        logger.error("can't bcrypt: " + err);
+        return res.json({ success: false });
+      }
 
-        // store the email being registered in the session data
-        if (!req.session) req.session = {};
+      try {
+        // upon success, stage_user returns a secret (that'll get baked into a url
+        // and given to the user), on failure it throws
+        db.stageUser(req.body.email, hash, function(err, secret) {
+          if (err) return wsapi.databaseDown(resp, err);
 
-        // store the secret we're sending via email in the users session, as checking
-        // that it still exists in the database is the surest way to determine the
-        // status of the email verification.
-        req.session.pendingCreation = secret;
+          // store the email being registered in the session data
+          if (!req.session) req.session = {};
 
-        resp.json({ success: true });
+          // store the secret we're sending via email in the users session, as checking
+          // that it still exists in the database is the surest way to determine the
+          // status of the email verification.
+          req.session.pendingCreation = secret;
 
-        // let's now kick out a verification email!
-        email.sendNewUserEmail(req.body.email, req.body.site, secret, langContext);
-      });
-    } catch(e) {
-      // we should differentiate tween' 400 and 500 here.
-      httputils.badRequest(resp, e.toString());
-    }
+          resp.json({ success: true });
+
+          // let's now kick out a verification email!
+          email.sendNewUserEmail(req.body.email, req.body.site, secret, langContext);
+        });
+      } catch(e) {
+        // we should differentiate tween' 400 and 500 here.
+        httputils.badRequest(resp, e.toString());
+      }
+    });
   });
 };
diff --git a/tests/add-email-with-assertion-test.js b/tests/add-email-with-assertion-test.js
index aca86c04c874d112f634a5a0b13d8e1bbb992dbb..19a58f0ae405b8c595101759dea3e47c14d5997e 100755
--- a/tests/add-email-with-assertion-test.js
+++ b/tests/add-email-with-assertion-test.js
@@ -112,6 +112,7 @@ suite.addBatch({
   "stage an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_FIRST_ACCT,
+      pass: 'fakepass',
       site:'http://fakesite.com:652'
     }),
     "works": function(err, r) {
@@ -126,7 +127,7 @@ suite.addBatch({
       },
       "can be used": {
         topic: function(token) {
-          wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'fakepass' }).call(this);
+          wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
         },
         "to verify email ownership": function(err, r) {
           assert.equal(r.code, 200);
diff --git a/tests/cert-emails-test.js b/tests/cert-emails-test.js
index 3bfba17e5ef1e0ffe4d14fdca5acd4a66c151937..2e79ad0fce3e76252fb56005340e49c4e81bb5ef 100755
--- a/tests/cert-emails-test.js
+++ b/tests/cert-emails-test.js
@@ -32,6 +32,8 @@ suite.addBatch({
   "staging an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'syncer@somehost.com',
+      pass: 'fakepass',
+      // TODO: is this pubkey needed?
       pubkey: 'fakekey',
       site:'http://fakesite.com'
     }),
@@ -57,7 +59,7 @@ suite.addBatch({
 suite.addBatch({
   "verifying account ownership": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'fakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "works": function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/db-test.js b/tests/db-test.js
index 65abaffa2926f260fd0157a7ee27dbf9f36d5565..e43237858abf3ea3e8d3ec23ef73a0a0cc7799a1 100755
--- a/tests/db-test.js
+++ b/tests/db-test.js
@@ -73,7 +73,7 @@ suite.addBatch({
 suite.addBatch({
   "stage a user for creation pending verification": {
     topic: function() {
-      db.stageUser('lloyd@nowhe.re', this.callback);
+      db.stageUser('lloyd@nowhe.re', 'biglonghashofapassword', this.callback);
     },
     "staging returns a valid secret": function(err, r) {
       assert.isNull(err);
@@ -125,7 +125,7 @@ suite.addBatch({
 suite.addBatch({
   "upon receipt of a secret": {
     topic: function() {
-      db.gotVerificationSecret(secret, 'fakepasswordhash', this.callback);
+      db.gotVerificationSecret(secret, this.callback);
     },
     "gotVerificationSecret completes without error": function (err, r) {
       assert.isNull(err);
@@ -164,7 +164,7 @@ suite.addBatch({
     },
     "the correct password": function(err, r) {
       assert.isNull(err);
-      assert.strictEqual(r, "fakepasswordhash");
+      assert.strictEqual(r, "biglonghashofapassword");
     }
   }
 });
@@ -200,7 +200,7 @@ suite.addBatch({
     },
     "then staging an email": {
       topic: function(err, uid) {
-        db.stageEmail(uid, 'lloyd@somewhe.re', this.callback);
+        db.stageEmail(uid, 'lloyd@somewhe.re', 'biglonghashofapassword', this.callback);
       },
       "yields a valid secret": function(err, secret) {
         assert.isNull(err);
@@ -215,7 +215,7 @@ suite.addBatch({
         "makes it visible via isStaged": function(sekret, r) { assert.isTrue(r); },
         "lets you verify it": {
           topic: function(secret, r) {
-            db.gotVerificationSecret(secret, undefined, this.callback);
+            db.gotVerificationSecret(secret, this.callback);
           },
           "successfully": function(err, r) {
             assert.isNull(err);
diff --git a/tests/email-throttling-test.js b/tests/email-throttling-test.js
index 4cceed04d5c83ee9c07d7a4e9bd3d87586d5207a..9b7e4560cfe8149e06d57272d946c9e042d3ee5d 100755
--- a/tests/email-throttling-test.js
+++ b/tests/email-throttling-test.js
@@ -24,6 +24,7 @@ suite.addBatch({
   "staging a registration": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'firstfakepass',
       site:'https://fakesite.com:443'
     }),
     "returns 200": function(err, r) {
@@ -49,6 +50,7 @@ suite.addBatch({
   "immediately staging another": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'firstfakepass',
       site:'http://fakesite.com:80'
     }),
     "is throttled": function(err, r) {
@@ -60,7 +62,7 @@ suite.addBatch({
 suite.addBatch({
   "finishing creating the first account": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "works": function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/forgotten-email-test.js b/tests/forgotten-email-test.js
index d08c98dad2cd25a056a07e14afa70443cdbbb6ef..c2e6aa3823be90f8c8093e013cc5697f09d6e67e 100755
--- a/tests/forgotten-email-test.js
+++ b/tests/forgotten-email-test.js
@@ -25,6 +25,7 @@ suite.addBatch({
   "staging an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'firstfakepass',
       site:'http://localhost:123'
     }),
     "works": function(err, r) {
@@ -49,7 +50,7 @@ suite.addBatch({
 suite.addBatch({
   "create first account": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "account created": function(err, r) {
       assert.equal(r.code, 200);
@@ -137,6 +138,7 @@ suite.addBatch({
   "re-stage first account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'secondfakepass',
       site:'https://otherfakesite.com'
     }),
     "works": function(err, r) {
@@ -187,7 +189,7 @@ suite.addBatch({
 suite.addBatch({
   "re-create first email address": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'secondfakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "account created": function(err, r) {
       assert.equal(r.code, 200);
@@ -196,7 +198,7 @@ suite.addBatch({
   }
 });
 
-// now we should be able to sign into the first email address with the second
+// now we should be able to sign into the first email address with the first
 // password, and all other combinations should fail
 suite.addBatch({
   "first email, first pass bad": {
diff --git a/tests/list-emails-wsapi-test.js b/tests/list-emails-wsapi-test.js
index 09fa35775cd57cbfc53ae6fa6c475dc0839e7bce..037bed0b812225b6618a231c75f94ec25e12bf33 100755
--- a/tests/list-emails-wsapi-test.js
+++ b/tests/list-emails-wsapi-test.js
@@ -27,6 +27,7 @@ suite.addBatch({
   "stage an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'syncer@somehost.com',
+      pass: 'fakepass',
       site:'https://foobar.fakesite.com'
     }),
     "works": function(err, r) {
@@ -51,7 +52,7 @@ suite.addBatch({
 suite.addBatch({
   "verifying account ownership": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'fakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "works": function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/no-cookie-test.js b/tests/no-cookie-test.js
index 33a2db02f237d4a816503b19be3cdfbf01e611f9..1689b22946e2faeca777c710a3a7570b6f908283 100755
--- a/tests/no-cookie-test.js
+++ b/tests/no-cookie-test.js
@@ -27,6 +27,7 @@ suite.addBatch({
   "start registration": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'firstfakepass',
       site:'http://fakesite.com:123'
     }),
     "returns 200": function(err, r) {
@@ -51,7 +52,7 @@ suite.addBatch({
 suite.addBatch({
   "completing user creation": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "works": function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/password-bcrypt-update-test.js b/tests/password-bcrypt-update-test.js
index df5eae39034b6ef26c86fe86f4c3812e491f23ab..033e9109f29269645e38ae45e1c3476881c14616 100755
--- a/tests/password-bcrypt-update-test.js
+++ b/tests/password-bcrypt-update-test.js
@@ -46,6 +46,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
+      pass: TEST_PASSWORD,
       site:'https://fakesite.com'
     }),
     "works":     function(err, r) {
@@ -72,8 +73,7 @@ suite.addBatch({
   "setting password": {
     topic: function() {
       wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: TEST_PASSWORD
+        token: token
       }).call(this);
     },
     "works just fine": function(err, r) {
diff --git a/tests/password-length-test.js b/tests/password-length-test.js
index 2294e3b306819392fdd67a96c18847ea8a5bfd81..dc7a2b02230b9dfac5438c093f1ab6a4369537a1 100755
--- a/tests/password-length-test.js
+++ b/tests/password-length-test.js
@@ -37,69 +37,50 @@ suite.addBatch({
 
 // first stage the account
 suite.addBatch({
-  "account staging": {
+  "a password that is non-existent": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
       site:'https://fakesite.com:123'
     }),
-    "works":     function(err, r) {
-      assert.equal(r.code, 200);
-    }
-  }
-});
-
-// wait for the token
-suite.addBatch({
-  "a token": {
-    topic: function() {
-      start_stop.waitForToken(this.callback);
-    },
-    "is obtained": function (t) {
-      assert.strictEqual(typeof t, 'string');
-      token = t;
+    "causes a HTTP error response": function(err, r) {
+      assert.equal(r.code, 400);
+      assert.equal(r.body, "Bad Request: missing 'pass' argument");
     }
-  }
-});
-
-
-// create a new account via the api with (first address)
-suite.addBatch({
+  },
   "a password that is too short": {
-    topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: '0123456' // less than 8 chars, invalid
-      }).call(this)
-    },
+    topic: wsapi.post('/wsapi/stage_user', {
+      email: 'first@fakeemail.com',
+      pass: '0123456', // less than 8 chars, invalid
+      site:'https://fakesite.com:123'
+    }),
     "causes a HTTP error response": function(err, r) {
       assert.equal(r.code, 400);
       assert.equal(r.body, "Bad Request: valid passwords are between 8 and 80 chars");
     }
   },
   "a password that is too long": {
-    topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: '012345678901234567890123456789012345678901234567890123456789012345678901234567891', // more than 81 chars, invalid.
-      }).call(this);
-    },
+    topic: wsapi.post('/wsapi/stage_user', {
+      email: 'first@fakeemail.com',
+      pass: '012345678901234567890123456789012345678901234567890123456789012345678901234567891', // more than 81 chars, invalid.
+      site:'https://fakesite.com:123'
+    }),
     "causes a HTTP error response": function(err, r) {
       assert.equal(r.code, 400);
       assert.equal(r.body, "Bad Request: valid passwords are between 8 and 80 chars");
     }
   },
   "but a password that is just right": {
-    topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: 'ahhh.  this is just right.'
-      }).call(this);
-    },
+    topic: wsapi.post('/wsapi/stage_user', {
+      email: 'first@fakeemail.com',
+      pass: 'ahhh.  this is just right.',
+      site:'https://fakesite.com:123'
+    }),
     "works just fine": function(err, r) {
       assert.equal(r.code, 200);
     }
   }
 });
+
 start_stop.addShutdownBatches(suite);
 
 // run or export the suite.
diff --git a/tests/password-update-test.js b/tests/password-update-test.js
index b04a4951fd7bf0f2a1161a56fc7dc02a92b41c47..1d8caefd0b2dadd92a43e0645403384b32ce69b9 100755
--- a/tests/password-update-test.js
+++ b/tests/password-update-test.js
@@ -34,6 +34,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
+      pass: OLD_PASSWORD,
       site: 'https://fakesite.com:123'
     }),
     "works":     function(err, r) {
diff --git a/tests/primary-then-secondary-test.js b/tests/primary-then-secondary-test.js
index 11104396191720812da44e195cb6b2020aea0751..8f0072acafef72e6eae0cc8a8030f92855b0af9e 100755
--- a/tests/primary-then-secondary-test.js
+++ b/tests/primary-then-secondary-test.js
@@ -75,7 +75,6 @@ suite.addBatch({
 });
 
 var token;
-
 // now we have a new account.  let's add a secondary to it
 suite.addBatch({
   "add a new email address to our account": {
@@ -83,40 +82,37 @@ suite.addBatch({
       email: SECONDARY_EMAIL,
       site:'https://fakesite.com'
     }),
-    "works": function(err, r) {
+    "fails without a password": function(err, r) {
       assert.strictEqual(r.code, 200);
+      assert.strictEqual(JSON.parse(r.body).success, false);
     },
-    "and get a token": {
-      topic: function() {
-        start_stop.waitForToken(this.callback);
-      },
-      "successfully": function (t) {
-        this._token = t;
-        assert.strictEqual(typeof t, 'string');
+    "but with a password": {
+      topic: wsapi.post('/wsapi/stage_email', {
+        email: SECONDARY_EMAIL,
+        pass: TEST_PASS,
+        site:'https://fakesite.com'
+      }),
+      "succeeds": function(err, r) {
+        assert.strictEqual(r.code, 200);
       },
-      "and complete":  {
-        topic: function(t) {
-          wsapi.get('/wsapi/email_for_token', {
-            token: t
-          }).call(this);
+      "and get a token": {
+        topic: function() {
+          start_stop.waitForToken(this.callback);
         },
-        "we need to set our password": function (err, r) {
-          r = JSON.parse(r.body);
-          assert.ok(r.needs_password);
+        "successfully": function (t) {
+          this._token = t;
+          assert.strictEqual(typeof t, 'string');
         },
-        "with": {
-          topic: function() {
-            wsapi.post('/wsapi/complete_email_addition', { token: this._token }).call(this);
-          },
-          "no password fails": function(err, r) {
-            assert.equal(r.code, 200);
-            assert.strictEqual(JSON.parse(r.body).success, false);
+        "and complete":  {
+          topic: function(t) {
+            wsapi.get('/wsapi/email_for_token', {
+              token: t
+            }).call(this);
           },
-          "a password": {
+          "which then": {
             topic: function() {
               wsapi.post('/wsapi/complete_email_addition', {
-                token: this._token,
-                pass: TEST_PASS
+                token: this._token
               }).call(this);
             },
             "succeeds": function(err, r) {
@@ -140,7 +136,6 @@ suite.addBatch({
   }
 });
 
-
 // after a small delay, we can authenticate with our password
 suite.addBatch({
   "after a small delay": {
@@ -161,41 +156,42 @@ suite.addBatch({
 
 // adding a second secondary will not let us set the password
 suite.addBatch({
-  "add a new email address to our account": {
+  "add a second secondary to account with": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: SECOND_SECONDARY_EMAIL,
+      pass: TEST_PASS,
       site:'http://fakesite.com:123'
     }),
-    "works": function(err, r) {
+    "a password fails": function(err, r) {
       assert.strictEqual(r.code, 200);
+      var body = JSON.parse(r.body);
+      assert.strictEqual(body.success, false);
+      assert.strictEqual(body.reason, 'a password may not be set at this time');
     },
-    "and get a token": {
-      topic: function() {
-        start_stop.waitForToken(this.callback);
-      },
-      "successfully": function (t) {
-        this._token = t;
-        assert.strictEqual(typeof t, 'string');
+    "but with no password specified": {
+      topic: wsapi.post('/wsapi/stage_email', {
+        email: SECOND_SECONDARY_EMAIL,
+        site:'http://fakesite.com:123'
+      }),
+      "succeeds": function(err, r) {
+        assert.strictEqual(r.code, 200);
+        assert.strictEqual(JSON.parse(r.body).success, true);
       },
-      "and to complete": {
-        topic: function(t) {
-          wsapi.get('/wsapi/email_for_token', {
-            token: t
-          }).call(this);
+      "and get a token": {
+        topic: function() {
+          start_stop.waitForToken(this.callback);
         },
-        "we do not need to set our password": function (err, r) {
-          r = JSON.parse(r.body);
-          assert.isFalse(r.needs_password);
+        "successfully": function (t) {
+          this._token = t;
+          assert.strictEqual(typeof t, 'string');
         },
-        "with": {
-          topic: function() {
-            wsapi.post('/wsapi/complete_email_addition', { token: this._token, pass: TEST_PASS }).call(this);
+        "and to complete":  {
+          topic: function(t) {
+            wsapi.get('/wsapi/email_for_token', {
+              token: t
+            }).call(this);
           },
-          "a password fails": function(err, r) {
-            assert.equal(r.code, 200);
-            assert.strictEqual(JSON.parse(r.body).success, false);
-          },
-          "no password succeeds": {
+          "with a token": {
             topic: function() {
               wsapi.post('/wsapi/complete_email_addition', {
                 token: this._token
@@ -231,11 +227,10 @@ suite.addBatch({
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
-    },
+    }
   }
 });
 
-
 // shut the server down and cleanup
 start_stop.addShutdownBatches(suite);
 
diff --git a/tests/registration-status-wsapi-test.js b/tests/registration-status-wsapi-test.js
index 9857e9e39a80172b4a8852e79c15ccb3280a7e2d..10944b1ae2388549ddf83fad688ca8fff0996228 100755
--- a/tests/registration-status-wsapi-test.js
+++ b/tests/registration-status-wsapi-test.js
@@ -51,6 +51,7 @@ suite.addBatch({
   "start registration": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'firstfakepass',
       site:'https://fakesite.com'
     }),
     "returns 200": function(err, r) {
@@ -110,7 +111,7 @@ suite.addBatch({
 suite.addBatch({
   "completing user creation": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'firstfakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "works": function(err, r) {
       assert.equal(r.code, 200);
@@ -170,6 +171,7 @@ suite.addBatch({
   "re-registering an existing email": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
+      pass: 'secondfakepass',
       site:'http://secondfakesite.com'
     }),
     "yields a HTTP 200": function (err, r) {
@@ -206,7 +208,7 @@ suite.addBatch({
 suite.addBatch({
   "proving email ownership causes account re-creation": {
     topic: function() {
-      wsapi.post('/wsapi/complete_user_creation', { token: token, pass: 'secondfakepass' }).call(this);
+      wsapi.post('/wsapi/complete_user_creation', { token: token }).call(this);
     },
     "and returns a 200 code": function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/session-context-test.js b/tests/session-context-test.js
index 87a42ac40ce34faf8658bcf6a5b20b68b232eb61..6c2fe805a1c2a519ff1a7a9bccb434f018c06e5a 100755
--- a/tests/session-context-test.js
+++ b/tests/session-context-test.js
@@ -32,6 +32,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
+      pass: PASSWORD,
       site: 'https://fakesite.com'
     }),
     "works":     function(err, r) {
@@ -58,8 +59,7 @@ suite.addBatch({
   "setting password": {
     topic: function() {
       wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: PASSWORD
+        token: token
       }).call(this);
     },
     "works just fine": function(err, r) {
@@ -89,7 +89,7 @@ suite.addBatch({
       var resp = JSON.parse(r.body);
       assert.strictEqual(typeof resp.csrf_token, 'string');
       var serverTime = new Date(resp.server_time);
-      assert.ok(new Date() - serverTime < 5000);      
+      assert.ok(new Date() - serverTime < 5000);
       assert.strictEqual(resp.authenticated, true);
       assert.strictEqual(resp.auth_level, 'password');
       var domainKeyCreation = new Date(resp.domain_key_creation_time);
diff --git a/tests/session-duration-test.js b/tests/session-duration-test.js
index cedec0f0857870940ede1492dfdbad94beae19bc..232ad6320a6f7979a6f5f5d39f800eb377229a9a 100755
--- a/tests/session-duration-test.js
+++ b/tests/session-duration-test.js
@@ -106,6 +106,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
+      pass: PASSWORD,
       site: 'http://a.really.fakesite123.com:999'
     }),
     "works":     function(err, r) {
@@ -132,8 +133,7 @@ suite.addBatch({
   "setting password": {
     topic: function() {
       wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: PASSWORD
+        token: token
       }).call(this);
     },
     "works just fine": function(err, r) {
diff --git a/tests/session-prolong-test.js b/tests/session-prolong-test.js
index adf193e2764b7d6adb53aa9d7603a585e1ef0dce..2df5cc4d2a2a7e1ad80cb847f233479389f5e9b5 100755
--- a/tests/session-prolong-test.js
+++ b/tests/session-prolong-test.js
@@ -32,6 +32,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
+      pass: PASSWORD,
       site: 'http://fakesite.com'
     }),
     "works":     function(err, r) {
@@ -58,8 +59,7 @@ suite.addBatch({
   "setting password": {
     topic: function() {
       wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: PASSWORD
+        token: token
       }).call(this);
     },
     "works just fine": function(err, r) {
diff --git a/tests/stalled-mysql-test.js b/tests/stalled-mysql-test.js
index 0bdd2b4b4b670b034120f3f47c886d5ced31bac8..864c8722506f3d7e55715394ce5c88e3c2cc356a 100755
--- a/tests/stalled-mysql-test.js
+++ b/tests/stalled-mysql-test.js
@@ -129,8 +129,7 @@ suite.addBatch({
   },
   "complete_user_creation": {
     topic: wsapi.post('/wsapi/complete_user_creation', {
-      token: 'bogus',
-      pass: 'fakefake'
+      token: 'bogus'
     }),
     "fails with 503": function(err, r) {
       assert.strictEqual(r.code, 503);
@@ -147,6 +146,7 @@ suite.addBatch({
   "stage_user": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'bogus@bogus.edu',
+      pass: 'a_password',
       site: 'https://whatev.er'
     }),
     "fails with 503": function(err, r) {
@@ -176,6 +176,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: "stalltest@whatev.er",
+      pass: 'a_password',
       site: 'http://fakesite.com'
     }),
     "works":     function(err, r) {
@@ -195,8 +196,7 @@ suite.addBatch({
     "setting password": {
       topic: function(token) {
         wsapi.post('/wsapi/complete_user_creation', {
-          token: token,
-          pass: "somepass"
+          token: token
         }).call(this);
       },
       "works just fine": function(err, r) {
@@ -266,6 +266,7 @@ suite.addBatch({
   "stage_email": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: "test2@whatev.er",
+      pass: 'a_password',
       site: "https://foo.com"
     }),
     "fails with 503": function(err, r) {
diff --git a/tests/verifier-test.js b/tests/verifier-test.js
index 09ea032801dc5fdff489040a1b281666efdb0a47..efa098a102eec4110daff9b8dc582218e1e2eb6d 100755
--- a/tests/verifier-test.js
+++ b/tests/verifier-test.js
@@ -41,6 +41,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
+      pass: TEST_PASSWORD,
       site: TEST_ORIGIN
     }),
     "works":     function(err, r) {
@@ -65,8 +66,7 @@ suite.addBatch({
   "setting password and creating the account": {
     topic: function() {
       wsapi.post('/wsapi/complete_user_creation', {
-        token: token,
-        pass: TEST_PASSWORD
+        token: token
       }).call(this);
     },
     "works just fine": function(err, r) {
@@ -714,7 +714,7 @@ suite.addBatch({
 });
 
 // now verify that assertions from a primary who does not have browserid support
-// will fail to verify 
+// will fail to verify
 function make_other_issuer_tests(new_style) {
   var title = "generating an assertion from a cert signed by some other domain with " + (new_style ? "new style" : "old style");
   var tests = {