diff --git a/resources/static/common/js/user.js b/resources/static/common/js/user.js
index c5433499a1e4f4a4ae765dc7d0a8ddb6f7fbfaeb..24e2f621fcb4b6d87f8acfe41763cfc86e573f5c 100644
--- a/resources/static/common/js/user.js
+++ b/resources/static/common/js/user.js
@@ -629,14 +629,19 @@ BrowserID.User = (function() {
      * @param {function} [onFailure] - Called on XHR failure.
      */
     requestPasswordReset: function(email, password, onComplete, onFailure) {
-      User.isEmailRegistered(email, function(registered) {
-        if (registered) {
+      User.addressInfo(email, function(info) {
+        // user is not known.  Can't request a password reset.
+        if (!info.known) {
+          complete(onComplete, { success: false, reason: "invalid_user" });
+        }
+        // user is trying to reset the password of a primary address.
+        else if (info.type === "primary") {
+          complete(onComplete, { success: false, reason: "primary_address" });
+        }
+        else {
           network.requestPasswordReset(email, password, origin,
             handleStageAddressVerifictionResponse.curry(onComplete), onFailure);
         }
-        else if (onComplete) {
-          onComplete({ success: false, reason: "invalid_user" });
-        }
       }, onFailure);
     },
 
@@ -949,10 +954,13 @@ BrowserID.User = (function() {
         network.addressInfo(email, function(info) {
           info.email = email;
           if(info.type === "primary") {
-            User.isUserAuthenticatedToPrimary(email, info, function(authed) {
-              info.authed = authed;
-              info.idpName = getIdPName(info);
-              complete(info);
+            User.isEmailRegistered(email, function(registered) {
+              User.isUserAuthenticatedToPrimary(email, info, function(authed) {
+                info.known = registered;
+                info.authed = authed;
+                info.idpName = getIdPName(info);
+                complete(info);
+              }, onFailure);
             }, onFailure);
           }
           else {
diff --git a/resources/static/pages/js/forgot.js b/resources/static/pages/js/forgot.js
index c8e2da96d4d6d6fb2f4939c3b5bb42852dbffee2..300e2a95f8a480895616eda46a3efac22f4d861e 100644
--- a/resources/static/pages/js/forgot.js
+++ b/resources/static/pages/js/forgot.js
@@ -17,8 +17,7 @@ BrowserID.forgot = (function() {
       tooltip = bid.Tooltip;
 
   function submit(oncomplete) {
-    // GET RID OF THIS HIDE CRAP AND USE CSS!
-    $(".notifications .notification").hide();
+    dom.hide(".notification");
 
     var email = helpers.getAndValidateEmail("#email"),
         pass = dom.getInner("#password"),
@@ -31,41 +30,86 @@ BrowserID.forgot = (function() {
           pageHelpers.emailSent("waitForPasswordResetComplete", email, oncomplete);
         }
         else {
-          var tooltipEl = info.reason === "throttle" ? "#could_not_add" : "#not_registered";
-          tooltip.showTooltip(tooltipEl);
+          var tooltipEls = {
+            throttle: "#could_not_add",
+            invalid_user: "#not_registered",
+            primary_address: "#primary_address"
+          };
+
+          var tooltipEl = tooltipEls[info.reason];
+          if (tooltipEl) {
+            tooltip.showTooltip(tooltipEl);
+          }
           complete(oncomplete);
         }
       }, pageHelpers.getFailure(bid.Errors.requestPasswordReset, oncomplete));
     } else {
       complete(oncomplete);
     }
-  };
+  }
 
   function back(oncomplete) {
     pageHelpers.cancelEmailSent(oncomplete);
   }
 
-  function init() {
-    $("form input[autofocus]").focus();
+  function redirectIfNeeded(doc, ready) {
+    // email addresses are stored if the user is coming from the signin or
+    // signup page.  If no email address is stored, the user browsed here
+    // directly.  If the user browsed here directly, kick them back to the
+    // sign in page.
+    var email = pageHelpers.getStoredEmail();
+    if (!email) {
+      doc.location.href = "/signin";
+      complete(ready);
+      return;
+    }
 
-    pageHelpers.setupEmail();
+    // We know an email address was stored, now check if it is registered.  If
+    // it is not registered, kick the user over to the signup page.  If it is
+    // registered, but a primary, kick them over to the signin page.
+    user.addressInfo(email, function(info) {
+      if (!info.known) {
+        doc.location.href = "/signup";
+      }
+      else if(info.type === "primary") {
+        doc.location.href="/signin";
+      }
 
-    dom.bindEvent("form", "submit", cancelEvent(submit));
-    dom.bindEvent("#back", "click", cancelEvent(back));
+      complete(ready);
+    });
   }
 
-  // BEGIN TESTING API
-  function reset() {
-    dom.unbindEvent("form", "submit");
-    dom.unbindEvent("#back", "click");
-  }
+  var Module = bid.Modules.PageModule.extend({
+    start: function(options) {
+      options = options || {};
+
+      var self=this,
+          doc = options.document || document;
+
+      // Check whether a redirection needs to happen before showing the rest of
+      // the content.
+      redirectIfNeeded(doc, function() {
+        dom.focus("form input[autofocus]");
+
+        pageHelpers.setupEmail();
+
+        self.bind("form", "submit", cancelEvent(submit));
+        self.click("#back", back);
+
+        Module.sc.init.call(self, options);
+
+        complete(options.ready);
+      });
+    }
 
-  init.submit = submit;
-  init.reset = reset;
-  init.back = back;
-  // END TESTING API
+    // BEGIN TESTING API
+    ,
+    submit: submit,
+    back: back
+    // END TESTING API
+  });
 
-  return init;
+  return Module;
 
 }());
 
diff --git a/resources/static/pages/js/start.js b/resources/static/pages/js/start.js
index 740b1f0bd3e0692a64b529430697ab004b92ebab..03f66f0ee36292d52850ada737e7881ea5ae0db3 100644
--- a/resources/static/pages/js/start.js
+++ b/resources/static/pages/js/start.js
@@ -139,7 +139,8 @@ $(function() {
       module.start({});
     }
     else if (path === "/forgot") {
-      bid.forgot();
+      var module = bid.forgot.create();
+      module.start({});
     }
     // START TRANSITION CODE
     // add_email_address has been renamed to confirm. Once all outstanding
diff --git a/resources/static/test/cases/common/js/user.js b/resources/static/test/cases/common/js/user.js
index 46f7a2376315a1cc18af304fd5f0d8b429cd48b0..94dd1b05677c3cbf96c1c3e7489d004223d80b7c 100644
--- a/resources/static/test/cases/common/js/user.js
+++ b/resources/static/test/cases/common/js/user.js
@@ -1291,31 +1291,57 @@
     );
   });
 
-  asyncTest("addressInfo with primary authenticated user", function() {
+  asyncTest("addressInfo with unknown primary authenticated user", function() {
+    xhr.useResult("primary");
+    provisioning.setStatus(provisioning.AUTHENTICATED);
+    lib.addressInfo(
+      "unregistered@testuser.com",
+      function(info) {
+        testObjectValuesEqual(info, {
+          type: "primary",
+          email: "unregistered@testuser.com",
+          authed: true,
+          idpName: "testuser.com",
+          known: false
+        });
+        start();
+      },
+      testHelpers.unexpectedFailure
+    );
+  });
+
+  asyncTest("addressInfo with known primary authenticated user", function() {
     xhr.useResult("primary");
     provisioning.setStatus(provisioning.AUTHENTICATED);
     lib.addressInfo(
       "registered@testuser.com",
       function(info) {
-        equal(info.type, "primary", "correct type");
-        equal(info.email, "registered@testuser.com", "correct email");
-        equal(info.authed, true, "user is authenticated with IdP");
-        equal(info.idpName, "testuser.com", "unknown IdP, use email host portion for name");
+        testObjectValuesEqual(info, {
+          type: "primary",
+          email: "registered@testuser.com",
+          authed: true,
+          idpName: "testuser.com",
+          known: true
+        });
         start();
       },
       testHelpers.unexpectedFailure
     );
   });
 
-  asyncTest("addressInfo with primary unauthenticated user", function() {
+  asyncTest("addressInfo with known primary unauthenticated user", function() {
     xhr.useResult("primary");
     provisioning.setStatus(provisioning.NOT_AUTHENTICATED);
     lib.addressInfo(
       "registered@testuser.com",
       function(info) {
-        equal(info.type, "primary", "correct type");
-        equal(info.email, "registered@testuser.com", "correct email");
-        equal(info.authed, false, "user is not authenticated with IdP");
+        testObjectValuesEqual(info, {
+          type: "primary",
+          email: "registered@testuser.com",
+          authed: false,
+          idpName: "testuser.com",
+          known: true
+        });
         start();
       },
       testHelpers.unexpectedFailure
diff --git a/resources/static/test/cases/pages/js/forgot.js b/resources/static/test/cases/pages/js/forgot.js
index d6179186f4f6b1eb5cf421e2ad56e4d6ec12e09b..d8252629c0280be490d288bc580fd10f6ef6afb2 100644
--- a/resources/static/test/cases/pages/js/forgot.js
+++ b/resources/static/test/cases/pages/js/forgot.js
@@ -10,23 +10,36 @@
       network = bid.Network,
       user = bid.User,
       testHelpers = bid.TestHelpers,
-      xhr = bid.Mocks.xhr;
+      pageHelpers = bid.PageHelpers,
+      xhr = bid.Mocks.xhr,
+      WindowMock = bid.Mocks.WindowMock,
+      controller,
+      docMock;
+
+  function createController(options) {
+    options = options || {};
+
+    docMock = new WindowMock().document;
+    options.document = docMock;
+
+    controller = bid.forgot.create();
+    controller.start(options);
+  }
 
   module("pages/js/forgot", {
     setup: function() {
       testHelpers.setup();
       bid.Renderer.render("#page_head", "site/forgot", {});
-      bid.forgot();
+      createController();
     },
     teardown: function() {
       testHelpers.teardown();
-      bid.forgot.reset();
     }
   });
 
   function testEmailNotSent(config) {
     config = config || {};
-    bid.forgot.submit(function() {
+    controller.submit(function() {
       equal($(".emailsent").is(":visible"), false, "email not sent");
       if(config.checkTooltip !== false) testHelpers.testTooltipVisible();
       if (config.ready) config.ready();
@@ -34,7 +47,38 @@
     });
   }
 
-  asyncTest("requestPasswordReset with invalid email", function() {
+  test("start with no stored email - redirect to /signin", function() {
+    equal(docMock.location.href, "/signin", "page redirected to signin if no email stored");
+  });
+
+  asyncTest("start with stored primary email - redirect to /signin", function() {
+    xhr.useResult("primary");
+    pageHelpers.setStoredEmail("testuser@testuser.com");
+    createController({
+      ready: function() {
+        equal(docMock.location.href, "/signin", "page redirected to signin if primary email stored");
+        start();
+      }
+    });
+  });
+
+  asyncTest("start with stored unknown secondary email - redirect to /signup", function() {
+    pageHelpers.setStoredEmail("unregistered@testuser.com");
+    createController({
+      ready: function() {
+        equal(docMock.location.href, "/signup", "page redirected to signup if unknown secondary email stored");
+        start();
+      }
+    });
+  });
+
+  test("start with stored known secondary email - no redirection", function() {
+    pageHelpers.setStoredEmail("testuser@testuser.com");
+    createController();
+    equal(docMock.location.href, document.location.href, "no page redirection if known secondary is stored");
+  });
+
+  asyncTest("submit with invalid email", function() {
     $("#email").val("invalid");
     $("#password,#vpassword").val("password");
 
@@ -43,62 +87,62 @@
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with known email, happy case - show email sent notice", function() {
+  asyncTest("submit with known secondary email, happy case - show email sent notice", function() {
     $("#email").val("registered@testuser.com");
     $("#password,#vpassword").val("password");
 
-    bid.forgot.submit(function() {
+    controller.submit(function() {
       ok($(".emailsent").is(":visible"), "email sent successfully");
       start();
     });
   });
 
-  asyncTest("requestPasswordReset with known email with leading/trailing whitespace - show email sent notice", function() {
+  asyncTest("submit with known secondary email with leading/trailing whitespace - show email sent notice", function() {
     $("#email").val("   registered@testuser.com  ");
     $("#password,#vpassword").val("password");
 
-    bid.forgot.submit(function() {
+    controller.submit(function() {
       ok($(".emailsent").is(":visible"), "email sent successfully");
       start();
     });
   });
 
-  asyncTest("requestPasswordReset with missing password", function() {
+  asyncTest("submit with missing password", function() {
     $("#email").val("unregistered@testuser.com");
     $("#vpassword").val("password");
 
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with too short of a password", function() {
+  asyncTest("submit with too short of a password", function() {
     $("#email").val("unregistered@testuser.com");
     $("#password,#vpassword").val(testHelpers.generateString(bid.PASSWORD_MIN_LENGTH - 1));
 
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with too long of a password", function() {
+  asyncTest("submit with too long of a password", function() {
     $("#email").val("unregistered@testuser.com");
     $("#password,#vpassword").val(testHelpers.generateString(bid.PASSWORD_MAX_LENGTH + 1));
 
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with missing vpassword", function() {
+  asyncTest("submit with missing vpassword", function() {
     $("#email").val("unregistered@testuser.com");
     $("#password").val("password");
 
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with unknown email", function() {
+  asyncTest("submit with unknown secondary email", function() {
     $("#email").val("unregistered@testuser.com");
     $("#password,#vpassword").val("password");
 
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with throttling", function() {
+  asyncTest("submit with throttling", function() {
     $("#email").val("registered@testuser.com");
     $("#password,#vpassword").val("password");
 
@@ -106,7 +150,7 @@
     testEmailNotSent();
   });
 
-  asyncTest("requestPasswordReset with XHR Error", function() {
+  asyncTest("submit with XHR Error", function() {
     $("#email").val("testuser@testuser.com");
     $("#password,#vpassword").val("password");
 
diff --git a/resources/static/test/mocks/xhr.js b/resources/static/test/mocks/xhr.js
index 1a23e87b0644c0b73c6e8140268008886d19a011..c5abae2d3c003c8baf1e451e33ac918ee3575799 100644
--- a/resources/static/test/mocks/xhr.js
+++ b/resources/static/test/mocks/xhr.js
@@ -108,10 +108,12 @@ BrowserID.Mocks.xhr = (function() {
       "get /wsapi/have_email?email=registered%40testuser.com throttle": { email_known: true },
       "get /wsapi/have_email?email=registered%40testuser.com ajaxError": undefined,
       "get /wsapi/have_email?email=testuser%40testuser.com valid": { email_known: true },
+      "get /wsapi/have_email?email=testuser%40testuser.com primary": { email_known: true },
       "get /wsapi/have_email?email=testuser%40testuser.com throttle": { email_known: true },
       "get /wsapi/have_email?email=testuser%40testuser.com ajaxError": undefined,
       "get /wsapi/have_email?email=unregistered%40testuser.com valid": { email_known: false },
       "get /wsapi/have_email?email=unregistered%40testuser.com primary": { email_known: false },
+      "get /wsapi/have_email?email=registered%40testuser.com primary": { email_known: true },
       "post /wsapi/remove_email valid": { success: true },
       "post /wsapi/remove_email invalid": { success: false },
       "post /wsapi/remove_email multiple": { success: true },
@@ -150,12 +152,17 @@ BrowserID.Mocks.xhr = (function() {
       "get /wsapi/address_info?email=unregistered%40testuser.com throttle": { type: "secondary", known: false },
       "get /wsapi/address_info?email=unregistered%40testuser.com valid": { type: "secondary", known: false },
       "get /wsapi/address_info?email=unregistered%40testuser.com unknown_secondary": { type: "secondary", known: false },
+      "get /wsapi/address_info?email=unregistered%40testuser.com primary": { type: "primary", auth: "https://auth_url", prov: "https://prov_url" },
+
+      "get /wsapi/address_info?email=registered%40testuser.com valid": { type: "secondary", known: true },
       "get /wsapi/address_info?email=registered%40testuser.com known_secondary": { type: "secondary", known: true },
+      "get /wsapi/address_info?email=registered%40testuser.com throttle": { type: "secondary", known: true },
       "get /wsapi/address_info?email=registered%40testuser.com primary": { type: "primary", auth: "https://auth_url", prov: "https://prov_url" },
-      "get /wsapi/address_info?email=unregistered%40testuser.com primary": { type: "primary", auth: "https://auth_url", prov: "https://prov_url" },
-      "get /wsapi/address_info?email=testuser%40testuser.com unknown_secondary": { type: "secondary", known: false },
-      "get /wsapi/address_info?email=testuser%40testuser.com known_secondary": { type: "secondary", known: true },
       "get /wsapi/address_info?email=registered%40testuser.com mustAuth": { type: "secondary", known: true },
+
+      "get /wsapi/address_info?email=testuser%40testuser.com valid": { type: "secondary", known: true },
+      "get /wsapi/address_info?email=testuser%40testuser.com known_secondary": { type: "secondary", known: true },
+      "get /wsapi/address_info?email=testuser%40testuser.com unknown_secondary": { type: "secondary", known: false },
       "get /wsapi/address_info?email=testuser%40testuser.com primary": { type: "primary", auth: "https://auth_url", prov: "https://prov_url" },
       "get /wsapi/address_info?email=testuser%40testuser.com ajaxError": undefined,
       "post /wsapi/add_email_with_assertion invalid": { success: false },
diff --git a/resources/views/forgot.ejs b/resources/views/forgot.ejs
index 7e76d76e2966c898768b7685a462331b1f9c4603..2dff1e3ef8e73a26a990ada9552ab10bda361535 100644
--- a/resources/views/forgot.ejs
+++ b/resources/views/forgot.ejs
@@ -24,7 +24,7 @@
             <ul class="inputs forminputs">
                 <li>
                     <label for="email"><%- gettext('Email Address') %></label>
-                    <input id="email" autofocus required placeholder="<%- gettext('Your Email') %>" type="email" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="254" />
+                    <input id="email" required placeholder="<%- gettext('Your Email') %>" type="email" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="254" disabled />
 
                     <div id="email_format" class="tooltip" for="email">
                       <%- gettext('This field must be an email address.') %>
@@ -41,11 +41,15 @@
                     <div id="not_registered" class="tooltip" for="email">
                       <%- gettext('Non existent user!') %>
                     </div>
+
+                    <div id="primary_address" class="tooltip" for="email">
+                      <%- gettext('Cannot reset the password of that address.') %>
+                    </div>
                 </li>
 
                 <li>
                     <label for="password"><%= format(gettext("Create a new password to use with %s."), ["Persona"]) %></label>
-                    <input id="password" placeholder="<%- gettext('Password') %>" type="password" maxlength="80" />
+                    <input id="password" placeholder="<%- gettext('Password') %>" type="password" maxlength="80" autofocus />
 
                     <div id="password_required" class="tooltip" for="password">
                         <%- gettext('Password is required.') %>