diff --git a/common.js b/common.js index e95ea5b..eae7c05 100644 --- a/common.js +++ b/common.js @@ -40,5 +40,28 @@ function checkIssuerToken(req, expectedSub) { }); } +async function getPrimarySub(grantStore, secondary) { + var results = await Promise.all([ + grantStore.find({ sub: secondary }), + grantStore.find({ azpSub: secondary }), + ]); + + // Extract only the sub field from each row and reduce the duplicates so that each value + // can only appear in the final list once. + var subList = [].concat.apply([], results).map(grant => grant.sub); + subList = subList.filter((sub, ind, list) => list.indexOf(sub) === ind); + + if (subList.length > 1) { + // This should not ever happen since there is a check for PPID collisions when saving + // grants, but it's probably better to have this check anyway just incase something + // happens that isn't currently accounted for. + console.error('acounts ' + JSON.stringify(subList) + ' are all associated with "'+secondary+'"'); + throw new Error('PPID collision'); + } + + return subList[0]; +} + module.exports.checkIssuerToken = checkIssuerToken; +module.exports.getPrimarySub = getPrimarySub; module.exports.makeB64UrlSafe = makeB64UrlSafe; diff --git a/grants.js b/grants.js index dec2d1d..2fe161a 100644 --- a/grants.js +++ b/grants.js @@ -56,14 +56,16 @@ function create(app) { throw new OpErr("malformed request: 'sub' and 'scope' must be strings"); } - return req.Store.find({ azpSub: req.body.sub }); - }).then(function (existing) { - if (existing.length) { - if (existing.length > 1) { - throw new OpErr("pre-existing PPID collision detected"); - } else if (existing[0].sub !== req.params.sub || existing[0].azp !== req.params.azp) { - throw new OpErr("PPID collision detected, cannot save authorized party's sub"); + return require('./common').getPrimarySub(req.Store, req.body.sub).catch(function (err) { + if (/collision/.test(err.message)) { + err.message = 'pre-existing PPID collision detected'; } + throw err; + }); + }).then(function (primSub) { + if (primSub && primSub !== req.params.sub) { + console.log('account "'+req.params.sub+'" cannot use PPID "'+req.body.sub+'" already used by "'+primSub+'"'); + throw new OpErr("PPID collision detected, cannot save authorized party's sub"); } var grant = { diff --git a/jwks.js b/jwks.js index c769f09..0be4092 100644 --- a/jwks.js +++ b/jwks.js @@ -62,26 +62,21 @@ function create(app) { } // The keys are stored by the issuer PPID, but the sub we have might be a different PPID - // for a 3rd party. As such we need to look up the subject in the grants to get the issuer - // PPID and to make sure there wasn't some kind of error that allowed multiple users to - // somehow use the same PPID. - var results = await Promise.all([ - store.IssuerOauth3OrgGrants.find({ sub: req.params.sub }), - store.IssuerOauth3OrgGrants.find({ azpSub: req.params.sub }), - ]); - var subList = [].concat.apply([], results).map(grant => grant.sub); - subList = subList.filter((sub, ind, list) => list.indexOf(sub) === ind); - if (!subList.length) { - throw new OpErr("unknown PPID '" + req.params.sub + "'"); - } - if (subList.length > 1) { - // This should not ever happen since there is a check for PPID collisions when saving - // grants, but it's probably better to have this check anyway just incase something - // happens that isn't currently accounted for. - throw new OpErr('PPID collision - unable to safely retrieve keys'); + // for a 3rd party. + var issuerSub; + try { + issuerSub = await require('./common').getPrimarySub(store.IssuerOauth3OrgGrants, req.params.sub); + } catch (err) { + if (/collision/.test(err.message)) { + err.message = 'PPID collision - unable to safely retrieve keys'; + } + throw err; } - return store.IssuerOauth3OrgJwks.get(subList[0] + '/' + req.params.kid); + if (!issuerSub) { + throw new OpErr("unknown PPID '" + req.params.sub + "'"); + } + return store.IssuerOauth3OrgJwks.get(issuerSub + '/' + req.params.kid); } restful.get = function (req, res) {