diff --git a/bin/load_gen b/bin/load_gen
index aa858490e194c7a1af8834a882d31289f617d530..a5b33cf11b66c6a0917c9578c29e2bfbefe1a633 100755
--- a/bin/load_gen
+++ b/bin/load_gen
@@ -61,7 +61,7 @@ var argv = require('optimist')
 .string('s')
 .describe('s', 'base URL to browserid server')
 .check(function(argv) {
-  return (typeof argv.s === 'string' || argv.l) != undefined;
+  return (argv.h || typeof argv.s === 'string' || argv.l) != undefined;
 })
 .alias('v', 'verifier')
 .describe('v', 'base URL to verifier service (default is browserid server + \'/verify\')')
diff --git a/lib/load_gen/common.js b/lib/load_gen/common.js
index 0a7cf61789c473e022578ff4195436de7309cd06..cd94b5ade023e161b63719957271f56ef26bdde2 100644
--- a/lib/load_gen/common.js
+++ b/lib/load_gen/common.js
@@ -67,7 +67,7 @@ exports.genAssertionAndVerify = function(cfg, user, ctx, email, audience, cb) {
     try {
       if (!typeof JSON.parse(r.body) === 'object') throw 'bogus response';
     } catch(e) {
-      return cb(e.toString() + " - " + r.body);
+      return cb(e.toString() + (r ? (" - " + r.body) : ""));
     }
 
     var assertion = crypto.getAssertion({
diff --git a/resources/static/css/style.css b/resources/static/css/style.css
index 4b20930d2604b35332b7cb58574f7934f6829b23..3447756e277df0647906734557dec594441950bc 100644
--- a/resources/static/css/style.css
+++ b/resources/static/css/style.css
@@ -419,6 +419,11 @@ button.delete:active {
 
 #edit_password {
   margin-bottom: 10px;
+  display: none;
+}
+
+.canSetPassword #edit_password {
+  display: block;
 }
 
 #edit_password label {
diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/controllers/actions.js
index b6b935f0558ab574609aedf6098e6624bd398b1b..01d972fd16cd59ff49853265762e43b06059f427 100644
--- a/resources/static/dialog/controllers/actions.js
+++ b/resources/static/dialog/controllers/actions.js
@@ -157,12 +157,6 @@ BrowserID.Modules.Actions = (function() {
       user.logoutUser(self.publish.bind(self, "logged_out"), self.getErrorDialog(errors.logoutUser));
     },
 
-    doSyncThenPickEmail: function(options) {
-      var self = this;
-      user.syncEmails(self.doPickEmail.bind(self, options),
-        self.getErrorDialog(errors.syncEmails));
-    },
-
     doCheckAuth: function() {
       var self=this;
       user.checkAuthenticationAndSync(function(authenticated) {
diff --git a/resources/static/dialog/controllers/required_email.js b/resources/static/dialog/controllers/required_email.js
index 1ab9faf3204fa989df54ecaa1cb89eb442fd4ecf..59b4f351dba29400c97abc605b0a96f9a1873695 100644
--- a/resources/static/dialog/controllers/required_email.js
+++ b/resources/static/dialog/controllers/required_email.js
@@ -63,39 +63,34 @@ BrowserID.Modules.RequiredEmail = (function() {
   function signIn(callback) {
     var self = this;
 
-    // If the user is already authenticated and they own this address, sign
-    // them in.
-    if (authenticated) {
-      if(primaryInfo) {
-        closePrimaryUser.call(self, callback);
-      }
-      else {
-        dialogHelpers.getAssertion.call(self, email, callback);
-      }
+    function getAssertion() {
+      dialogHelpers.getAssertion.call(self, email, callback);
+    }
+
+    if(primaryInfo) {
+      // With a primary, just go to the primary flow, it'll be taken care of.
+      closePrimaryUser.call(self, callback);
+    }
+    else if (authenticated) {
+      // If the user is already authenticated and they own this address, sign
+      // them in.
+      getAssertion();
     }
     else {
-      if(primaryInfo) {
-        closePrimaryUser.call(self, callback);
-      }
-      else {
-        // If the user is not already authenticated, but they potentially own
-        // this address, try and sign them in and generate an assertion if they
-        // get the password right.
-        var password = helpers.getAndValidatePassword("#password");
-        if (password) {
-          dialogHelpers.authenticateUser.call(self, email, password, function(authenticated) {
-            if (authenticated) {
-              // Now that the user has authenticated, sync their emails and get an
-              // assertion for the email we care about.
-              user.syncEmailKeypair(email, function() {
-                dialogHelpers.getAssertion.call(self, email, callback);
-              }, self.getErrorDialog(errors.syncEmailKeypair, callback));
-            }
-            else {
-              callback && callback();
-            }
-          });
-        }
+      // If the user is not already authenticated, but they potentially own
+      // this address, try and sign them in and generate an assertion if they
+      // get the password right.
+      var password = helpers.getAndValidatePassword("#password");
+      if (password) {
+        dialogHelpers.authenticateUser.call(self, email, password, function(authenticated) {
+          if (authenticated) {
+            // Now that the user has authenticated, we can get an assertion.
+            getAssertion();
+          }
+          else {
+            callback && callback();
+          }
+        });
       }
     }
   }
diff --git a/resources/static/dialog/resources/state_machine.js b/resources/static/dialog/resources/state_machine.js
index e4b86b772e3eb3e3718f23f708731ba393dfe30f..488c57638fdab71539e9ba80252d018d63aa563b 100644
--- a/resources/static/dialog/resources/state_machine.js
+++ b/resources/static/dialog/resources/state_machine.js
@@ -202,10 +202,6 @@
       startState("doEmailChosen", info);
     });
 
-    subscribe("authenticate_with_required_email", function(msg, info) {
-      startState("doAuthenticateWithRequiredEmail", info);
-    });
-
     subscribe("pick_email", function() {
       startState("doPickEmail", {
         origin: self.hostname,
@@ -216,13 +212,18 @@
     subscribe("email_chosen", function(msg, info) {
       var idInfo = storage.getEmail(info.email);
       if(idInfo) {
-        if(idInfo.type === "primary" && !idInfo.cert) {
-          // If the email is a primary, and their cert is not available,
-          // throw the user down the primary flow.
-          // Doing so will catch cases where the primary certificate is expired
-          // and the user must re-verify with their IdP.  This flow will
-          // generate its own assertion when ready.
-          publish("primary_user", info);
+        if(idInfo.type === "primary") {
+          if(idInfo.cert) {
+            startState("doEmailChosen", info);
+          }
+          else {
+            // If the email is a primary, and their cert is not available,
+            // throw the user down the primary flow.
+            // Doing so will catch cases where the primary certificate is expired
+            // and the user must re-verify with their IdP.  This flow will
+            // generate its own assertion when ready.
+            publish("primary_user", info);
+          }
         }
         else {
           startState("doEmailChosen", info);
@@ -242,10 +243,7 @@
     });
 
     subscribe("authenticated", function(msg, info) {
-      startState("doSyncThenPickEmail", {
-        origin: self.hostname,
-        allow_persistent: self.allowPersistent
-      });
+      mediator.publish("pick_email");
     });
 
     subscribe("forgot_password", function(msg, info) {
diff --git a/resources/static/pages/manage_account.js b/resources/static/pages/manage_account.js
index 896099c622d8dbe95e66712f6e64ad8089522125..44e38a9e7a56dafed6153877c8ffdde499c8f60c 100644
--- a/resources/static/pages/manage_account.js
+++ b/resources/static/pages/manage_account.js
@@ -131,7 +131,6 @@ BrowserID.manageAccount = (function() {
   }
 
   function syncAndDisplayEmails(oncomplete) {
-
     user.syncEmails(function() {
       displayStoredEmails(oncomplete);
     }, pageHelpers.getFailure(errors.syncEmails, oncomplete));
@@ -265,6 +264,13 @@ BrowserID.manageAccount = (function() {
     storage.manage_page.set("has_visited_manage_page", true);
   }
 
+  function displayChangePassword(oncomplete) {
+    user.canSetPassword(function(canSetPassword) {
+      dom[canSetPassword ? "addClass" : "removeClass"]("body", "canSetPassword");
+      oncomplete && oncomplete();
+    }, pageHelpers.getFailure(errors.hasSecondary));
+  }
+
   function init(options, oncomplete) {
     options = options || {};
 
@@ -277,9 +283,10 @@ BrowserID.manageAccount = (function() {
     dom.bindEvent("button.done", "click", cancelEdit);
     dom.bindEvent("#edit_password_form", "submit", cancelEvent(changePassword));
 
-    syncAndDisplayEmails(oncomplete);
-
-    displayHelpTextToNewUser();
+    syncAndDisplayEmails(function() {
+      displayHelpTextToNewUser();
+      displayChangePassword(oncomplete);
+    });
   }
 
   // BEGIN TESTING API
diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js
index b40308c96e66bbfd3fa637b8a6a1b253be6669f8..e8d2c148fd5b933a2fe7cbf2680dbad0a7df07d2 100644
--- a/resources/static/shared/user.js
+++ b/resources/static/shared/user.js
@@ -521,6 +521,18 @@ BrowserID.User = (function() {
       }, onFailure);
     },
 
+    /**
+     * Check if the user can set their password.  Only returns true for users
+     * with secondary accounts
+     * @method canSetPassword
+     * @param {function} [onComplete] - Called on with boolean flag on
+     * successful completion.
+     * @param {function} [onFailure] - Called on error.
+     */
+    canSetPassword: function(onComplete, onFailure) {
+      User.hasSecondary(onComplete, onFailure);
+    },
+
     /**
      * Set the initial password of the current user.
      * @method setPassword
@@ -691,7 +703,8 @@ BrowserID.User = (function() {
     },
 
     /**
-     * Authenticate the user with the given email and password.
+     * Authenticate the user with the given email and password.  This will sync
+     * the user's addresses.
      * @method authenticate
      * @param {string} email - Email address to authenticate.
      * @param {string} password - Password.
@@ -702,8 +715,14 @@ BrowserID.User = (function() {
     authenticate: function(email, password, onComplete, onFailure) {
       network.authenticate(email, password, function(authenticated) {
         setAuthenticationStatus(authenticated);
-        if (onComplete)
+
+        if(authenticated) {
+          User.syncEmails(function() {
+            onComplete && onComplete(authenticated);
+          }, onFailure);
+        } else if (onComplete) {
           onComplete(authenticated);
+        }
       }, onFailure);
     },
 
@@ -951,7 +970,7 @@ BrowserID.User = (function() {
             else {
               // we have no key for this identity, go generate the key,
               // sync it and then get the assertion recursively.
-              User.syncEmailKeypair(email, function() {
+              User.syncEmailKeypair(email, function(status) {
                 User.getAssertion(email, audience, onComplete, onFailure);
               }, onFailure);
             }
@@ -1033,7 +1052,30 @@ BrowserID.User = (function() {
           onComplete(false);
         }
       }, onFailure);
+    },
+
+    /**
+     * Check if the user has any secondary addresses.
+     * @method hasSecondary
+     * @param {function} onComplete - called with true if user has at least one
+     * email address, false otw.
+     * @param {function} onFailure - called on XHR failure.
+     */
+    hasSecondary: function(onComplete, onFailure) {
+      var hasSecondary = false,
+          emails = storage.getEmails();
+
+      for(var key in emails) {
+        if(emails[key].type === "secondary") {
+          hasSecondary = true;
+          break;
+        }
+      }
+
+      onComplete(hasSecondary);
     }
+
+
   };
 
   User.setOrigin(document.location.host);
diff --git a/resources/static/test/qunit/controllers/required_email_unit_test.js b/resources/static/test/qunit/controllers/required_email_unit_test.js
index e806045deacf239d1d77316c79e478c89642f972..ead470e41ce7548b7afa2f198efc722fd6281572 100644
--- a/resources/static/test/qunit/controllers/required_email_unit_test.js
+++ b/resources/static/test/qunit/controllers/required_email_unit_test.js
@@ -357,7 +357,7 @@
       authenticated: false
     });
 
-    var email = "registered@testuser.com";
+    var email = "testuser@testuser.com";
     xhr.useResult("known_secondary");
 
     createController({
diff --git a/resources/static/test/qunit/mocks/xhr.js b/resources/static/test/qunit/mocks/xhr.js
index f77340dc6e2867596341b02682b400a84cb9543b..7438c4dba2c3029f4c991fbd4aeb5068344765c9 100644
--- a/resources/static/test/qunit/mocks/xhr.js
+++ b/resources/static/test/qunit/mocks/xhr.js
@@ -1,5 +1,5 @@
 /*jshint browsers:true, forin: true, laxbreak: true */
-/*global wrappedAsyncTest: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */
+/*global start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
@@ -113,9 +113,11 @@ BrowserID.Mocks.xhr = (function() {
       "get /wsapi/email_addition_status?email=registered%40testuser.com mustAuth": { status: "mustAuth" },
       "get /wsapi/email_addition_status?email=registered%40testuser.com noRegistration": { status: "noRegistration" },
       "get /wsapi/email_addition_status?email=registered%40testuser.com ajaxError": undefined,
-      "get /wsapi/list_emails valid": {"testuser@testuser.com":{}},
+      "get /wsapi/list_emails valid": {"testuser@testuser.com":{ type: "secondary" }},
+      //"get /wsapi/list_emails known_secondary": {"registered@testuser.com":{ type: "secondary" }},
+      "get /wsapi/list_emails primary": {"testuser@testuser.com": { type: "primary" }},
       "get /wsapi/list_emails multiple": {"testuser@testuser.com":{}, "testuser2@testuser.com":{}},
-      "get /wsapi/list_emails no_identities": [],
+      "get /wsapi/list_emails no_identities": {},
       "get /wsapi/list_emails ajaxError": undefined,
       // Used in conjunction with registration to do a complete userflow
       "get /wsapi/list_emails complete": {"registered@testuser.com":{}},
diff --git a/resources/static/test/qunit/pages/manage_account_unit_test.js b/resources/static/test/qunit/pages/manage_account_unit_test.js
index 50c679253ce5452f2a9778ea726c946c79f54f01..0d12a81f807edcb3c905325caa372cd7063dbc8e 100644
--- a/resources/static/test/qunit/pages/manage_account_unit_test.js
+++ b/resources/static/test/qunit/pages/manage_account_unit_test.js
@@ -38,12 +38,10 @@
   "use strict";
 
   var bid = BrowserID,
-      user = bid.User,
       xhr = bid.Mocks.xhr,
       errorScreen = bid.Screens.error,
+      storage = bid.Storage,
       testHelpers = bid.TestHelpers,
-      validToken = true,
-      TEST_ORIGIN = "http://browserid.org",
       tooltip = bid.Tooltip,
       mocks = {
         confirm: function() { return true; },
@@ -53,7 +51,6 @@
   module("pages/manage_account", {
     setup: function() {
       testHelpers.setup();
-      user.setOrigin(TEST_ORIGIN);
       bid.Renderer.render("#page_head", "site/index", {});
       mocks.document.location = "";
     },
@@ -166,6 +163,24 @@
     });
   });
 
+  asyncTest("user with only primary emails should not have 'canSetPassword' class", function() {
+    xhr.useResult("primary");
+
+    bid.manageAccount(mocks, function() {
+      equal($("body").hasClass("canSetPassword"), false, "canSetPassword class not added to body");
+      start();
+    });
+  });
+
+  asyncTest("user with >= 1 secondary email should see have 'canSetPassword' class", function() {
+    storage.addEmail("primary_user@primaryuser.com", { type: "secondary" });
+
+    bid.manageAccount(mocks, function() {
+      equal($("body").hasClass("canSetPassword"), true, "canSetPassword class added to body");
+      start();
+    });
+  });
+
   asyncTest("changePassword with missing old password, expect tooltip", function() {
     bid.manageAccount(mocks, function() {
       $("#old_password").val("");
diff --git a/resources/static/test/qunit/resources/state_machine_unit_test.js b/resources/static/test/qunit/resources/state_machine_unit_test.js
index 0a413215b3c44b8ecb223ae78c36ad619071c74e..d0df3c9e6837e4b893768495ce4267030d499a27 100644
--- a/resources/static/test/qunit/resources/state_machine_unit_test.js
+++ b/resources/static/test/qunit/resources/state_machine_unit_test.js
@@ -151,7 +151,7 @@
   test("authenticated", function() {
     mediator.publish("authenticated");
 
-    ok(actions.called.doSyncThenPickEmail, "doSyncThenPickEmail has been called");
+    ok(actions.called.doPickEmail, "doPickEmail has been called");
   });
 
   test("forgot_password", function() {
@@ -261,13 +261,16 @@
   });
 
 
-  test("email_chosen with secondary email - call doEmailChosen", function() {
+  test("email_chosen with secondary email, user must authenticate - call doAuthenticateWithRequiredEmail", function() {
+
+  });
+
+  test("email_chosen with secondary email, user authenticated to secondary - call doEmailChosen", function() {
     var email = "testuser@testuser.com";
     storage.addEmail(email, { type: "secondary" });
     mediator.publish("email_chosen", { email: email });
 
     equal(actions.called.doEmailChosen, true, "doEmailChosen called");
-
   });
 
   test("email_chosen with primary email - call doProvisionPrimaryUser", function() {
diff --git a/resources/static/test/qunit/shared/user_unit_test.js b/resources/static/test/qunit/shared/user_unit_test.js
index 8ba403ec6bc1638e6359a66c018d3e28371f5cde..50564754d25a5725eeeb25d6cd69b1f740bc8ad8 100644
--- a/resources/static/test/qunit/shared/user_unit_test.js
+++ b/resources/static/test/qunit/shared/user_unit_test.js
@@ -469,15 +469,45 @@ var vep = require("./vep");
     );
   });
 
-  /*
-  asyncTest("setPassword", function() {
-    lib.setPassword("password", function() {
-      // XXX fill this in.
-      ok(true);
+  asyncTest("canSetPassword with only primary addresses - expect false", function() {
+    storage.addEmail("testuser@testuser.com", { type: "primary" });
+
+    lib.canSetPassword(function(status) {
+      equal(false, status, "status is false with user with only primaries");
       start();
-    });
+    }, testHelpers.unexpectedFailure);
   });
-  */
+
+  asyncTest("canSetPassword with secondary addresses - expect true", function() {
+    storage.addEmail("testuser@testuser.com", { type: "secondary" });
+
+    lib.canSetPassword(function(status) {
+      equal(true, status, "status is true with user with secondaries");
+      start();
+    }, testHelpers.unexpectedFailure);
+  });
+
+  asyncTest("setPassword with XHR failure", function() {
+    xhr.useResult("ajaxError");
+
+    lib.setPassword(
+      "password",
+      testHelpers.unexpectedSuccess,
+      testHelpers.expectedXHRFailure
+    );
+  });
+
+  asyncTest("setPassword success", function() {
+    lib.setPassword(
+      "password",
+      function(status) {
+        ok(status, true, "status is true for success");
+        start();
+      },
+      testHelpers.expectedXHRFailure
+    );
+  });
+
   asyncTest("requestPasswordReset with known email", function() {
     lib.requestPasswordReset("registered@testuser.com", function(status) {
       equal(status.success, true, "password reset for known user");
@@ -507,9 +537,11 @@ var vep = require("./vep");
   });
 
 
-  asyncTest("authenticate with valid credentials", function() {
+  asyncTest("authenticate with valid credentials, also syncs email with server", function() {
     lib.authenticate("testuser@testuser.com", "testuser", function(authenticated) {
       equal(true, authenticated, "we are authenticated!");
+      var emails = lib.getStoredEmailKeypairs();
+      equal(_.size(emails) > 0, true, "emails have been synced to server");
       start();
     }, testHelpers.unexpectedXHRFailure);
   });
@@ -1158,4 +1190,19 @@ var vep = require("./vep");
     );
   });
 
+  asyncTest("hasSecondary returns false if the user has 0 secondary email address", function() {
+    lib.hasSecondary(function(hasSecondary) {
+      equal(hasSecondary, false, "hasSecondary is false");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("hasSecondary returns true if the user has at least one secondary email address", function() {
+    storage.addEmail("testuser@testuser.com", { type: "secondary" });
+    lib.hasSecondary(function(hasSecondary) {
+      equal(hasSecondary, true, "hasSecondary is true");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
 }());
diff --git a/tests/verifier-test.js b/tests/verifier-test.js
index d0db8a31a77e1f9b6809e3766e137d6d7be2fb09..c960717b24fb5046e97daca1f18e789e55a9be5f 100755
--- a/tests/verifier-test.js
+++ b/tests/verifier-test.js
@@ -604,9 +604,12 @@ function make_crazy_assertion_tests(new_style) {
         tok.sign(g_keypair.secretKey),
         new_style);
     },
-    "and removing the last char from it": {
+    "and removing the last two chars from it": {
       topic: function(assertion) {
-        assertion = assertion.substr(0, assertion.length - 1);
+        // we used to chop off one char, but because of
+        // robustness in base64-decoding, that still worked 25%
+        // of the time. No need to build this knowledge in here.
+        assertion = assertion.substr(0, assertion.length - 2);
         wsapi.post('/verify', {
           audience: TEST_ORIGIN,
           assertion: assertion