Allow IdPs to publish multiple public keys.
This allows the IdP support document to include a 'keys' property containing a list of public keys, any of which could be used to produce a valid signature. IdPs might like to use this during e.g. key rotation events.
This commit is contained in:
Родитель
0760ddcb03
Коммит
30dbded9c4
|
@ -4,8 +4,9 @@ env:
|
|||
global:
|
||||
- TMPDIR=/tmp
|
||||
|
||||
before_install:
|
||||
- sudo apt-get install libgmp3-dev
|
||||
addons:
|
||||
apt_packages:
|
||||
- libgmp-dev
|
||||
|
||||
node_js:
|
||||
- "0.10"
|
||||
|
|
|
@ -63,6 +63,9 @@ browserid.lookup(function(err, details) {
|
|||
if (argv.v) console.log("\n");
|
||||
// convert publicKey to displayable object
|
||||
details.publicKey = details.publicKey.toSimpleObject();
|
||||
for (var i=0; i<details.publicKeys.length; i++) {
|
||||
details.publicKeys[i] = details.publicKeys[i].toSimpleObject();
|
||||
}
|
||||
console.log(details.authoritativeDomain.info, "is authoritative for", '@' + principalDomain.info, "email addresses:", JSON.stringify(details, null, 2).data);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -235,7 +235,10 @@ function lookup(emitter, args, currentDomain, principalDomain, cb, delegationCha
|
|||
var url_prefix = 'https://' + currentDomain;
|
||||
|
||||
var details = {
|
||||
// For b/w compat export both a single "current key"
|
||||
// as well as a list of all acceptable keys.
|
||||
publicKey: supportDoc.publicKey,
|
||||
publicKeys: supportDoc.publicKeys,
|
||||
delegationChain: delegationChain,
|
||||
authoritativeDomain: delegationChain[delegationChain.length - 1],
|
||||
urls: {
|
||||
|
|
|
@ -134,7 +134,7 @@ function verify(browserid, args, cb) {
|
|||
dbug.error(err);
|
||||
return cb(err);
|
||||
}
|
||||
next(null, details.publicKey);
|
||||
next(null, details.publicKeys);
|
||||
});
|
||||
}, function(err, certParamsArray, payload, assertionParams) {
|
||||
if (err) {
|
||||
|
|
|
@ -19,7 +19,8 @@ function validateUrlPath(path) {
|
|||
// * if type is "delegation", also:
|
||||
// * authority - the domain authority is delegated to
|
||||
// * if type is "supported":
|
||||
// * publicKey - a parsed representation of the public key
|
||||
// * publicKeys - a parsed representation of its list of published public keys
|
||||
// * publicKey - a parsed representation of its current public key
|
||||
// * paths.authentication - the path to the 'authentication' html
|
||||
// * paths.provisioning - the path to the 'provisioning' html
|
||||
module.exports = function(doc) {
|
||||
|
@ -35,7 +36,7 @@ module.exports = function(doc) {
|
|||
|
||||
// there are three main types of support documents
|
||||
// 1. "supported" - declares the domain is a browserid authority,
|
||||
// contains public-key, authentication, and provisioning
|
||||
// contains public keys, authentication, and provisioning
|
||||
// 2. "delegation" - declares the domain allows a different domain
|
||||
// to be authoritative for it.
|
||||
// 3. "disable" - domain declares explicitly that it wants a secondary
|
||||
|
@ -71,6 +72,7 @@ module.exports = function(doc) {
|
|||
var parsed = {
|
||||
type: "supported",
|
||||
paths: {},
|
||||
publicKeys: [],
|
||||
publicKey: null
|
||||
};
|
||||
|
||||
|
@ -83,15 +85,41 @@ module.exports = function(doc) {
|
|||
}
|
||||
});
|
||||
|
||||
if (!doc['public-key']) {
|
||||
throw new Error("support document missing required 'public-key'");
|
||||
// For backwards-compat reasons, the support document can contain
|
||||
// one or both of:
|
||||
// * a single current public key in 'public-key'
|
||||
// * a list of acceptable public keys in 'keys'
|
||||
|
||||
if (doc['public-key']) {
|
||||
try {
|
||||
parsed.publicKey = jwcrypto.loadPublicKeyFromObject(doc['public-key']);
|
||||
} catch(e) {
|
||||
throw new Error("mal-formed public key in support doc: " + e.toString());
|
||||
}
|
||||
parsed.publicKeys.push(parsed.publicKey);
|
||||
}
|
||||
|
||||
// can we parse that key?
|
||||
try {
|
||||
parsed.publicKey = jwcrypto.loadPublicKeyFromObject(doc['public-key']);
|
||||
} catch(e) {
|
||||
throw new Error("mal-formed public key in support doc: " + e.toString());
|
||||
if (doc.keys) {
|
||||
if (!Array.isArray(doc.keys)) {
|
||||
throw new Error("mal-formed list of public keys in support doc");
|
||||
}
|
||||
doc.keys.forEach(function(key) {
|
||||
try {
|
||||
// Ensure only keys meant for signing are included in the list.
|
||||
if (!key.use || key.use === 'sig') {
|
||||
parsed.publicKeys.push(jwcrypto.loadPublicKeyFromObject(key));
|
||||
}
|
||||
} catch(e) {
|
||||
throw new Error("mal-formed public key in support doc: " + e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (parsed.publicKeys.length === 0) {
|
||||
throw new Error("support document missing required property 'keys' and/or 'public-key'");
|
||||
}
|
||||
if (!parsed.publicKey) {
|
||||
parsed.publicKey = parsed.publicKeys[0];
|
||||
}
|
||||
|
||||
// success!
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"main": "lib/browserid-local-verify",
|
||||
"dependencies": {
|
||||
"browserid-crypto": "0.7.0",
|
||||
"browserid-crypto": "git+https://github.com/mozilla/browserid-crypto#support-multiple-root-pubkeys",
|
||||
"async": "0.2.9",
|
||||
"dbug": "0.4.1",
|
||||
"urlparse": "0.0.1",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"jshint": "2.3.0",
|
||||
"walk": "2.2.1",
|
||||
"temp": "0.5.1",
|
||||
"ass": "0.0.4"
|
||||
"ass": "git://github.com/jrgm/ass.git#5be99ee7abc9fcf63f9ebcc37b151b9c822146d1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha -R spec tests/*.js"
|
||||
|
|
|
@ -18,7 +18,7 @@ describe('.well-known lookup, malformed', function() {
|
|||
|
||||
it('should handle bogus public key', function(done) {
|
||||
var x = idp.wellKnown();
|
||||
x['public-key'].n += "bogus";
|
||||
delete x['public-key'].n;
|
||||
idp.wellKnown(x);
|
||||
|
||||
browserid.lookup({ insecureSSL: true, domain: idp.domain() }, function(err) {
|
||||
|
@ -31,6 +31,55 @@ describe('.well-known lookup, malformed', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should handle missing public key', function(done) {
|
||||
var x = idp.wellKnown();
|
||||
delete x['public-key'];
|
||||
idp.wellKnown(x);
|
||||
|
||||
browserid.lookup({ insecureSSL: true, domain: idp.domain() }, function(err) {
|
||||
(err).should.contain("missing required property 'keys' and/or 'public-key'");
|
||||
|
||||
// repair well-known
|
||||
idp.wellKnown(null);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty list of public keys', function(done) {
|
||||
var x = idp.wellKnown();
|
||||
x.keys = [];
|
||||
delete x['public-key'];
|
||||
idp.wellKnown(x);
|
||||
|
||||
browserid.lookup({ insecureSSL: true, domain: idp.domain() }, function(err) {
|
||||
(err).should.contain("missing required property 'keys' and/or 'public-key'");
|
||||
|
||||
// repair well-known
|
||||
idp.wellKnown(null);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle list of public keys', function(done) {
|
||||
var x = idp.wellKnown();
|
||||
x.keys = [x['public-key']];
|
||||
delete x['public-key'];
|
||||
idp.wellKnown(x);
|
||||
|
||||
browserid.lookup({ insecureSSL: true, domain: idp.domain() }, function(err, details) {
|
||||
should.not.exist(err);
|
||||
(details.publicKeys.length).should.equal(1);
|
||||
details.publicKey.should.equal(details.publicKeys[0]);
|
||||
|
||||
// repair well-known
|
||||
idp.wellKnown(null);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle missing required fields', function(done) {
|
||||
var x = idp.wellKnown();
|
||||
delete x.provisioning;
|
||||
|
|
Загрузка…
Ссылка в новой задаче