diff --git a/lib/db.js b/lib/db.js
index 7caf732f50790814dd69b15684ffbbab5df8ebd1..22fc1da0021d7cc5d004a64397234040f73f617d 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -108,6 +108,7 @@ exports.onReady = function(f) {
   'isStaged',
   'emailsBelongToSameAccount',
   'emailForVerificationSecret',
+  'haveVerificationSecret',
   'verificationSecretForEmail',
   'checkAuth',
   'listEmails',
diff --git a/lib/db/json.js b/lib/db/json.js
index 9d0133880c31a01a545ab9dbac3b4047866598f3..831721a98511bb027410123aba633b9cf5f1005b 100644
--- a/lib/db/json.js
+++ b/lib/db/json.js
@@ -230,11 +230,26 @@ exports.createUserWithPrimaryEmail = function(email, cb) {
   });
 };
 
+exports.haveVerificationSecret = function(secret, cb) {
+  process.nextTick(function() {
+    sync();
+    cb(!!(db.staged[secret]));
+  });
+};
+
+
 exports.emailForVerificationSecret = function(secret, cb) {
-  setTimeout(function() {
+  process.nextTick(function() {
     sync();
-    cb(db.staged[secret] ? db.staged[secret].email : undefined);
-  }, 0);
+    if (!db.staged[secret]) return cb("no such secret");
+    exports.checkAuth(db.staged[secret].email, function (hash) {
+      console.log(db.staged[secret].email, hash);
+      cb(undefined, {
+        email: db.staged[secret].email,
+        needs_password: !hash
+      });
+    });
+  });
 };
 
 exports.verificationSecretForEmail = function(email, cb) {
diff --git a/lib/db/mysql.js b/lib/db/mysql.js
index 660de0b5afda46a7920162963c7174fdf215020a..bed01cde4db4d874a294920000d33a63b187f274 100644
--- a/lib/db/mysql.js
+++ b/lib/db/mysql.js
@@ -280,12 +280,40 @@ exports.stageUser = function(email, cb) {
   });
 };
 
+
+exports.haveVerificationSecret = function(secret, cb) {
+  client.query(
+    "SELECT count(*) as n FROM staged WHERE secret = ?", [ secret ],
+    function(err, rows) {
+      if (err) cb(false);
+      else cb(rows.length === 1 && rows[0].n === 1);
+    });
+};
+
 exports.emailForVerificationSecret = function(secret, cb) {
   client.query(
-    "SELECT email FROM staged WHERE secret = ?", [ secret ],
+    "SELECT email, new_acct, existing FROM staged WHERE secret = ?", [ secret ],
     function(err, rows) {
       if (err) logUnexpectedError(err);
-      cb((rows && rows.length > 0) ? rows[0].email : undefined);
+      // if the record was not found, fail out
+      if (!rows || rows.length != 1) return cb("no such secret");
+
+      var o = rows[0];
+
+      // if the record was found and this is for a new_acct, return the email
+      if (o.new_acct) return cb(undefined, { email: o.email, needs_password: false });
+
+      // if the account is being added to an existing account, let's find
+      // out if the account has a password set (if only primary email addresses
+      // are associated with the acct at the moment, then there will not be a
+      // password set and the user will need to set one with the addition of
+      // this addresss)
+      exports.checkAuth(o.email, function(hash) {
+        cb(undefined, {
+          email: o.email,
+          needs_password: (hash == null)
+        });
+      });
     });
 };
 
diff --git a/lib/wsapi/complete_email_addition.js b/lib/wsapi/complete_email_addition.js
index 00a4422719e7dced325861bcaa9ca190be0cbd4b..cee00dfad1576104e323b675de02f7cb706e3328 100644
--- a/lib/wsapi/complete_email_addition.js
+++ b/lib/wsapi/complete_email_addition.js
@@ -5,10 +5,15 @@ logger = require('../logging.js').logger;
 exports.method = 'post';
 exports.writes_db = true;
 // XXX: see issue #290 - we want to require authentication here and update frontend code
-exports.authed = false;
+exports.authed = true;
 exports.args = ['token'];
 
 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.checkAuth
