diff --git a/resources/static/css/style.css b/resources/static/css/style.css
index 817012bcab516e04c2772cf28210079b5225cb45..951553961de674231e3dee2f3b05e00b00674d2f 100644
--- a/resources/static/css/style.css
+++ b/resources/static/css/style.css
@@ -649,14 +649,18 @@ h1 {
           border-radius: 3px;
 }
 
-#signUpForm > .password_entry {
-  display: none;
+#signUpForm > .siteinfo {
+  margin-bottom: 10px;
 }
 
-.siteinfo, #congrats {
+.siteinfo, #congrats, #signUpForm > .password_entry, .enter_password .hint {
   display: none;
 }
 
+.enter_password #signUpForm > .password_entry {
+  display: block;
+}
+
 #congrats p {
     color: #62615F;
     text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
diff --git a/resources/static/dialog/controllers/page.js b/resources/static/dialog/controllers/page.js
index 8f29623cdfbb3b22ba7ea26fe16f0956bd3d49a2..08a3711b5fdde34cbf242c50d4707a8a47f881a3 100644
--- a/resources/static/dialog/controllers/page.js
+++ b/resources/static/dialog/controllers/page.js
@@ -75,6 +75,15 @@ BrowserID.Modules.PageModule = (function() {
       }
     },
 
