diff --git a/rp/server/run.js b/rp/server/run.js
index 291f1e1cc25ee53b4182dcbad51aa7a222694d42..710eca65d258b4bc51a5628958b87f4ed11d8216 100644
--- a/rp/server/run.js
+++ b/rp/server/run.js
@@ -3,17 +3,17 @@ const path = require('path'),
      wsapi = require('./wsapi.js'),
  httputils = require('./httputils.js'),
    connect = require('connect'),
-        fs = require('fs');
+        fs = require('fs'),
+    verify = require('./verify.js');
 
 const STATIC_DIR = path.join(path.dirname(__dirname), "static");
 
-exports.handler = function(request, response, serveFile) {
+exports.handler = function(request, response, serveFile, subHostNames) {
   // dispatch!
   var urlpath = url.parse(request.url).pathname;
 
-  if (urlpath === '/authenticate') {
-    // XXX: do something
-    httputils.serverError(response, "notImplemented");
+  if (urlpath === '/login') {
+    verify.performVerfication(subHostNames("http://verifier.mozilla.org"), subHostNames("rp.mozilla.org"), request, response);
   } else {
     // node.js takes care of sanitizing the request path
     // automatically serve index.html if this is a directory
diff --git a/rp/server/verify.js b/rp/server/verify.js
new file mode 100644
index 0000000000000000000000000000000000000000..1dc36c8f3bf1941670107b579b8838e874b3895f
--- /dev/null
+++ b/rp/server/verify.js
@@ -0,0 +1,52 @@
+const httputils = require('./httputils.js'),
+           http = require('http'),
+            url = require('url'); 
+
+exports.performVerfication = function(server, audience, req, resp) {
+  console.log("performVerification called");
+
+  var assertion = "";
+
+  req.on('data', function(str) {
+    assertion += str;
+  });
+  req.on('end', function() {
+    console.log("Got assertion for verification: " + assertion);
+    console.log("bouncing this off my verification server: " + server);
+
+    serverParsed = url.parse(server);
+
+    try {
+      var req = http.request({
+        host: serverParsed.hostname,
+        port: serverParsed.port,
+        path: '/wsapi/verify?assertion=' + encodeURIComponent(assertion) + "&audience=" + audience,
+        method: 'GET'
+      }, function(res) {
+        res.setEncoding('utf8');
+        res.on('data', function (chunk) {
+          try {
+            if (res.statusCode != 200) throw "bad status: " + res.statusCode;
+            var response;
+            try { response = JSON.parse(chunk); } catch(e) { throw "malformed json response body"; }
+            if (response.status === 'failure') throw response.reason;
+            else if (response.status === 'okay') {
+              console.log("your identity is validated!");
+              // extract email address and store it in session state
+              httputils.serverError(resp, "not implemented");
+            } else {
+              throw "unknown response code: " + response.status;
+            }
+          } catch(e) {
+            httputils.serverError(resp, "error verifying assertion: " + e);
+          }
+        });
+      });
+      // execute the request
+      req.end();
+    } catch(e) {
+      console.log("request to verifier failed: " + e);
+      httputils.serverError(resp, "verifierFailed");
+    }
+  });
+};
diff --git a/rp/static/index.html b/rp/static/index.html
index f90e388b200e55183a25d7d6f6723fb859d1d2a4..33407ce71f9d6bfef662002837bf649445e25914 100644
--- a/rp/static/index.html
+++ b/rp/static/index.html
@@ -84,14 +84,19 @@ an example app that uses Mozilla ID for an awesome login experience.
         // validation
         $("#logininstructions").empty().html($("<p>verifying your identity of <i>" + assertion + "</i></p>"));
 
-        $.post(
-          '/login',
-          JSON.stringify(assertion),
-          function(data, textStatus) {
+        $.ajax({
+          url: '/login',
+          type: "post",
+          data: assertion,
+          success: function(data, textStatus, jqXHR) {
             // we've got a response from the server half of the mywords
             // application which has validated the assertion
             $("#logininstructions").empty().text("verification complete: " + data);
-          });
+          },
+          error: function(jqXHR, textStatus, errorThrown) {
+            $("#logininstructions").empty().text("verification failed: " + jqXHR.responseText);
+          }
+        })
       }, function(code, msg) {
         alert("something very bad happened! ("+code+"): " + msg); 
       });
diff --git a/run.js b/run.js
index 42da0e40f86b80f70fc1a3f46391c45e6cb5e3e4..037dd1b64a2c75e18b2c38af4cbd4d7b51cee340 100644
--- a/run.js
+++ b/run.js
@@ -25,6 +25,17 @@ function getSiteRef(host, port) {
   return undefined;
 }
 
+function subHostNames(data) {
+  for (var i = 0; i < boundServers.length; i++) {
+    var o = boundServers[i]
+    var a = o.server.address();
+    var from = o.name + ".mozilla.org";
+    var to = a.address + ":" + a.port;
+    data = data.replace(new RegExp(from, 'g'), to);
+  }
+  return data;
+}
+
 function getServerByName(name) {
   for (var i = 0; i < boundServers.length; i++) {
     if (boundServers[i].name === name) return boundServers[i];
@@ -61,13 +72,7 @@ function serveFile(filename, response) {
       var ext = path.extname(filename);
       var mimeType = exts[ext] || "application/octet-stream";
 
-      for (var i = 0; i < boundServers.length; i++) {
-        var o = boundServers[i]
-        var a = o.server.address();
-        var from = o.name + ".mozilla.org";
-        var to = a.address + ":" + a.port;
-        data = data.replace(new RegExp(from, 'g'), to);
-      }
+      data = subHostNames(data);
 
       response.writeHead(200, {"Content-Type": mimeType});
       response.write(data, "binary");
@@ -97,7 +102,7 @@ function createServer(obj) {
   // if this site has a handler, we'll run that, otherwise serve statically
   if (obj.handler) {
     server.use(function(req, resp, next) {
-      obj.handler(req, resp, serveFile);
+      obj.handler(req, resp, serveFile, subHostNames);
     });
   } else {
     server.use(function(req, resp, next) {