diff --git a/authority/server/run.js b/authority/server/run.js
index 9b214a9737144603e812cfe75cad1b7085f0684a..cbcf9da3e553eaadb8f17a6d7a84233b12f2c2a1 100644
--- a/authority/server/run.js
+++ b/authority/server/run.js
@@ -17,8 +17,8 @@ exports.handler = function(request, response, serveFile) {
       wsapi[method](request, response);
     } catch(e) {
       var errMsg = "oops, error executing wsapi method: " + method + " (" + e.toString() +")";
-      httputils.fourOhFour(response, errMsg);
       console.log(errMsg);
+      httputils.fourOhFour(response, errMsg);
     }
   } else {
     // node.js takes care of sanitizing the request path
diff --git a/authority/static/dialog/crypto-stubs.js b/authority/static/dialog/crypto-stubs.js
index be803e98373d4b1d5049fcd1aff7f50545a7dc32..99b5c349a06c605026487c24c5aa9a1eb84fb360 100644
--- a/authority/static/dialog/crypto-stubs.js
+++ b/authority/static/dialog/crypto-stubs.js
@@ -22,7 +22,17 @@ CryptoStubs = (function() {
     };
   }
 
+  function createAssertion(audience, email, privkey) {
+    // XXX: in the future, we need to sign via JWT spec, now let's just glom together and stringify
+    return JSON.stringify({
+      audience: audience,
+      email: email,
+      "valid-until": (new Date()).getTime() + (1000 * 120) // 2 mins from now.
+    });
+  }
+
   return {
-    genKeyPair: genKeyPair
+    genKeyPair: genKeyPair,
+    createAssertion: createAssertion
   };
 })();
diff --git a/authority/static/dialog/index.html b/authority/static/dialog/index.html
index 0cd21b90637c6bdd2edeacdc50e6ecdb7c96a81c..018d83679de6ab07180610be91f7b5a46eae86d9 100644
--- a/authority/static/dialog/index.html
+++ b/authority/static/dialog/index.html
@@ -70,12 +70,20 @@
     <form id="identities" name="identities">
     </form>
   </div>
+  <div class="actions">
+    <div class="action"><a href="#">Add a new email address</a></div>
+  </div>
 </div>
 <div id="error_dialog" class="dialog">
   <div class="title"> Sign in with Firefox ID </div>
   <div class="content">
   </div>
 </div>
+<div id="waiting_dialog" class="dialog">
+  <div class="title"> Sign in with Firefox ID </div>
+  <div class="content">
+  </div>
+</div>
 <div id="bottom-bar">
   <button id="back">Go Back</button>
   <button id="submit" class="righty action">Sign In</button>
diff --git a/authority/static/dialog/main.js b/authority/static/dialog/main.js
index 7f27a9719d106d651c7d13462c33a799d8e4c09a..1561829cdecb5212f3bedfeee98abbcf7111e5ad 100644
--- a/authority/static/dialog/main.js
+++ b/authority/static/dialog/main.js
@@ -8,6 +8,8 @@
       scope: "mozid"
     });
 
+  var remoteOrigin = undefined;
+
   function runSignInDialog(onsuccess, onerror) {
     $(".dialog").hide();
 
@@ -16,12 +18,29 @@
       onerror("canceled");
     });
     $("#submit").show().unbind('click').click(function() {
-      onerror("notImplemented");
+      var email = $("#identities input:checked").parent().find("div").text();
+      // yay!  now we need to produce an assertion.
+      var privkey = JSON.parse(window.localStorage.emails)[email].priv;
+      var assertion = CryptoStubs.createAssertion(remoteOrigin, email, privkey);
+      onsuccess(assertion);
     }).text("Sign In");
 
     $("#default_dialog div.actions div.action a").unbind('click').click(function() {
       onerror("notImplemented");
     });
+
+    // now populate the selection list with all available emails
+    // we assume there are identities available, because without them 
+    var emails = JSON.parse(window.localStorage.emails);
+    var first = true; 
+    for (var k in emails) {
+      var id = $("<div />")
+        .append($("<input />").attr('type', 'radio').attr('name', 'identity').attr('checked', first))
+        .append($("<div />").text(k));
+      first = false;
+      id.appendTo($("form#identities"));
+    }
+
     $("#sign_in_dialog").fadeIn(500);
   }
 
@@ -53,10 +72,9 @@
   var nextEmailToCheck = undefined;
   // a set of emails that we've checked for this session
   var checkedEmails = {
-
   };
 