+
   db.gotVerificationSecret(req.body.token, undefined, function(e) {
     if (e) {
       logger.warn("couldn't complete email verification: " + e);
diff --git a/lib/wsapi/complete_user_creation.js b/lib/wsapi/complete_user_creation.js
index 1e586c14506f3126d5bb763c76f21896508af4c2..aeefb9dfa835b15193cd57f1f0368b94b246f03a 100644
--- a/lib/wsapi/complete_user_creation.js
+++ b/lib/wsapi/complete_user_creation.js
@@ -23,8 +23,8 @@ exports.process = function(req, res) {
   // 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.emailForVerificationSecret(req.body.token, function(email) {
-    if (!email) return res.json({ success: false} );
+  db.haveVerificationSecret(req.body.token, function(known) {
+    if (!known) return res.json({ success: false} );
 
     // now bcrypt the password
     wsapi.bcryptPassword(req.body.pass, function (err, hash) {
diff --git a/lib/wsapi/email_addition_status.js b/lib/wsapi/email_addition_status.js
index 87d387cdb2f1ea810d8a7a2f671388a256c5588a..f8b2cef27b94aa6d9fa56ef9949a59a90ac53da6 100644
--- a/lib/wsapi/email_addition_status.js
+++ b/lib/wsapi/email_addition_status.js
@@ -27,8 +27,8 @@ exports.process = function(req, res) {
       } else if (!req.session.pendingAddition) {
         res.json({ status: 'failed' });
       } else {
-        db.emailForVerificationSecret(req.session.pendingAddition, function (email) {
-          if (email) {
+        db.haveVerificationSecret(req.session.pendingAddition, function (known) {
+          if (known) {
             return res.json({ status: 'pending' });
           } else {
             delete req.session.pendingAddition;
diff --git a/lib/wsapi/email_for_token.js b/lib/wsapi/email_for_token.js
index 3a6f70748e49bc45a58f4c3a1cbd210933b1cf86..7e9497023f5d44885cb5ce15c2c6048e99903b87 100644
--- a/lib/wsapi/email_for_token.js
+++ b/lib/wsapi/email_for_token.js
@@ -13,7 +13,18 @@ exports.authed = false;
 exports.args = ['token'];
 
 exports.process = function(req, res) {
-  db.emailForVerificationSecret(req.query.token, function(email) {
-    res.json({ email: email });
+  db.emailForVerificationSecret(req.query.token, function(err, r) {
+    if (err) {
+      res.json({
+        success: false,
+        reason: err
+      });
+    } else {
+      res.json({
+        success: true,
+        email: r.email,
+        needs_password: r.needs_password
+      });
+    }
   });
 };
diff --git a/lib/wsapi/user_creation_status.js b/lib/wsapi/user_creation_status.js
index ea80db25824c9f86c1b93455a7e6c6daeee3cdef..04461bfcac795bd1ccf9a703020bfa8a9962e592 100644
--- a/lib/wsapi/user_creation_status.js
+++ b/lib/wsapi/user_creation_status.js
@@ -22,8 +22,8 @@ exports.process = function(req, res) {
 
   // if the secret is still in the database, it hasn't yet been verified and
   // verification is still pending
-  db.emailForVerificationSecret(req.session.pendingCreation, function (email) {
-    if (email) return res.json({ status: 'pending' });
+  db.haveVerificationSecret(req.session.pendingCreation, function (known) {
+    if (known) return res.json({ status: 'pending' });
     // if the secret isn't known, and we're not authenticated, then the user must authenticate
     // (maybe they verified the URL on a different browser, or maybe they canceled the account
     // creation)
diff --git a/tests/auth-with-assertion-test.js b/tests/auth-with-assertion-test.js
index 6b0c58d6642ec61bc19e50b2d1b0defa07ebce44..4ec2c50b89e0b5b6da2efa14e99a23307f467fdd 100755
--- a/tests/auth-with-assertion-test.js
+++ b/tests/auth-with-assertion-test.js
@@ -44,13 +44,9 @@ start_stop = require('./lib/start-stop.js'),
 wsapi = require('./lib/wsapi.js'),
 db = require('../lib/db.js'),
 config = require('../lib/configuration.js'),
-jwk = require('jwcrypto/jwk.js'),
-jwt = require('jwcrypto/jwt.js'),
-vep = require('jwcrypto/vep.js'),
-jwcert = require('jwcrypto/jwcert.js'),
 http = require('http'),
 querystring = require('querystring'),
-path = require("path");
+primary = require('./lib/primary.js');
 
 var suite = vows.describe('auth-with-assertion');
 
@@ -63,61 +59,19 @@ const TEST_DOMAIN = 'example.domain',
       TEST_EMAIL = 'testuser@' + TEST_DOMAIN,
       TEST_ORIGIN = 'http://127.0.0.1:10002';
 
-var token = undefined;
-
 // here we go!  let's authenticate with an assertion from
 // a primary.
 
-// now we need to generate a keypair and a certificate
-// signed by our in tree authority
-var g_keypair, g_cert;
-
-suite.addBatch({
-  "generating a keypair": {
-    topic: function() {
-      return jwk.KeyPair.generate("DS", 256)
-    },
-    "succeeds": function(r, err) {
-      assert.isObject(r);
-      assert.isObject(r.publicKey);
-      assert.isObject(r.secretKey);
-      g_keypair = r;
-    }
-  }
-});
-
-// for this trick we'll need the "secret" key of our built in
-// primary
-var g_privKey = jwk.SecretKey.fromSimpleObject(
-  JSON.parse(require('fs').readFileSync(
-    path.join(__dirname, '..', 'example', 'primary', 'sample.privatekey'))));
-
-
-suite.addBatch({
-  "generting a certificate": {
-    topic: function() {
-      var domain = process.env['SHIMMED_DOMAIN'];
-
-      var expiration = new Date();
-      expiration.setTime(new Date().valueOf() + 60 * 60 * 1000);
-      g_cert = new jwcert.JWCert(TEST_DOMAIN, expiration, new Date(),
-                                 g_keypair.publicKey, {email: TEST_EMAIL}).sign(g_privKey);
-      return g_cert;
-    },
-    "works swimmingly": function(cert, err) {
-      assert.isString(cert);
-      assert.lengthOf(cert.split('.'), 3);
-    }
-  }
+var primaryUser = new primary({
+  email: TEST_EMAIL,
+  domain: TEST_DOMAIN
 });
 
-// now let's generate an assertion using the cert
+// now let's generate an assertion using this user
 suite.addBatch({
   "generating an assertion": {
     topic: function() {
-      var expirationDate = new Date(new Date().getTime() + (2 * 60 * 1000));
-      var tok = new jwt.JWT(null, expirationDate, TEST_ORIGIN);
-      return vep.bundleCertsAndAssertion([g_cert], tok.sign(g_keypair.secretKey));
+      return primaryUser.getAssertion(TEST_ORIGIN);
     },
     "succeeds": function(r, err) {
       assert.isString(r);
diff --git a/tests/db-test.js b/tests/db-test.js
index f9ae288704b614a7b0c6a7b5cc79c1c22ca91cb6..59d4b70cf73457b3fb5ca426e13fd95bcafea579 100755
--- a/tests/db-test.js
+++ b/tests/db-test.js
@@ -113,8 +113,8 @@ suite.addBatch({
       topic: function(secret) {
         db.emailForVerificationSecret(secret, this.callback);
       },
-      "matches expected email": function(storedEmail) {
-        assert.strictEqual('lloyd@nowhe.re', storedEmail);
+      "matches expected email": function(err, r) {
+        assert.strictEqual(r.email, 'lloyd@nowhe.re');
       }
     },
     "fetch secret for email": {
diff --git a/tests/lib/primary.js b/tests/lib/primary.js
new file mode 100644
index 0000000000000000000000000000000000000000..404f422dfad3b1b44792db8ac1120583f3b10839
--- /dev/null
+++ b/tests/lib/primary.js
@@ -0,0 +1,30 @@
+const
+jwk = require('jwcrypto/jwk.js'),
+jwt = require('jwcrypto/jwt.js'),
+vep = require('jwcrypto/vep.js'),
+jwcert = require('jwcrypto/jwcert.js'),
+path = require("path");
+
+// the private secret of our built-in primary
+const g_privKey = jwk.SecretKey.fromSimpleObject(
+  JSON.parse(require('fs').readFileSync(
+    path.join(__dirname, '..', '..', 'example', 'primary', 'sample.privatekey'))));
+
+
+function User(options) {
+  // upon allocation of a user, we'll gen a keypair and get a signed cert
+  this._keyPair = jwk.KeyPair.generate("DS", 256);
+  var expiration = new Date();
+  expiration.setTime(new Date().valueOf() + 60 * 60 * 1000);
+  this._cert = new jwcert.JWCert(
+    options.domain, expiration, new Date(),
+    this._keyPair.publicKey, {email: options.email}).sign(g_privKey);
+}
+
+User.prototype.getAssertion = function(origin) {
+  var expirationDate = new Date(new Date().getTime() + (2 * 60 * 1000));
+  var tok = new jwt.JWT(null, expirationDate, origin);
+  return vep.bundleCertsAndAssertion([this._cert], tok.sign(this._keyPair.secretKey));
+}
+
+module.exports = User;
\ No newline at end of file
diff --git a/tests/lib/start-stop.js b/tests/lib/start-stop.js
index 0b40ec633083c41d2734c828c8f32c82cdb8ee45..d25ff3fb0c5f22bfbaae3a6772a43c1a38b14fc0 100644
--- a/tests/lib/start-stop.js
+++ b/tests/lib/start-stop.js
@@ -54,7 +54,8 @@ var tokenStack = [];
 
 exports.waitForToken = function(cb) {
   if (tokenStack.length) {
-    cb(tokenStack.shift());
+    var t = tokenStack.shift();
+    process.nextTick(function() { cb(t); });
   }
   else {
     if (nextTokenFunction) throw "can't wait for a verification token when someone else is!";
diff --git a/tests/primary-then-secondary-test.js b/tests/primary-then-secondary-test.js
new file mode 100755
index 0000000000000000000000000000000000000000..93607786fc5b43ff79b6b328ceadfde3c90d1472
--- /dev/null
+++ b/tests/primary-then-secondary-test.js
@@ -0,0 +1,179 @@
+#!/usr/bin/env node
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+require('./lib/test_env.js');
+
+const
+assert = require('assert'),
+vows = require('vows'),
+start_stop = require('./lib/start-stop.js'),
+wsapi = require('./lib/wsapi.js'),
+primary = require('./lib/primary.js');
+
+var suite = vows.describe('primary-then-secondary');
+
+// start up a pristine server
+start_stop.addStartupBatches(suite);
+
+// this test excercises the codepath whereby a user adds
+// a primary email address, then a secondary, then another
+// secondary.  It checks that the critical wsapi calls
+// along the way perform as expected
+
+// first we'll need to authenticate a user with an assertion from a
+// primary IdP
+
+const TEST_DOMAIN = 'example.domain',
+      TEST_EMAIL = 'testuser@' + TEST_DOMAIN,
+      TEST_ORIGIN = 'http://127.0.0.1:10002',
+      TEST_PASS = 'fakepass',
+      SECONDARY_EMAIL = 'second@fakeemail.com';
+
+var primaryUser = new primary({
+  email: TEST_EMAIL,
+  domain: TEST_DOMAIN
+});
+
+// now let's generate an assertion using this user
+suite.addBatch({
+  "generating an assertion": {
+    topic: function() {
+      return primaryUser.getAssertion(TEST_ORIGIN);
+    },
+    "succeeds": function(r, err) {
+      assert.isString(r);
+    },
+    "and logging in with the assertion succeeds": {
+      topic: function(assertion)  {
+        wsapi.post('/wsapi/auth_with_assertion', {
+          email: TEST_EMAIL,
+          assertion: assertion
+        }).call(this);
+      },
+      "works": function(r, err) {
+        var resp = JSON.parse(r.body);
+        assert.isObject(resp);
+        assert.isTrue(resp.success);
+      }
+    }
+  }
+});
+
+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": {
+    topic: wsapi.post('/wsapi/stage_email', {
+      email: SECONDARY_EMAIL,
+      site:'fakesite.com'
+    }),
+    "works": function(r, err) {
+      assert.strictEqual(r.code, 200);
+    },
+    "and get a token": {
+      topic: function() {
+        start_stop.waitForToken(this.callback);
+      },
+      "successfully": function (t) {
+        this._token = t;
+        assert.strictEqual(typeof t, 'string');
+      },
+      "and complete":  {
+        topic: function(t) {
+          wsapi.get('/wsapi/email_for_token', {
+            token: t
+          }).call(this);
+        },
+        "we need to set our password": function (r) {
+          r = JSON.parse(r.body);
+          assert.ok(r.needs_password);
+        },
+        "with": {
+          topic: function() {
+            wsapi.post('/wsapi/complete_email_addition', { token: this._token }).call(this);
+          },
+          "no password fails": function(r, err) {
+            assert.equal(r.code, 200);
+            assert.strictEqual(JSON.parse(r.body).success, false);
+          },
+          "a password": {
+            topic: function() {
+              wsapi.post('/wsapi/complete_email_addition', {
+                token: this._token,
+                pass: TEST_PASS
+              }).call(this);
+            },
+            "succeeds": function(r, err) {
+              assert.equal(r.code, 200);
+              assert.strictEqual(JSON.parse(r.body).success, true);
+            }
+          }
+        }
+      }
+    }
+  }
+});
+
+suite.addBatch({
+  "authentication with first email": {
+    topic: wsapi.post('/wsapi/authenticate_user', {
+      email: TEST_EMAIL,
+      passwd: TEST_PASS
+    }),
+    "works": function(r, err) {
+      assert.strictEqual(r.code, 200);
+    },
+  },
+  "authentication with second email": {
+    topic: wsapi.post('/wsapi/authenticate_user', {
+      email: SECONDARY_EMAIL,
+      passwd: TEST_PASS
+    }),
+    "works": function(r, err) {
+      assert.strictEqual(r.code, 200);
+    },
+  }
+});
+
+
+// shut the server down and cleanup
+start_stop.addShutdownBatches(suite);
+
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);