diff --git a/lib/browserid/primary.js b/lib/browserid/primary.js index f2a8543d184da603b937f88bb4b4dfc33241723e..f9596c2234069533ab978ac3405f80f98ecb8332 100644 --- a/lib/browserid/primary.js +++ b/lib/browserid/primary.js @@ -42,7 +42,10 @@ const https = require('https'), logger = require('../logging.js').logger, urlparse = require('urlparse'), -jwk = require('jwcrypto/jwk'); +jwk = require('jwcrypto/jwk'), +jwcert = require("jwcrypto/jwcert"), +vep = require("jwcrypto/vep"), +jwt = require("jwcrypto/jwt"); const WELL_KNOWN_URL = "/.well-known/vep"; @@ -170,4 +173,44 @@ if (process.env['SHIMMED_PRIMARIES']) { }); } +// verify an assertion generated to authenticate to browserid +exports.verifyAssertion = function(assertion, cb) { + try { + var bundle = vep.unbundleCertsAndAssertion(assertion); + } catch(e) { + return process.nextTick(function() { cb("malformed assertion: " + e); }); + } + jwcert.JWCert.verifyChain( + bundle.certificates, + new Date(), function(issuer, next) { + // issuer cannot be the browserid + if (issuer === config.get('hostname')) { + cb("cannot authenticate to browserid with a certificate issued by it."); + } else { + exports.checkSupport(issuer, function(err, rv) { + if (err) return cb(err); + var pubKey = g_cache[issuer].publicKey; + if (!pubKey) return cb("can't get public key for " + issuer); + next(pubKey); + }); + } + }, function(pk, principal) { + try { + var tok = new jwt.JWT(); + tok.parse(bundle.assertion); + + // audience must be browserid itself + var want = urlparse(config.get('URL')).originOnly(); + var got = urlparse(tok.audience).originOnly(); + + if (want.toString() !== got.toString()) { + return cb("can't log in with an assertion for '" + got.toString() + "'"); + } + if (!tok.verify(pk)) throw "verification failure"; + cb(null, principal.email); + } catch(e) { + cb("can't verify assertion: " + e.toString()); + } + }, cb); +}; \ No newline at end of file