diff --git a/resources/static/dialog/controllers/authenticate_controller.js b/resources/static/dialog/controllers/authenticate_controller.js
index 84632f5f3008fe152f92d6c27a29a3b22899e047..21354b2b3ed27b4c5abe357b22da1eb0e1bff1e6 100644
--- a/resources/static/dialog/controllers/authenticate_controller.js
+++ b/resources/static/dialog/controllers/authenticate_controller.js
@@ -42,6 +42,7 @@
       user = bid.User,
       errors = bid.Errors,
       validation = bid.Validation,
+      tooltip = bid.Tooltip,
       lastEmail = "";
 
   function checkEmail(el, event) {
@@ -50,9 +51,7 @@
 
     cancelEvent(event);
 
-    if (!validation.email(email)) {
-      return;
-    }
+    if (!validation.email(email)) return;
 
     user.isEmailRegistered(email, function onComplete(registered) {
       if (registered) {
@@ -70,19 +69,16 @@
 
     cancelEvent(event);
 
-    if (!validation.email(email)) {
-      return;
-    }
+    if (!validation.email(email)) return;
 
-    user.createUser(email, function(keypair) {
-      if (keypair) {
+    user.createUser(email, function(staged) {
+      if (staged) {
         self.close("user_staged", {
-          email: email,
-          keypair: keypair
+          email: email
         });
       }
       else {
-        // XXX can't register this email address.
+        tooltip.showTooltip("#could_not_add");
       }
     }, self.getErrorDialog(errors.createUser));
   }
@@ -94,9 +90,7 @@
 
     cancelEvent(event);
 
-    if (!validation.emailAndPassword(email, pass)) {
-      return;
-    }
+    if (!validation.emailAndPassword(email, pass)) return;
 
     user.authenticate(email, pass, 
       function onComplete(authenticated) {
@@ -131,16 +125,12 @@
   }
 
   function cancelEvent(event) {
-    if (event) {
-      event.preventDefault();
-    }
+    if (event) event.preventDefault();
   }
 
   function enterEmailState(el, event) {
-    if (event && event.which === 13) {
-      // Enter key, do nothing
-      return;
-    }
+    // Enter key, do nothing
+    if (event && event.which === 13) return;
 
     if (!el.is(":disabled")) {
       this.submit = checkEmail;
@@ -185,6 +175,10 @@
     init: function(el, options) {
       options = options || {};
 
+      if (options.user) {
+        user = options.user;
+      }
+
       this._super(el, {
         bodyTemplate: "authenticate.ejs",
         bodyVars: {
@@ -196,9 +190,7 @@
       this.submit = checkEmail;
       // If we already have an email address, check if it is valid, if so, show 
       // password.
-      if (options.email) {
-        this.submit();
-      }
+      if (options.email) this.submit();
     },
 
     "#email keyup": function(el, event) {
diff --git a/resources/static/dialog/qunit.html b/resources/static/dialog/qunit.html
index 7e8189f829a5ff161198b6c2aa4251a536757786..93eb8cb2141e60e3e86587d8db256987223d81f4 100644
--- a/resources/static/dialog/qunit.html
+++ b/resources/static/dialog/qunit.html
@@ -32,7 +32,7 @@
           <div class="contents"></div>
       </div>
 
-      <span id="email"></span>
+      <input id="email" />
       <span id="cannotconfirm" class="error">Cannot confirm</span>
       <span id="cannotcommunicate" class="error">Cannot communicate</span>
       <span id="siteinfo" class="error"><span class="website"></span></span>
diff --git a/resources/static/dialog/resources/network.js b/resources/static/dialog/resources/network.js
index 855a349baea328f8d9c06042e66e6d2c249fcc9e..ea1f53b312e0e9c1c9c4a3a7c6351386996c71c1 100644
--- a/resources/static/dialog/resources/network.js
+++ b/resources/static/dialog/resources/network.js
@@ -59,6 +59,7 @@ BrowserID.Network = (function() {
       info = info || {};
       var network = info.network = info.network || {};
 
+      network.status = jqXHR && jqXHR.status;
       network.textStatus = textStatus;
       network.errorThrown = errorThrown;
 
@@ -252,7 +253,13 @@ BrowserID.Network = (function() {
         success: function(status) {
           if (onSuccess) onSuccess(status.success);
         },
-        error: onFailure
+        error: function(info) {
+          // 403 is throttling.
+          if(info.network.status === 403) {
+            if (onSuccess) onSuccess(false); 
+          }
+          else if (onFailure) onFailure(info);
+        }
       });
     },
 
@@ -410,7 +417,13 @@ BrowserID.Network = (function() {
         success: function(status) {
           if (onSuccess) onSuccess(status.success);
         },
-        error: onFailure
+        error: function(info) {
+          // 403 is throttling.
+          if(info.network.status === 403) {
+            if (onSuccess) onSuccess(false); 
+          }
+          else if (onFailure) onFailure(info);
+        }
       });
     },
 
diff --git a/resources/static/dialog/resources/user.js b/resources/static/dialog/resources/user.js
index 3fde2a9a9eb795b03e40e2685abd70266e544417..f78fadefde32261d645b0b1d25c5653880e7910b 100644
--- a/resources/static/dialog/resources/user.js
+++ b/resources/static/dialog/resources/user.js
@@ -236,11 +236,7 @@ BrowserID.User = (function() {
       // remember this for later
       storage.setStagedOnBehalfOf(self.getHostname());
       
-      network.createUser(email, origin, function(created) {
-        if (onSuccess) {
-          onSuccess(created);
-        }
-      }, onFailure);
+      network.createUser(email, origin, onSuccess, onFailure);
     },
 
     /**
@@ -484,13 +480,10 @@ BrowserID.User = (function() {
     addEmail: function(email, onSuccess, onFailure) {
       var self = this;
       network.addEmail(email, origin, function(added) {
-        if (added) {
-          storage.setStagedOnBehalfOf(self.getHostname());
-          // we no longer send the keypair, since we will certify it later.
-          if (onSuccess) {
-            onSuccess(added);
-          }
-        }
+        if (added) storage.setStagedOnBehalfOf(self.getHostname());
+
+        // we no longer send the keypair, since we will certify it later.
+        if (onSuccess) onSuccess(added);
       }, onFailure);
     },
 
diff --git a/resources/static/dialog/test/qunit/controllers/authenticate_controller_unit_test.js b/resources/static/dialog/test/qunit/controllers/authenticate_controller_unit_test.js
new file mode 100644
index 0000000000000000000000000000000000000000..0a40cf088dd586bcd81dfdbcf7ec65c98a7da1c2
--- /dev/null
+++ b/resources/static/dialog/test/qunit/controllers/authenticate_controller_unit_test.js
@@ -0,0 +1,94 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global steal: true, test: true, 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
+ *
+ * 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 ***** */
+steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/controllers/authenticate_controller", function() {
+  "use strict";
+
+  var controller, 
+      el = $("body"),
+      storage = BrowserID.Storage,
+      emailRegistered = false,
+      userCreated = true;
+
+  var userMock = {
+    getHostname: function() { return "host"; },
+    isEmailRegistered: function(email, onSuccess, onFailure) {
+      onSuccess(emailRegistered);
+    },
+
+    createUser: function(email, onSuccess, onFailure) {
+      onSuccess(userCreated);
+    }
+  };
+
+  function reset() {
+    el = $("#controller_head");
+    el.find("#formWrap .contents").html("");
+    el.find("#wait .contents").html("");
+    el.find("#error .contents").html("");
+
+    emailRegistered = false;
+    userCreated = true;
+
+    OpenAjax.hub.unsubscribe("user_staged");
+  }
+
+  module("controllers/authenticate_controller", {
+    setup: function() {
+      reset();
+      storage.clear();
+      controller = el.authenticate({ user: userMock }).controller();
+    },
+
+    teardown: function() {
+      if (controller) {
+        controller.destroy();
+      }    
+      reset();
+      storage.clear();
+    } 
+  });
+
+  test("setting email address prefills address field", function() {
+      controller.destroy();
+      $("#email").val("");
+      controller = el.authenticate({ user: userMock, email: "testuser@testuser.com" }).controller();
+      equal($("#email").val(), "testuser@testuser.com", "email prefilled");
+  });
+
+
+});
+
diff --git a/resources/static/dialog/test/qunit/qunit.js b/resources/static/dialog/test/qunit/qunit.js
index ffecc86f2b4e903e5884105bdf51827953fa2a96..e97265eb156944baff0a3f3522101a329c3570a0 100644
--- a/resources/static/dialog/test/qunit/qunit.js
+++ b/resources/static/dialog/test/qunit/qunit.js
@@ -30,4 +30,5 @@ steal("/dialog/resources/browserid.js",
   .then("controllers/page_controller_unit_test")
   .then("controllers/pickemail_controller_unit_test")
   .then("controllers/dialog_controller_unit_test")
+  .then("controllers/authenticate_controller_unit_test")
 
diff --git a/resources/static/dialog/test/qunit/resources/network_unit_test.js b/resources/static/dialog/test/qunit/resources/network_unit_test.js
index d0a8bda50b4f48e16ad8b42ec83244a85a76984d..029e8d76cbe40e146a988cd19fed8f77ac8231fb 100644
--- a/resources/static/dialog/test/qunit/resources/network_unit_test.js
+++ b/resources/static/dialog/test/qunit/resources/network_unit_test.js
@@ -126,6 +126,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
       // We are going to test for XHR failures for session_context using 
       // call to serverTime.  We are going to use the flag contextAjaxError
       "get /wsapi/session_context ajaxError": contextInfo, 
+      "get /wsapi/session_context throttle": contextInfo, 
       "get /wsapi/session_context contextAjaxError": undefined,  
       "post /wsapi/authenticate_user valid": { success: true },
       "post /wsapi/authenticate_user invalid": { success: false },
@@ -135,6 +136,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
       "post /wsapi/complete_email_addition ajaxError": undefined,
       "post /wsapi/stage_user valid": { success: true },
       "post /wsapi/stage_user invalid": { success: false },
+      "post /wsapi/stage_user throttle": 403,
       "post /wsapi/stage_user ajaxError": undefined,
       "get /wsapi/user_creation_status?email=address notcreated": undefined, // undefined because server returns 400 error
       "get /wsapi/user_creation_status?email=address pending": { status: "pending" },
@@ -156,6 +158,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
       "post /wsapi/account_cancel ajaxError": undefined,
       "post /wsapi/stage_email valid": { success: true },
       "post /wsapi/stage_email invalid": { success: false },
+      "post /wsapi/stage_email throttle": 403,
       "post /wsapi/stage_email ajaxError": undefined,
       "get /wsapi/email_addition_status?email=address notcreated": undefined, // undefined because server returns 400 error
       "get /wsapi/email_addition_status?email=address pending": { status: "pending" },
@@ -189,7 +192,8 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
       var resName = req.type + " " + req.url + " " + xhr.resultType;
       var result = xhr.results[resName];
 
-      if(result) {
+      var type = typeof result;
+      if(!(type == "number" || type == "undefined")) {
         if(obj.success) {
           obj.success(result);
         }
@@ -197,7 +201,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
       else if (obj.error) {
         // Invalid result - either invalid URL, invalid GET/POST or 
         // invalid resultType
-        obj.error({}, "errorStatus", "errorThrown");
+        obj.error({ status: result || 400 }, "errorStatus", "errorThrown");
       }
     }
   }
@@ -373,6 +377,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
     stop();
   });
 
+  wrappedAsyncTest("createUser throttled", function() {
+    xhr.useResult("throttle");
+
+    network.createUser("validuser", "origin", function onSuccess(added) {
+      equal(added, false, "throttled email returns onSuccess but with false as the value");
+      wrappedStart();
+    }, function onFailure() {
+      ok(false);
+      wrappedStart();
+    });
+
+    stop();
+  });
+
   wrappedAsyncTest("createUser with XHR failure", function() {
     notificationCheck(network.createUser, "validuser", "origin");
   });
@@ -548,6 +566,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func
     stop();
   });
 
+  wrappedAsyncTest("addEmail throttled", function() {
+    xhr.useResult("throttle");
+
+    network.addEmail("address", "origin", function onSuccess(added) {
+      equal(added, false, "throttled email returns onSuccess but with false as the value");
+      wrappedStart();
+    }, function onFailure() {
+      ok(false);
+      wrappedStart();
+    });
+
+    stop();
+  });
+
   wrappedAsyncTest("addEmail with XHR failure", function() {
     notificationCheck(network.addEmail, "address", "origin");
   });
diff --git a/resources/static/dialog/test/qunit/resources/user_unit_test.js b/resources/static/dialog/test/qunit/resources/user_unit_test.js
index 48e7a4b705930a18aa72f16d7206ddec37c739c0..7ef2dfd486e92752218cea80e5911dbc93705840 100644
--- a/resources/static/dialog/test/qunit/resources/user_unit_test.js
+++ b/resources/static/dialog/test/qunit/resources/user_unit_test.js
@@ -61,7 +61,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
 
   var netStub = {
     reset: function() {
-      credentialsValid = syncValid = true;
+      credentialsValid = emailAdded = userAdded = syncValid = true;
       unknownEmails = [];
       keyRefresh = [];
       userEmails = {"testuser@testuser.com": {}};
@@ -93,7 +93,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
     },
 
     addEmail: function(email, origin, onSuccess, onFailure) {
-      xhrFailure ? onFailure() : onSuccess(true);
+      xhrFailure ? onFailure() : onSuccess(emailAdded);
     },
 
     checkEmailRegistration: function(email, onSuccess, onFailure) {
@@ -145,7 +145,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
     },
 
     createUser: function(email, origin, onSuccess, onFailure) {
-      xhrFailure ? onFailure() : onSuccess(true);
+      xhrFailure ? onFailure() : onSuccess(userAdded);
     },
 
     setPassword: function(password, onSuccess) {
@@ -206,7 +206,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
     */
   }
 
-  module("user", {
+  module("resources/user", {
     setup: function() {
       lib.setNetwork(netStub);
       lib.clearStoredEmailKeypairs();
@@ -284,6 +284,16 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
     stop();
   });
 
+  test("createUser with user creation refused", function() {
+    userAdded = false
+    lib.createUser("testuser@testuser.com", function(status) {
+      equal(status, false, "user creation refused");
+      start();
+    }, failure("createUser failure"));
+
+    stop();
+  });
+
   test("createUser with XHR failure", function() {
     xhrFailure = true;
 
@@ -597,7 +607,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
       var identities = lib.getStoredEmailKeypairs();
       equal(false, "testemail@testemail.com" in identities, "Our new email is not added until confirmation.");
 
-
       equal(storage.getStagedOnBehalfOf(), lib.getHostname(), "initiatingOrigin is stored"); 
 
       start();
@@ -606,6 +615,23 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
     stop();
   });
 
+  test("addEmail with addition refused", function() {
+    emailAdded = false;
+
+    lib.addEmail("testemail@testemail.com", function(added) {
+      equal(added, false, "user addition was refused");
+
+      var identities = lib.getStoredEmailKeypairs();
+      equal(false, "testemail@testemail.com" in identities, "Our new email is not added until confirmation.");
+
+      equal(typeof storage.getStagedOnBehalfOf(), "undefined", "initiatingOrigin is not stored"); 
+
+      start();
+    }, failure("addEmail failure"));
+
+    stop();
+  });
+
   test("addEmail with XHR failure", function() {
     xhrFailure = true;
     lib.addEmail("testemail@testemail.com", function(added) {
diff --git a/resources/static/dialog/views/authenticate.ejs b/resources/static/dialog/views/authenticate.ejs
index ae0c180a6e5837c3111364ea6d1ca9d2947f8c08..4a4edfa17269a734af146e8488b05b6b9c696cd4 100644
--- a/resources/static/dialog/views/authenticate.ejs
+++ b/resources/static/dialog/views/authenticate.ejs
@@ -13,6 +13,14 @@
               <div id="email_required" class="tooltip" for="email">
                 The email field is required.
               </div>
+
+              <div id="could_not_add" class="tooltip" for="email">
+                We just sent an email to that address!  If you really want to send another, wait a minute or two and try again.
+              </div>
+
+              <div id="cannotStage" class="tooltip" for="email">
+                We just sent an email to that address!  If you really want to send another, wait a minute or two and try again.
+              </div>
           </li>
 
           <li id="hint_section" class="start">
diff --git a/resources/static/dialog/views/pickemail.ejs b/resources/static/dialog/views/pickemail.ejs
index 8e0b1669133bb225f96c535f662c0e9f72a0c53b..c7255d16f4841d164fb47699cf18ed0febcc8c8c 100644
--- a/resources/static/dialog/views/pickemail.ejs
+++ b/resources/static/dialog/views/pickemail.ejs
@@ -40,7 +40,7 @@
               </div>
 
               <div id="could_not_add" class="tooltip" for="newEmail">
-                This email address could not be added.
+                We just sent an email to that address!  If you really want to send another, wait a minute or two and try again.
               </div>
 
               <div id="already_taken" class="tooltip" for="newEmail">