+    checkRequired: function(options) {
+      var list = [].slice.call(arguments, 1);
+      for(var item, index = 0; item = list[index]; ++index) {
+        if(!options.hasOwnProperty(item)) {
+          throw "missing config option: " + item;
+        }
+      }
+    },
+
     start: function(options) {
       var self=this;
       self.bind("form", "submit", onSubmit);
diff --git a/resources/static/pages/add_email_address.js b/resources/static/pages/add_email_address.js
index d14aa7befac874cf5d338d39d38f2587bd877ab6..c9b5238f5c7e8e950f003ed8847d493ad93030c7 100644
--- a/resources/static/pages/add_email_address.js
+++ b/resources/static/pages/add_email_address.js
@@ -34,47 +34,124 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-(function() {
+BrowserID.addEmailAddress = (function() {
   "use strict";
 
   var ANIMATION_TIME=250,
       bid = BrowserID,
-      dom = bid.DOM;
+      user = bid.User,
+      storage = bid.Storage,
+      errors = bid.Errors,
+      pageHelpers = bid.PageHelpers,
+      dom = bid.DOM,
+      token,
+      sc;
 
-  function emailRegistrationSuccess(info) {
+  function showError(el) {
+    $(".hint,#signUpForm").hide();
+    $(el).fadeIn(ANIMATION_TIME);
+  }
+
+  function emailRegistrationComplete(oncomplete, info) {
+    var valid = info.valid;
+    if (valid) {
+      emailRegistrationSuccess(info);
+    }
+    else {
+      showError("#cannotconfirm");
+    }
+    oncomplete && oncomplete(valid);
+  }
 
-    dom.setInner("#email", info.email);
+  function showRegistrationInfo(info) {
+    dom.setInner(".email", info.email);
 
     if (info.origin) {
       dom.setInner(".website", info.origin);
       $(".siteinfo").show();
     }
+  }
+
+  function emailRegistrationSuccess(info) {
+    dom.addClass("body", "complete");
+
+    showRegistrationInfo(info);
 
-    $("#signUpForm").delay(2000).fadeOut(ANIMATION_TIME, function() {
-      $("#congrats").fadeIn(ANIMATION_TIME);
-    });
+    setTimeout(function() {
+      pageHelpers.replaceFormWithNotice("#congrats");
+    }, 2000);
   }
 
-  function showError(el) {
-    $(".hint").hide();
-    $(el).fadeIn(ANIMATION_TIME);
+  function userMustEnterPassword() {
+    var emails = storage.getEmails(),
+        length = 0,
+        anySecondaries = _.find(emails, function(item) { length++; return item.type === "secondary"; });
+
+    return length && !anySecondaries;
+  }
+
+  function verifyWithoutPassword(oncomplete) {
+    user.verifyEmailNoPassword(token,
+      emailRegistrationComplete.bind(null, oncomplete),
+      pageHelpers.getFailure(errors.verifyEmail, oncomplete)
+    );
+  }
+
+  function verifyWithPassword(oncomplete) {
+    var pass = dom.getInner("#password"),
+        vpass = dom.getInner("#vpassword"),
+        valid = bid.Validation.passwordAndValidationPassword(pass, vpass);
+
+    if(valid) {
+      user.verifyEmailWithPassword(token, pass,
+        emailRegistrationComplete.bind(null, oncomplete),
+        pageHelpers.getFailure(errors.verifyEmail, oncomplete)
+      );
+    }
+    else {
+      oncomplete && oncomplete(false);
+    }
   }
 
-  BrowserID.addEmailAddress = function(token, oncomplete) {
-    var user = BrowserID.User;
+  function startVerification(oncomplete) {
+    user.tokenInfo(token, function(info) {
+      if(info) {
+        showRegistrationInfo(info);
 
-    user.verifyEmail(token, function onSuccess(info) {
-      if (info.valid) {
-        emailRegistrationSuccess(info);
+        if(userMustEnterPassword()) {
+          dom.addClass("body", "enter_password");
+          oncomplete(true);
+        }
+        else {
+          verifyWithoutPassword(oncomplete);
+        }
       }
       else {
         showError("#cannotconfirm");
+        oncomplete(false);
+      }
+    }, pageHelpers.getFailure(errors.getTokenInfo, oncomplete));
+  }
+
+  var Module = bid.Modules.PageModule.extend({
+    start: function(options) {
+      function oncomplete(status) {
+        options.ready && options.ready(status);
       }
-      oncomplete && oncomplete();
-    }, function onFailure() {
-      // XXX This should use a real error page.
-      showError("#cannotcommunicate");
-      oncomplete && oncomplete();
-    });
-  };
+
+      this.checkRequired(options, "token");
+
+      token = options.token;
+
+      startVerification(oncomplete);
+
+      sc.start.call(this, options);
+    },
+
+    submit: verifyWithPassword
+  });
+
+  sc = Module.sc;
+
+  return Module;
 }());
diff --git a/resources/static/pages/browserid.js b/resources/static/pages/start.js
similarity index 95%
rename from resources/static/pages/browserid.js
rename to resources/static/pages/start.js
index f872832b3ffed6d259ea5796f3ad8cada5293e06..060d16e6e0b16320d6b6aafe0871189ad4ea3c66 100644
--- a/resources/static/pages/browserid.js
+++ b/resources/static/pages/start.js
@@ -59,8 +59,11 @@ $(function() {
   else if (path === "/forgot") {
     bid.forgot();
   }
-  else if (token && path === "/add_email_address") {
-    bid.addEmailAddress(token);
+  else if (path === "/add_email_address") {
+    var module = bid.addEmailAddress.create();
+    module.start({
+      token: token
+    });
   }
   else if(token && path === "/verify_email_address") {
     bid.verifyEmailAddress(token);
diff --git a/resources/static/shared/error-messages.js b/resources/static/shared/error-messages.js
index 9f96220519ce985403b2f6fd745c5684d984f9b5..789025acd7c72f919e2f1b81176ec3de6b27f04b 100644
--- a/resources/static/shared/error-messages.js
+++ b/resources/static/shared/error-messages.js
@@ -150,10 +150,18 @@ BrowserID.Errors = (function(){
       title: "Sync Keys for Address"
     },
 
+    tokenInfo: {
+      title: "Getting Token Info"
+    },
+
     updatePassword: {
       title: "Updating password"
     },
 
+    verifyEmail: {
+      title: "Verifying email address"
+    },
+
     xhrError: {
       title: "Communication Error"
     }
diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js
index 56fadfd9980b0c749bd9a7ecf08e7c41507a8796..fcbcc9885cf34d5e3596e2e4d44fe8d3b295a3e7 100644
--- a/resources/static/shared/user.js
+++ b/resources/static/shared/user.js
@@ -417,31 +417,44 @@ BrowserID.User = (function() {
       cancelRegistrationPoll();
     },
 
+    /**
+     * Get site and email info for a token
+     * @method tokenInfo
+     * @param {string} token
+     * @param {function} [onComplete]
+     * @param {function} [onFailure]
+     */
+    tokenInfo: function(token, onComplete, onFailure) {
+      network.emailForVerificationToken(token, function (email) {
+        var info = email ? {
+          email: email,
+          origin: storage.getStagedOnBehalfOf()
+        } : null;
+
+        onComplete(info);
+      }, onFailure);
+
+    },
+
     /**
      * Verify a user
      * @method verifyUser
      * @param {string} token - token to verify.
      * @param {string} password - password to set for account.
-     * @param {function} [onSuccess] - Called to give status updates.
+     * @param {function} [onComplete] - Called to give status updates.
      * @param {function} [onFailure] - Called on error.
      */
-    verifyUser: function(token, password, onSuccess, onFailure) {
-      network.emailForVerificationToken(token, function (email) {
+    verifyUser: function(token, password, onComplete, onFailure) {
+      User.tokenInfo(token, function(info) {
         var invalidInfo = { valid: false };
-        if (email) {
+        if (info) {
           network.completeUserRegistration(token, password, function (valid) {
-            var info = valid ? {
-              valid: valid,
-              email: email,
-              origin: storage.getStagedOnBehalfOf()
-            } : invalidInfo;
-
+            info.valid = valid;
             storage.setStagedOnBehalfOf("");
-
-            if (onSuccess) onSuccess(info);
+            if (onComplete) onComplete(info);
           }, onFailure);
-        } else if (onSuccess) {
-          onSuccess(invalidInfo);
+        } else if (onComplete) {
+          onComplete(invalidInfo);
         }
       }, onFailure);
     },
@@ -718,12 +731,12 @@ BrowserID.User = (function() {
      * Verify a users email address given by the token
      * @method verifyEmail
      * @param {string} token
-     * @param {function} [onSuccess] - Called on success.
+     * @param {function} [onComplete] - Called on completion.
      *   Called with an object with valid, email, and origin if valid, called
      *   with only valid otw.
      * @param {function} [onFailure] - Called on error.
      */
-    verifyEmail: function(token, onSuccess, onFailure) {
+    verifyEmailNoPassword: function(token, onComplete, onFailure) {
       network.emailForVerificationToken(token, function (email) {
         var invalidInfo = { valid: false };
         if (email) {
@@ -736,10 +749,24 @@ BrowserID.User = (function() {
 
             storage.setStagedOnBehalfOf("");
 
-            if (onSuccess) onSuccess(info);
+            if (onComplete) onComplete(info);
+          }, onFailure);
+        } else if (onComplete) {
+          onComplete(invalidInfo);
+        }
+      }, onFailure);
+    },
+
+    verifyEmailWithPassword: function(token, pass, onComplete, onFailure) {
+      User.verifyEmailNoPassword(token, function(userInfo) {
+        var invalidInfo = { valid: false };
+        if(userInfo.status !== false) {
+          User.setPassword(pass, function(status) {
+            onComplete(status ? userInfo : invalidInfo);
           }, onFailure);
-        } else if (onSuccess) {
-          onSuccess(invalidInfo);
+        }
+        else {
+          onComplete(invalidInfo);
         }
       }, onFailure);
     },
diff --git a/resources/static/test/qunit/controllers/page_unit_test.js b/resources/static/test/qunit/controllers/page_unit_test.js
index 012c55d93d3fdd5201f5d7b2310bb96638b2fe1b..3c044c1c4074480e6d4ac8bec7c050599af834a3 100644
--- a/resources/static/test/qunit/controllers/page_unit_test.js
+++ b/resources/static/test/qunit/controllers/page_unit_test.js
@@ -238,5 +238,9 @@
     });
   });
 
+  test("checkRequired", function() {
+    ok(false, "write a test for this");
+  });
+
 }());
 
diff --git a/resources/static/test/qunit/pages/add_email_address_test.js b/resources/static/test/qunit/pages/add_email_address_test.js
index 7f6916c101f223b145b714de4432caf87a41fa8c..656e1ac01ed85adff0fe08a796a4193d73dd1a75 100644
--- a/resources/static/test/qunit/pages/add_email_address_test.js
+++ b/resources/static/test/qunit/pages/add_email_address_test.js
@@ -38,17 +38,21 @@
   "use strict";
 
   var bid = BrowserID,
-      network = bid.Network,
       storage = bid.Storage,
       xhr = bid.Mocks.xhr,
+      dom = bid.DOM,
       testHelpers = bid.TestHelpers,
-      validToken = true;
+      validToken = true,
+      controller,
+      config = {
+        token: "token"
+      };
 
   module("pages/add_email_address", {
     setup: function() {
       testHelpers.setup();
       bid.Renderer.render("#page_head", "site/add_email_address", {});
-      $(".siteinfo").hide();
+      $(".siteinfo,.password_entry").hide();
     },
     teardown: function() {
       testHelpers.teardown();
@@ -56,45 +60,148 @@
     }
   });
 
-  asyncTest("addEmailAddress with good token and site", function() {
+  function createPrimaryUser() {
+    storage.addEmail("testuser@testuser.com", {
+      created: new Date(),
+      type: "primary"
+    });
+  }
+
+  function createController(options, callback) {
+    controller = BrowserID.addEmailAddress.create();
+    options = options || {};
+    options.ready = callback;
+    controller.start(options);
+  }
+
+  function expectTooltipVisible() {
+    createPrimaryUser();
+    createController(config, function() {
+      controller.submit(function() {
+        testHelpers.testTooltipVisible();
+        start();
+      });
+    });
+  }
+
+  function testEmail() {
+    equal(dom.getInner(".email"), "testuser@testuser.com", "correct email shown");
+  }
+
+  function testCannotConfirm() {
+    ok($("#cannotconfirm").is(":visible"), "cannot confirm box is visible");
+  }
+
+  test("start with missing token", function() {
+    var error;
+    try {
+      createController({});
+    } catch(e) {
+      error = e;
+    }
+
+    equal(error, "missing config option: token", "correct error thrown");
+  });
+
+  asyncTest("no password: start with good token and site", function() {
     storage.setStagedOnBehalfOf("browserid.org");
 
-    bid.addEmailAddress("token", function() {
-      equal($("#email").val(), "testuser@testuser.com", "email set");
+    createController(config, function() {
+      testEmail();
       ok($(".siteinfo").is(":visible"), "siteinfo is visible when we say what it is");
       equal($(".website:nth(0)").text(), "browserid.org", "origin is updated");
+      equal($("body").hasClass("complete"), true, "body has complete class");
       start();
     });
   });
 
-  asyncTest("addEmailAddress with good token and nosite", function() {
-    bid.addEmailAddress("token", function() {
-      equal($("#email").val(), "testuser@testuser.com", "email set");
+  asyncTest("no password: start with good token and nosite", function() {
+    createController(config, function() {
+      testEmail();
       equal($(".siteinfo").is(":visible"), false, "siteinfo is not visible without having it");
       equal($(".siteinfo .website").text(), "", "origin is not updated");
       start();
     });
   });
 
-  asyncTest("addEmailAddress with bad token", function() {
+  asyncTest("no password: start with bad token", function() {
     xhr.useResult("invalid");
 
-    bid.addEmailAddress("token", function() {
-      ok($("#cannotconfirm").is(":visible"), "cannot confirm box is visible");
+    createController(config, function() {
+      testCannotConfirm();
       start();
     });
   });
 
-  asyncTest("addEmailAddress with emailForVerficationToken XHR failure", function() {
+  asyncTest("no password: start with emailForVerficationToken XHR failure", function() {
     xhr.useResult("ajaxError");
-    bid.addEmailAddress("token", function() {
-      ok($("#cannotcommunicate").is(":visible"), "cannot communicate box is visible");
+    createController(config, function() {
+      testHelpers.testErrorVisible();
       start();
     });
   });
 
-  asyncTest("addEmailAddress - first secondary address added to account with only primaries - must enter password", function() {
-    start();
+  asyncTest("password: first secondary address added", function() {
+    createPrimaryUser();
+    createController(config, function() {
+      equal($("body").hasClass("enter_password"), true, "enter_password added to body");
+      testEmail();
+      start();
+    });
+  });
+
+  asyncTest("password: missing password", function() {
+    $("#password").val();
+    $("#vpassword").val("password");
+
+    expectTooltipVisible();
+  });
+
+  asyncTest("password: missing verify password", function() {
+    $("#password").val("password");
+    $("#vpassword").val();
+
+    expectTooltipVisible();
+  });
+
+  asyncTest("password: too short of a password", function() {
+    $("#password").val("pass");
+    $("#vpassword").val("pass");
+
+    expectTooltipVisible();
+  });
+
+  asyncTest("password: mismatched passwords", function() {
+    $("#password").val("passwords");
+    $("#vpassword").val("password");
+
+    expectTooltipVisible();
+  });
+
+  asyncTest("password: good password", function() {
+    $("#password").val("password");
+    $("#vpassword").val("password");
+
+    createPrimaryUser();
+    createController(config, function() {
+      controller.submit(function(status) {
+        equal(status, true, "correct status");
+        equal($("body").hasClass("complete"), true, "body has complete class");
+        start();
+      });
+    });
+  });
+
+  asyncTest("password: good password bad token", function() {
+    $("#password").val("password");
+    $("#vpassword").val("password");
+
+    xhr.useResult("invalid");
+    createPrimaryUser();
+    createController(config, function() {
+      testCannotConfirm();
+      start();
+    });
   });
 
 }());
diff --git a/resources/static/test/qunit/shared/user_unit_test.js b/resources/static/test/qunit/shared/user_unit_test.js
index 54a2e2d0fe258fc1f59b1c325dd422c06f7a0bff..7a8db43257bf88ad7937a5563bd0b96cfdd2a711 100644
--- a/resources/static/test/qunit/shared/user_unit_test.js
+++ b/resources/static/test/qunit/shared/user_unit_test.js
@@ -317,6 +317,33 @@ var vep = require("./vep");
     }, 500);
   });
 
+  asyncTest("tokenInfo with a good token and origin info, expect origin in results", function() {
+    storage.setStagedOnBehalfOf(testOrigin);
+
+    lib.tokenInfo("token", function(info) {
+      equal(info.email, "testuser@testuser.com", "correct email");
+      equal(info.origin, testOrigin, "correct origin");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("tokenInfo with a bad token without site info, no site in results", function() {
+    lib.tokenInfo("token", function(info) {
+      equal(info.email, "testuser@testuser.com", "correct email");
+      equal(typeof info.origin, "undefined", "origin is undefined");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("tokenInfo with XHR error", function() {
+    xhr.useResult("ajaxError");
+    lib.tokenInfo(
+      "token",
+      testHelpers.unexpectedSuccess,
+      testHelpers.expectedXHRFailure
+    );
+  });
+
   asyncTest("verifyUser with a good token", function() {
     storage.setStagedOnBehalfOf(testOrigin);
 
@@ -650,9 +677,9 @@ var vep = require("./vep");
     }, 500);
   });
 
-  asyncTest("verifyEmail with a good token", function() {
+  asyncTest("verifyEmailNoPassword with a good token", function() {
     storage.setStagedOnBehalfOf(testOrigin);
-    lib.verifyEmail("token", function onSuccess(info) {
+    lib.verifyEmailNoPassword("token", function onSuccess(info) {
 
       ok(info.valid, "token was valid");
       equal(info.email, "testuser@testuser.com", "email part of info");
@@ -663,27 +690,58 @@ var vep = require("./vep");
     }, testHelpers.unexpectedXHRFailure);
   });
 
-  asyncTest("verifyEmail with a bad token", function() {
+  asyncTest("verifyEmailNoPassword with a bad token", function() {
     xhr.useResult("invalid");
 
-    lib.verifyEmail("token", function onSuccess(info) {
-
+    lib.verifyEmailNoPassword("token", function onSuccess(info) {
       equal(info.valid, false, "bad token calls onSuccess with a false validity");
 
       start();
     }, testHelpers.unexpectedXHRFailure);
   });
 
-  asyncTest("verifyEmail with an XHR failure", function() {
+  asyncTest("verifyEmailNoPassword with an XHR failure", function() {
     xhr.useResult("ajaxError");
 
-    lib.verifyEmail("token", function onSuccess(info) {
-      ok(false, "xhr failure should never succeed");
+    lib.verifyEmailNoPassword(
+      "token",
+      testHelpers.unexpectedSuccess,
+      testHelpers.expectedXHRFailure
+    );
+  });
+
+  asyncTest("verifyEmailWithPassword with a good token", function() {
+    storage.setStagedOnBehalfOf(testOrigin);
+    lib.verifyEmailWithPassword("token", "password", function onSuccess(info) {
+
+      ok(info.valid, "token was valid");
+      equal(info.email, "testuser@testuser.com", "email part of info");
+      equal(info.origin, testOrigin, "origin in info");
+      equal(storage.getStagedOnBehalfOf(), "", "initiating origin was removed");
+
       start();
-    }, function() {
-      ok(true, "xhr failure should always be a failure");
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("verifyEmailWithPassword with a bad token", function() {
+    xhr.useResult("invalid");
+
+    lib.verifyEmailWithPassword("token", "password", function onSuccess(info) {
+      equal(info.valid, false, "bad token calls onSuccess with a false validity");
+
       start();
-    });
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("verifyEmailWithPassword with an XHR failure", function() {
+    xhr.useResult("ajaxError");
+
+    lib.verifyEmailWithPassword(
+      "token",
+      "password",
+      testHelpers.unexpectedSuccess,
+      testHelpers.expectedXHRFailure
+    );
   });
 
   asyncTest("syncEmailKeypair with successful sync", function() {
diff --git a/resources/views/add_email_address.ejs b/resources/views/add_email_address.ejs
index 36c22156c4b10475295fd76c79515cabe6b85028..92b1c0e47da582e1105d96db1f090d00980ef855 100644
--- a/resources/views/add_email_address.ejs
+++ b/resources/views/add_email_address.ejs
@@ -2,18 +2,18 @@
     <div id="signUpFormWrap">
         <ul class="notifications">
             <li class="notification error" id="cannotconfirm">Error encountered while attempting to confirm your address. Have you previously verified this address?</li>
-            <li class="notification error" id="cannotcommunicate">Error comunicating with server.</li>
             <li class="notification error" id="cannotcomplete">Error encountered trying to complete registration.</li>
         </ul>
 
         <form id="signUpForm" class="cf">
             <p class="hint siteinfo">Finish signing into: <strong><span class="website"></span></strong></p>
+
             <h1 class="serif">Email Verification</h1>
 
             <ul class="inputs password_entry">
                 <li>
                     <label class="serif" for="email">Email Address</label>
-                    <input class="youraddress sans" id="email" placeholder="Your Email" type="email" value="" disabled="disabled" maxlength="254" />
+                    <input class="youraddress sans email" id="email" placeholder="Your Email" type="email" value="" disabled="disabled" maxlength="254" />
                 </li>
                 <li>
                     <label class="serif" for="password">New Password</label>
@@ -45,13 +45,12 @@
                 <button>Finish</button>
             </div>
 
-            <p class="hint">One moment while we attempt to confirm your email address...</p>
 
         </form>
 
         <div id="congrats">
             <p class="serif">
-                <strong id="email">Your address</strong> has been verified!
+                <strong class="email">Your address</strong> has been verified!
 
                 <p class="siteinfo">
                   Your new address is set up and you should now be signed in.
diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs
index 72a2c3fc955b04c4430d97abd813e572bdef7b31..5e0b43f2697a83f5c048aa136d7caba8ee09c6ba 100644
--- a/resources/views/layout.ejs
+++ b/resources/views/layout.ejs
@@ -38,10 +38,13 @@
     <script src="/shared/tooltip.js" type="text/javascript"></script>
     <script src="/shared/validation.js" type="text/javascript"></script>
     <script src="/shared/helpers.js" type="text/javascript"></script>
+    <script src="/shared/class.js" type="text/javascript"></script>
+
+    <script src="/dialog/controllers/page.js" type="text/javascript"></script>
 
     <script src="/pages/page_helpers.js" type="text/javascript"></script>
-    <script src="/pages/browserid.js" type="text/javascript"></script>
     <script src="/pages/index.js" type="text/javascript"></script>
+    <script src="/pages/start.js" type="text/javascript"></script>
     <script src="/pages/add_email_address.js" type="text/javascript"></script>
     <script src="/pages/verify_email_address.js" type="text/javascript"></script>
     <script src="/pages/forgot.js" type="text/javascript"></script>
diff --git a/scripts/compress.sh b/scripts/compress.sh
index 6c1260f74422590639072f561d67f74285188ebc..d9d6ffd569e37b38820459a1393a8d5a83a81945 100755
--- a/scripts/compress.sh
+++ b/scripts/compress.sh
@@ -67,7 +67,7 @@ echo '****Building BrowserID.org HTML, CSS, and JS****'
 echo ''
 
 #produce the main site js
-cat lib/jquery-1.6.2.min.js lib/json2.js lib/underscore-min.js lib/ejs.js shared/javascript-extensions.js shared/browserid.js lib/dom-jquery.js $BUILD_PATH/templates.js shared/renderer.js shared/error-display.js shared/screens.js shared/error-messages.js shared/storage.js shared/network.js shared/provisioning.js shared/user.js shared/tooltip.js shared/validation.js shared/helpers.js pages/page_helpers.js pages/browserid.js pages/index.js pages/add_email_address.js pages/verify_email_address.js pages/forgot.js pages/manage_account.js pages/signin.js pages/signup.js > $BUILD_PATH/browserid.uncompressed.js
+cat lib/jquery-1.6.2.min.js lib/json2.js lib/underscore-min.js lib/ejs.js shared/javascript-extensions.js shared/browserid.js lib/dom-jquery.js $BUILD_PATH/templates.js shared/renderer.js shared/error-display.js shared/screens.js shared/error-messages.js shared/storage.js shared/network.js shared/provisioning.js shared/user.js shared/tooltip.js shared/validation.js shared/helpers.js shared/class.js dialog/controllers/page.js pages/page_helpers.js pages/index.js pages/start.js pages/add_email_address.js pages/verify_email_address.js pages/forgot.js pages/manage_account.js pages/signin.js pages/signup.js > $BUILD_PATH/browserid.uncompressed.js
 
 # produce the main site css
 cat css/common.css css/style.css css/m.css > $BUILD_PATH/browserid.uncompressed.css