-  function runConfirmEmailDialog(email, onsuccess, onerror) {
+  function runConfirmEmailDialog(email, keypair, onsuccess, onerror) {
     $(".dialog").hide();
 
     $("span.email").text(email);
@@ -73,7 +91,11 @@
             //   'pending'  - a registration is in progress
             //   'noRegistration' - no registration is in progress  
             if (status === 'complete') {
-              // XXX: now we need to add all of the pertinent data to local storage
+              // now we need to add all of the pertinent data to local storage
+              var emails = {};
+              if (window.localStorage.emails) emails = JSON.parse(window.localStorage.emails);
+              emails[email] = keypair;
+              window.localStorage.emails = JSON.stringify(emails);
 
               // and tell the user that everything is really quite awesome.
               runConfirmedEmailDialog(email, onsuccess, onerror);
@@ -88,8 +110,6 @@
                 onsuccess,
                 onerror);
             }
-            console.log("success");
-            console.log(data);
           },
           error: function(jqXHR, textStatus, errorThrown) {
             runErrorDialog("serverError", "Registration Failed", jqXHR.responseText, onsuccess, onerror);
@@ -151,6 +171,21 @@
     $("#error_dialog").fadeIn(500);
   }
 
+  function runWaitingDialog(title, message, onsuccess, onerror) {
+    $(".dialog").hide();
+
+    $("#waiting_dialog div.title").text(title);
+    $("#waiting_dialog div.content").text(message);
+
+    $("#back").hide();
+    $("#submit").hide();
+    $("#cancel").show().unbind('click').click(function() {
+      onerror("canceled");
+    });
+
+    $("#waiting_dialog").fadeIn(500);
+  }
+
   function runCreateDialog(onsuccess, onerror) {
     $(".dialog").hide();
 
@@ -169,14 +204,19 @@
       var pass = $("#create_dialog input:eq(1)").val();
       var keypair = CryptoStubs.genKeyPair();
 
-      // XXX: we should be showing the user a waiting/status page here
+      // kick the user to waiting/status page while we talk to the server.
+      runWaitingDialog(
+        "One Moment Please...",
+        "We're creating your account, this should only take a couple seconds",
+        onsuccess,
+        onerror
+      );
 
       $.ajax({
         url: '/wsapi/stage_user?email=' + encodeURIComponent(email) + '&pass=' + encodeURIComponent(pass) + '&pubkey=' + encodeURIComponent(keypair.pub),
         success: function() {
-
           // account successfully staged, now wait for email confirmation
-          runConfirmEmailDialog(email, onsuccess, onerror);
+          runConfirmEmailDialog(email, keypair, onsuccess, onerror);
         },
         error: function() {
           runErrorDialog(
@@ -186,8 +226,6 @@
             onsuccess, onerror);
         }
       });
-
-
     }).text("Continue").addClass("disabled");
 
 
@@ -297,11 +335,24 @@
   chan.bind("getVerifiedEmail", function(trans, s) {
     trans.delayReturn(true);
 
+    remoteOrigin = trans.origin;
+
     // set the requesting site
     $(".sitename").text(trans.origin.replace(/^.*:\/\//, ""));
 
-    // XXX: check to see if there's any pubkeys stored in the browser
+    // check to see if there's any pubkeys stored in the browser
     var haveIDs = false;
+    try {
+      var emails = JSON.parse(window.localStorage.emails);
+      if (typeof emails !== 'object') throw "emails blob bogus!";
+      for (var k in emails) {
+        if (!emails.hasOwnProperty(k)) continue;
+        haveIDs = true;
+        break;
+      }
+    } catch(e) {
+      window.localStorage.emails = JSON.stringify({});
+    }
 
     if (haveIDs) {
       runSignInDialog(function(rv) {
diff --git a/authority/static/dialog/style.css b/authority/static/dialog/style.css
index 5de639d084b8f0d11e533819c3a8b4a03a887a13..f1cd65060c09caf03db8dd2cb1bae559ab25fa80 100644
--- a/authority/static/dialog/style.css
+++ b/authority/static/dialog/style.css
@@ -93,10 +93,15 @@ div.actions {
     margin-left: 79px;
 }
 
+#identities {
+    width: 450px;
+    margin: auto;
+}
+
 #identities > div {
-    margin-left: .8em;
     margin-bottom: .5em;
     height: 1.4em;
+    font-size: .9em;
 }
 
 #identities > div > * {
@@ -131,21 +136,22 @@ div.input > div.note {
     margin-top: .7em;
 }
 
-div.content > div.input {
+div.content {
     width: 450px;
-    height: 3em;
     margin: auto;
+    text-align: left;
+}
+
+div.content > div.input {
+    height: 3em;
     font-size: .8em;
 }
 
 div.content > div.summary {
     text-align: left;
-    width: 450px;
-    margin: auto;
     margin-bottom: 2em;
 }
 
-
 div.content > div.input > div.label {
     width: 80px;
     text-align: left;