diff --git a/lib/primary.js b/lib/primary.js
index c76a80af9a5723d98ecb53bf3e18943315e21be2..e47f13fa7109b8a983a6fbf0909f4c93f3b37923 100644
--- a/lib/primary.js
+++ b/lib/primary.js
@@ -173,6 +173,15 @@ if (process.env['SHIMMED_PRIMARIES']) {
   });
 }
 
+exports.getPublicKey = function(domain, cb) {
+  exports.checkSupport(domain, function(err, rv) {
+    if (err) return cb(err);
+    var pubKey = g_cache[domain].publicKey;
+    if (!pubKey) return cb("can't get public key for " + domain);
+    cb(null, pubKey);
+  });
+};
+
 // verify an assertion generated to authenticate to browserid
 exports.verifyAssertion = function(assertion, cb) {
   try {
@@ -188,10 +197,8 @@ exports.verifyAssertion = function(assertion, cb) {
       if (issuer === config.get('hostname')) {
         cb("cannot authenticate to browserid with a certificate issued by it.");
       } else {
-        exports.checkSupport(issuer, function(err, rv) {
+        exports.getPublicKey(issuer, function(err, pubKey) {
           if (err) return cb(err);
-          var pubKey = g_cache[issuer].publicKey;
-          if (!pubKey) return cb("can't get public key for " + issuer);
           next(pubKey);
         });
       }
@@ -213,4 +220,4 @@ exports.verifyAssertion = function(assertion, cb) {
         cb("can't verify assertion: " + e.toString());
       }
     }, cb);
-};
\ No newline at end of file
+};
diff --git a/lib/verifier/certassertion.js b/lib/verifier/certassertion.js
index 0a954a76c72f07e8ccc9013318d6a9fac7b4f807..e41345af9407ca21dde3d98fc405b8cb778c1f42 100644
--- a/lib/verifier/certassertion.js
+++ b/lib/verifier/certassertion.js
@@ -47,7 +47,8 @@ jwcert = require("jwcrypto/jwcert"),
 vep = require("jwcrypto/vep"),
 config = require("../configuration.js"),
 logger = require("../logging.js").logger,
-secrets = require('../secrets.js');
+secrets = require('../secrets.js'),
+primary = require('../primary.js');
 
 try {
   const publicKey = secrets.loadPublicKey();
@@ -126,12 +127,29 @@ function verify(assertion, audience, successCB, errorCB) {
     return errorCB("malformed assertion");
   }
 
+  var ultimateIssuer;
+
   jwcert.JWCert.verifyChain(
     bundle.certificates,
     new Date(), function(issuer, next) {
+      // update issuer with each issuer in the chain, so the
+      // returned issuer will be the last cert in the chain
+      ultimateIssuer = issuer;
+
       // allow other retrievers for testing
       if (issuer === config.get('hostname')) return next(publicKey);
-      return errorCB("this verifier doesn't respect certs issued from domains other than: " + config.get('hostname'));
+
+      // XXX: this network work happening inside a compute process.
+      // if we have a large number of requests to auth assertions that require
+      // keyfetch, this could theoretically hurt our throughput.  We could
+      // move the fetch up into the browserid process and pass it into the
+      // compute process at some point.
+      
+      // let's go fetch the public key for this host
+      primary.getPublicKey(issuer, function(err, pubKey) {
+        if (err) return errorCB(err);
+        next(pubKey);
+      });
     }, function(pk, principal) {
       var tok = new jwt.JWT();
       tok.parse(bundle.assertion);
@@ -144,8 +162,18 @@ function verify(assertion, audience, successCB, errorCB) {
         return errorCB("audience mismatch: " + err);
       }
 
+      // verify that the issuer is the same as the email domain
+      // NOTE: for "delegation of authority" support we'll need to make this check
+      // more sophisticated
+      var domainFromEmail = principal.email.replace(/^.*@/, '');
+      if (ultimateIssuer != config.get('hostname') && ultimateIssuer !== domainFromEmail)
+      {
+        return errorCB("issuer issue '" + ultimateIssuer + "' may not speak for emails from '"
+                       + domainFromEmail + "'");
+      }
+
       if (tok.verify(pk)) {
-        successCB(principal.email, tok.audience, tok.expires, config.get('hostname'));
+        successCB(principal.email, tok.audience, tok.expires, ultimateIssuer);
       } else {
         errorCB("verification failure");
       }
diff --git a/tests/verifier-test.js b/tests/verifier-test.js
index 1cf64f544483fe8829b246fadbc563a2cc69ecfd..2d90a5ff31c0e7793b0bb6dd34451fa4c745e072 100755
--- a/tests/verifier-test.js
+++ b/tests/verifier-test.js
@@ -49,7 +49,8 @@ jwt = require('jwcrypto/jwt.js'),
 vep = require('jwcrypto/vep.js'),
 jwcert = require('jwcrypto/jwcert.js'),
 http = require('http'),
-querystring = require('querystring');
+querystring = require('querystring'),
+path = require('path');
 
 var suite = vows.describe('verifier');
 
@@ -657,10 +658,10 @@ suite.addBatch({
   }
 });
 
-// now verify that no-one other than browserid is allowed to issue assertions
-// (until primary support is implemented)
+// now verify that assertions from a primary who does not have browserid support
+// will fail to verify 
 suite.addBatch({
-  "generating an assertion from a cert signed by some other domain": {
+  "generating an assertion from a cert signed by a bogus primary": {
     topic: function() {
       var fakeDomainKeypair = jwk.KeyPair.generate("RS", 64);
       var newClientKeypair = jwk.KeyPair.generate("DS", 256);
@@ -686,7 +687,85 @@ suite.addBatch({
       "to return a clear error message": function (r, err) {
         var resp = JSON.parse(r.body);
         assert.strictEqual(resp.status, 'failure');
-        assert.strictEqual(resp.reason, "this verifier doesn't respect certs issued from domains other than: 127.0.0.1");
+        assert.strictEqual(resp.reason, "can't get public key for otherdomain.tld");
+      }
+    }
+  }
+});
+
+// now verify that assertions from a primary who does have browserid support
+// but has no authority to speak for an email address will fail
+suite.addBatch({
+  "generating an assertion from a cert signed by a real (simulated) primary": {
+    topic: function() {
+      var secretKey = jwk.SecretKey.fromSimpleObject(
+        JSON.parse(require('fs').readFileSync(
+          path.join(__dirname, '..', 'example', 'primary', 'sample.privatekey'))));
+
+      var newClientKeypair = jwk.KeyPair.generate("DS", 256);
+      expiration = new Date(new Date().getTime() + (1000 * 60 * 60 * 6));
+      var cert = new jwcert.JWCert("example.domain", expiration, new Date(), newClientKeypair.publicKey,
+                                   {email: TEST_EMAIL}).sign(secretKey);
+
+      var expirationDate = new Date(new Date().getTime() + (2 * 60 * 1000));
+      var tok = new jwt.JWT(null, expirationDate, TEST_ORIGIN);
+      return vep.bundleCertsAndAssertion([cert], tok.sign(newClientKeypair.secretKey));
+    },
+    "yields a good looking assertion": function (r, err) {
+      assert.isString(r);
+      assert.equal(r.length > 0, true);
+    },
+    "will cause the verifier": {
+      topic: function(assertion) {
+        wsapi.post('/verify', {
+          audience: TEST_ORIGIN,
+          assertion: assertion
+        }).call(this);
+      },
+      "to return a clear error message": function (r, err) {
+        var resp = JSON.parse(r.body);
+        assert.strictEqual(resp.status, 'failure');
+        assert.strictEqual(resp.reason, "issuer issue 'example.domain' may not speak for emails from 'somedomain.com'");
+      }
+    }
+  }
+});
+
+// now verify that assertions from a primary who does have browserid support
+// and may speak for an email address will succeed
+suite.addBatch({
+  "generating an assertion from a cert signed by a real (simulated) primary": {
+    topic: function() {
+      var secretKey = jwk.SecretKey.fromSimpleObject(
+        JSON.parse(require('fs').readFileSync(
+          path.join(__dirname, '..', 'example', 'primary', 'sample.privatekey'))));
+
+      var newClientKeypair = jwk.KeyPair.generate("DS", 256);
+      expiration = new Date(new Date().getTime() + (1000 * 60 * 60 * 6));
+      var cert = new jwcert.JWCert("example.domain", expiration, new Date(), newClientKeypair.publicKey,
+                                   {email: "foo@example.domain"}).sign(secretKey);
+
+      var expirationDate = new Date(new Date().getTime() + (2 * 60 * 1000));
+      var tok = new jwt.JWT(null, expirationDate, TEST_ORIGIN);
+      return vep.bundleCertsAndAssertion([cert], tok.sign(newClientKeypair.secretKey));
+    },
+    "yields a good looking assertion": function (r, err) {
+      assert.isString(r);
+      assert.equal(r.length > 0, true);
+    },
+    "will cause the verifier": {
+      topic: function(assertion) {
+        wsapi.post('/verify', {
+          audience: TEST_ORIGIN,
+          assertion: assertion
+        }).call(this);
+      },
+      "to return a clear error message": function (r, err) {
+        var resp = JSON.parse(r.body);
+        assert.strictEqual(resp.status, 'okay');
+        assert.strictEqual(resp.issuer, "example.domain");
+        assert.strictEqual(resp.audience, TEST_ORIGIN);
+        assert.strictEqual(resp.email, "foo@example.domain");
       }
     }
   }