diff --git a/example/rp/index.html b/example/rp/index.html
index c851b321aba15fe96ea2e92172d1648e37de2004..c3fa2e9f208c6b5398e749a2b3ad2602f478eb1f 100644
--- a/example/rp/index.html
+++ b/example/rp/index.html
@@ -144,7 +144,7 @@ function checkAssertion(assertion) {
 };
 
 navigator.id.experimental.watch({
-  email: (storage.loggedInUser === 'null') ? null : storage.loggedInUser,
+  loggedInEmail: (storage.loggedInUser === 'null') ? null : storage.loggedInUser,
   onready: function () {
     loggit("onready");
     var txt = serial++ + ' navigator.id ready at ' + (new Date).toString();
@@ -157,6 +157,8 @@ navigator.id.experimental.watch({
     $(".loginEvents > pre").text(txt);
 
     checkAssertion(assertion);
+
+    $(".specify button.assertion").removeAttr('disabled');
   },
   onlogout: function () {
     loggit("onlogout");
@@ -167,8 +169,6 @@ navigator.id.experimental.watch({
 
 $(document).ready(function() {
   $(".specify button.assertion").click(function() {
-    $("pre").text("... waiting ...");
-
     var requiredEmail = $.trim($('#requiredEmail').val());
     if (!requiredEmail.length) requiredEmail = undefined;
 
@@ -178,8 +178,8 @@ $(document).ready(function() {
       privacyURL: $('#privacy').attr('checked') ? "/privacy.html" : undefined,
       tosURL: $('#tos').attr('checked') ? "/TOS.html" : undefined,
       requiredEmail: requiredEmail,
-      onclose: function() {
-        loggit("onclose");
+      oncancel: function() {
+        loggit("oncancel");
         $(".specify button.assertion").removeAttr('disabled');
       }
     });
diff --git a/resources/static/dialog/controllers/required_email.js b/resources/static/dialog/controllers/required_email.js
index 7155951bff5d80eab348c5f86483cc97013d3a6d..67505cbb6aed1a3e0c8df11c62b547aadc4a750e 100644
--- a/resources/static/dialog/controllers/required_email.js
+++ b/resources/static/dialog/controllers/required_email.js
@@ -91,7 +91,10 @@ BrowserID.Modules.RequiredEmail = (function() {
 
 
   function cancel() {
-    this.close(secondaryAuth ? "cancel_state" : "cancel");
+    // The cancel button is only shown to a user who has to enter their
+    // password to go from "assertion" authentication to "password"
+    // authentication.
+    this.close("cancel_state");
   }
 
   var RequiredEmail = bid.Modules.PageModule.extend({
diff --git a/resources/static/dialog/resources/state.js b/resources/static/dialog/resources/state.js
index 6dfa32485ce929e54ebfe992a0efdc95b4b0af00..6fb5c9ec6363dd9475612df059cf354e8f69137a 100644
--- a/resources/static/dialog/resources/state.js
+++ b/resources/static/dialog/resources/state.js
@@ -62,7 +62,9 @@ BrowserID.State = (function() {
     handleState("window_unload", function() {
       if (!self.success) {
         storage.setStagedOnBehalfOf("");
-        startAction("doCancel");
+        // do not call doCancel here, let winchan's cancel
+        // handling do the work. This gives us consistent semantics
+        // across browsers on the RP side of the WinChan.
       }
     });
 
diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js
index 212c3f444221485884508251a170e38453a68221..ccf7e540c84d57bd5b5480b5050eb578bcff04bc 100644
--- a/resources/static/include_js/include.js
+++ b/resources/static/include_js/include.js
@@ -1012,6 +1012,9 @@
         throw "non-function where function expected in parameters to navigator.id.watch()";
       }
 
+      if (!options.onlogin) throw "'onlogin' is a required argument to navigator.id.watch()";
+      if (!options.onlogout) throw "'onlogout' is a required argument to navigator.id.watch()";
+
       observers.login = options.onlogin || null;
       observers.logout = options.onlogout || null;
       observers.ready = options.onready || null;
@@ -1021,10 +1024,10 @@
       // check that the commChan was properly initialized before interacting with it.
       // on unsupported browsers commChan might still be undefined, in which case
       // we let the dialog display the "unsupported browser" message upon spawning.
-      if (typeof options.email !== 'undefined' && commChan) {
+      if (typeof options.loggedInEmail !== 'undefined' && commChan) {
         commChan.notify({
           method: 'loggedInUser',
-          params: options.email
+          params: options.loggedInEmail
         });
       }
     }
@@ -1092,9 +1095,12 @@
           }
         }
 
-        // complete
-        if (options && options.onclose) options.onclose();
-        delete options.onclose;
+        // if either err indicates the user canceled the signin (expected) or a
+        // null response was sent (unexpected), invoke the .oncancel() handler.
+        if (err === 'client closed window' || !r) {
+          if (options && options.oncancel) options.oncancel();
+          delete options.oncancel;
+        }
       });
     };
 
@@ -1132,7 +1138,7 @@
             }
           }
         });
-        options.onclose = function() {
+        options.oncancel = function() {
           if (callback) {
             callback(null);
             callback = null;
diff --git a/resources/static/test/cases/controllers/required_email.js b/resources/static/test/cases/controllers/required_email.js
index 474adf97a94dfbd014bd6c718258f10c2b6a9554..5a92da544ebd7de3e4a013ac63f7138dfed7475b 100644
--- a/resources/static/test/cases/controllers/required_email.js
+++ b/resources/static/test/cases/controllers/required_email.js
@@ -454,24 +454,7 @@
     });
   });
 
-  asyncTest("cancel normally raises the 'cancel' message", function() {
-    var email = "registered@testuser.com",
-        message = "cancel";
-
-    createController({
-      email: email,
-      ready: function() {
-        register(message, function(item, info) {
-          ok(true, message + " received");
-          start();
-        });
-
-        controller.cancel();
-      }
-    });
-  });
-
-  asyncTest("cancel with 'secondary_auth' raises the 'cancel_state' message", function() {
+  asyncTest("cancel raises the 'cancel_state' message", function() {
     var email = "registered@testuser.com",
         message = "cancel_state";