diff --git a/browserid/app.js b/browserid/app.js
index e116604c8b7f46b8b3567be21cbe8c43a2c6e773..c646a2bc75950cc45829ca212c92ed6eb5831459 100644
--- a/browserid/app.js
+++ b/browserid/app.js
@@ -21,71 +21,71 @@ const COOKIE_SECRET = secrets.hydrateSecret('cookie_secret', VAR_DIR);
 const COOKIE_KEY = 'browserid_state';
 
 function handler(request, response, next) {
-    // dispatch!
-    var urlpath = url.parse(request.url).pathname;
+  // dispatch!
+  var urlpath = url.parse(request.url).pathname;
 
-    if (urlpath === '/sign_in') {
-        // a little remapping!
-        request.url = "/dialog/index.html";
-        next();
-    } else if (urlpath === '/register_iframe') {
-        request.url = "/dialog/register_iframe.html";
-        next();
-    } else if (/^\/wsapi\/\w+$/.test(urlpath)) {
-        try {
-            var method = path.basename(urlpath);
-            wsapi[method](request, response);
-        } catch(e) {
-            var errMsg = "oops, error executing wsapi method: " + method + " (" + e.toString() +")";
-            console.log(errMsg);
-            httputils.fourOhFour(response, errMsg);
-        }
-    } else if (/^\/users\/[^\/]+.xml$/.test(urlpath)) {
-        var identity = path.basename(urlpath).replace(/.xml$/, '').replace(/^acct:/, '');
-
-        webfinger.renderUserPage(identity, function (resultDocument) {
-            if (resultDocument === undefined) {
-                httputils.fourOhFour(response, "I don't know anything about: " + identity + "\n");
-            } else {
-                httputils.xmlResponse(response, resultDocument);
-            }
-        });
-    } else if (urlpath === "/code_update") {
-        console.log("code updated.  shutting down.");
-        process.exit();
-    } else {
-        next();
+  if (urlpath === '/sign_in') {
+    // a little remapping!
+    request.url = "/dialog/index.html";
+    next();
+  } else if (urlpath === '/register_iframe') {
+    request.url = "/dialog/register_iframe.html";
+    next();
+  } else if (/^\/wsapi\/\w+$/.test(urlpath)) {
+    try {
+      var method = path.basename(urlpath);
+      wsapi[method](request, response);
+    } catch(e) {
+      var errMsg = "oops, error executing wsapi method: " + method + " (" + e.toString() +")";
+      console.log(errMsg);
+      httputils.fourOhFour(response, errMsg);
     }
+  } else if (/^\/users\/[^\/]+.xml$/.test(urlpath)) {
+    var identity = path.basename(urlpath).replace(/.xml$/, '').replace(/^acct:/, '');
+
+    webfinger.renderUserPage(identity, function (resultDocument) {
+      if (resultDocument === undefined) {
+        httputils.fourOhFour(response, "I don't know anything about: " + identity + "\n");
+      } else {
+        httputils.xmlResponse(response, resultDocument);
+      }
+    });
+  } else if (urlpath === "/code_update") {
+    console.log("code updated.  shutting down.");
+    process.exit();
+  } else {
+    next();
+  }
 };
 
 exports.varDir = VAR_DIR;
 
 exports.setup = function(server) {
-    server.use(express.cookieParser());
+  server.use(express.cookieParser());
 
-    var cookieSessionMiddleware = sessions({
-        secret: COOKIE_SECRET,
-        session_key: COOKIE_KEY,
-        path: '/'
-    });
+  var cookieSessionMiddleware = sessions({
+    secret: COOKIE_SECRET,
+    session_key: COOKIE_KEY,
+    path: '/'
+  });
 
-    server.use(function(req, resp, next) {
-        try {
-            cookieSessionMiddleware(req, resp, next);
-        } catch(e) {
-            console.log("invalid cookie found: ignoring");
-            delete req.cookies[COOKIE_KEY];
-            cookieSessionMiddleware(req, resp, next);
-        }
-    });
+  server.use(function(req, resp, next) {
+    try {
+      cookieSessionMiddleware(req, resp, next);
+    } catch(e) {
+      console.log("invalid cookie found: ignoring");
+      delete req.cookies[COOKIE_KEY];
+      cookieSessionMiddleware(req, resp, next);
+    }
+  });
 
-    server.use(handler);
+  server.use(handler);
 
-    // a tweak to get the content type of host-meta correct
-    server.use(function(req, resp, next) {
-        if (req.url === '/.well-known/host-meta') {
-            resp.setHeader('content-type', 'text/xml');
-        }
-        next();
-    });
+  // a tweak to get the content type of host-meta correct
+  server.use(function(req, resp, next) {
+    if (req.url === '/.well-known/host-meta') {
+      resp.setHeader('content-type', 'text/xml');
+    }
+    next();
+  });
 }
diff --git a/browserid/lib/secrets.js b/browserid/lib/secrets.js
index 6923a989954b06fd4ffa1bb3ab1d9de94251e9ea..bcf03f302ee054abf58feecdd6fdefc1fefb30f6 100644
--- a/browserid/lib/secrets.js
+++ b/browserid/lib/secrets.js
@@ -14,11 +14,10 @@ exports.hydrateSecret = function(name, dir) {
   var p = path.join(dir, name + ".sekret");
   var fileExists = false;
   var secret = undefined;
-    
+
   try{ secret = fs.readFileSync(p).toString(); } catch(e) {};
 
   if (secret === undefined) {
-    console.log("Generating server secret ("+name+")...");
     secret = generateSecret();
     fs.writeFileSync(p, secret);
   }
diff --git a/browserid/lib/wsapi.js b/browserid/lib/wsapi.js
index aafd2d1eec96af90e3a7beca1bfc168535b32c54..ea6b5f7f6a1d4469d120acfad15a88d828959dcc 100644
--- a/browserid/lib/wsapi.js
+++ b/browserid/lib/wsapi.js
@@ -7,7 +7,6 @@ const     db = require('./db.js'),
        email = require('./email.js');
 
 function logRequest(method, args) {
-  console.log("WSAPI ("+method+") " + (args ? JSON.stringify(args) : "")); 
 }
 
 function checkParams(getArgs, resp, params) {
@@ -42,7 +41,7 @@ exports.have_email = function(req, resp) {
   // get inputs from get data!
   var email = url.parse(req.url, true).query['email'];
   logRequest("have_email", {email: email});
-  db.emailKnown(email, function(known) { 
+  db.emailKnown(email, function(known) {
     httputils.jsonResponse(resp, known);
   });
 };
diff --git a/browserid/tests/forgotten-email-test.js b/browserid/tests/forgotten-email-test.js
index 406a6b9fa55cac3c01b58e39c9c86cbfcfe7a44f..915c3f120ddd35012d4e8a49957b3dfb62b8f60f 100755
--- a/browserid/tests/forgotten-email-test.js
+++ b/browserid/tests/forgotten-email-test.js
@@ -4,7 +4,8 @@ const assert = require('assert'),
       vows = require('vows'),
       fs = require('fs'),
       path = require('path'),
-      http = require('http');
+      http = require('http'),
+      querystring = require('querystring');
 
 const amMain = (process.argv[1] === __filename);
 const varPath = path.join(path.dirname(__dirname), "var");
@@ -20,6 +21,52 @@ function removeVarDir() {
   } catch(e) {}
 }
 
+// wsapi abstractions trivial cookie jar
+var cookieJar = {};
+
+// A macro for wsapi requests
+var wsapi = {
+  get: function (path, getArgs) {
+    return function () {
+      var cb = this.callback;
+      if (typeof getArgs === 'object')
+        path += "?" + querystring.stringify(getArgs);
+
+      var headers = {};
+      if (Object.keys(cookieJar).length) {
+        headers['Cookie'] = "";
+        for (var k in cookieJar) {
+          headers['Cookie'] += k + "=" + cookieJar[k];
+        }
+      }
+      http.get({
+        host: '127.0.0.1',
+        port: '62700',
+        path: path,
+        headers: headers
+      }, function(res) {
+        // see if there are any set-cookies that we should honor
+        if (res.headers['set-cookie']) {
+          res.headers['set-cookie'].forEach(function(cookie) {
+            var m = /^([^;]+)(?:;.*)$/.exec(cookie);
+            if (m) {
+              var x = m[1].split('=');
+              cookieJar[x[0]] = x[1];
+            }
+          });
+        }
+        var body = '';
+        res.on('data', function(chunk) { body += chunk; })
+          .on('end', function() {
+            cb({code: res.statusCode, headers: res.headers, body: body});
+          });
+      }).on('error', function (e) {
+        cb();
+      });
+    };
+  }
+};
+
 suite.addBatch({
   "remove the user database": {
     topic: function() {
@@ -41,26 +88,14 @@ suite.addBatch({
       return true;
     },
     "server should be running": {
-      topic: function() {
-        var cb = this.callback;
-        http.get({
-          host: '127.0.0.1',
-          port: '62700',
-          path: '/ping.txt'
-        }, function(res) {
-          cb(true);
-        }).on('error', function (e) {
-          cb(false);
-        });
-      },
-      "server is running": function (r) {
-        assert.notStrictEqual(r, false);
+      topic: wsapi.get('/ping.txt'),
+      "server is running": function (r, err) {
+        assert.equal(r.code, 200);
       }
     }
   }
 });
 
-
 suite.addBatch({
   "wait for readiness": {
     topic: function() {
@@ -73,22 +108,120 @@ suite.addBatch({
   }
 });
 
+var lastEmailBody = undefined;
+// let's kludge our way into nodemailer to intercept outbound emails
+const nodeMailer = require('nodemailer');
+nodeMailer.EmailMessage.prototype.send = function(callback) {
+  lastEmailBody = this.body;
+};
 
-
+var token = undefined;
 
 // create a new account via the api with (first address)
+suite.addBatch({
+  "stage first account": {
+    topic: wsapi.get('/wsapi/stage_user', {
+      email: 'first@fakeemail.com',
+      pass: 'firstfakepass',
+      pubkey: 'fakepubkey',
+      site:'fakesite.com'
+    }),
+    "caused an email to be sent": function (r, err) {
+      assert.equal(r.code, 200);
+      var m = /token=([a-zA-Z0-9]+)/.exec(lastEmailBody);
+      token = m[1];
+    },
+    "the token is sane": function(r, err) {
+      assert.strictEqual('string', typeof token);
+    }
+  }
+});
+
+suite.addBatch({
+  "create first account": {
+    topic: function() {
+      wsapi.get('/wsapi/prove_email_ownership', { token: token }).call(this);
+    },
+    "account created": function(r, err) {
+      assert.equal(r.code, 200);
+    }
+  }
+});
 
-// manually verify the account
+suite.addBatch({
+  "email created": {
+    topic: wsapi.get('/wsapi/registration_status'),
+    "should exist": function(r, err) {
+      assert.strictEqual(r.code, 200);
+      assert.strictEqual(JSON.parse(r.body), "complete");
+    }
+  }
+});
 
 // add a new email address to the account (second address)
+suite.addBatch({
+  "add a new email address to our account": {
+    topic: wsapi.get('/wsapi/add_email', {
+      email: 'second@fakeemail.com',
+      pubkey: 'fakepubkey',
+      site:'fakesite.com'
+    }),
+    "caused an email to be sent": function (r, err) {
+      assert.equal(r.code, 200);
+      var m = /token=([a-zA-Z0-9]+)/.exec(lastEmailBody);
+      token = m[1];
+    },
+    "the token is sane": function(r, err) {
+      assert.strictEqual('string', typeof token);
+    }
+  }
+});
+
+// confirm second email email address to the account
+suite.addBatch({
+  "create second account": {
+    topic: function() {
+      wsapi.get('/wsapi/prove_email_ownership', { token: token }).call(this);
+    },
+    "account created": function(r, err) {
+      assert.equal(r.code, 200);
+    }
+  }
+});
+
+// verify now both email addresses are known
+suite.addBatch({
+  "first email exists": {
+    topic: wsapi.get('/wsapi/have_email', { email: 'first@fakeemail.com' }),
+    "should exist": function(r, err) {
+      assert.strictEqual(true, JSON.parse(r.body));
+    }
+  },
+  "second email exists": {
+    topic: wsapi.get('/wsapi/have_email', { email: 'second@fakeemail.com' }),
+    "should exist": function(r, err) {
+      assert.strictEqual(JSON.parse(r.body), true);
+    }
+  },
+  "a random email doesn't exist": {
+    topic: wsapi.get('/wsapi/have_email', { email: 'third@fakeemail.com' }),
+    "shouldn't exist": function(r, err) {
+      assert.strictEqual(JSON.parse(r.body), false);
+    }
+  }
+});
 
 // run the "forgot_email" flow with first address
+// XXX
 
 // try to log into the first email address with oldpassword
+// XXX
 
 // try to log into the second email address with oldpassword
+// XXX
 
 // try to log into the first email with newpassword
+// XXX
 
 // stop the server
 suite.addBatch({