Skip to content

Commit

Permalink
refactor: remove client.unpackAggregatedClaims and client.fetchDistri…
Browse files Browse the repository at this point in the history
…butedClaims

BREAKING CHANGE: Client methods `unpackAggregatedClaims` and `fetchDistributedClaims`
were removed with no replacement.
  • Loading branch information
panva committed Oct 27, 2021
1 parent 6b8e8e0 commit b7f261f
Show file tree
Hide file tree
Showing 3 changed files with 2 additions and 686 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ openid-client.
- Implicit Flow
- Hybrid Flow
- UserInfo Request
- Fetching Distributed Claims
- Unpacking Aggregated Claims
- Offline Access / Refresh Token Grant
- Client Credentials Grant
- Client Authentication
Expand Down
145 changes: 1 addition & 144 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const request = require('./helpers/request');
const {
CALLBACK_PROPERTIES, CLIENT_DEFAULTS, JWT_CONTENT, CLOCK_TOLERANCE,
} = require('./helpers/consts');
const issuerRegistry = require('./issuer_registry');
const instance = require('./helpers/weak_cache');
const KeyStore = require('./helpers/keystore');
const { authenticatedPost, resolveResponseType, resolveRedirectUri } = require('./helpers/client');
Expand All @@ -48,28 +47,6 @@ function authorizationHeaderValue(token, tokenType = 'Bearer') {
return `${tokenType} ${token}`;
}

function cleanUpClaims(claims) {
if (Object.keys(claims._claim_names).length === 0) {
delete claims._claim_names;
}
if (Object.keys(claims._claim_sources).length === 0) {
delete claims._claim_sources;
}
}

function assignClaim(target, source, sourceName, throwOnMissing = true) {
return ([claim, inSource]) => {
if (inSource === sourceName) {
if (throwOnMissing && source[claim] === undefined) {
throw new RPError(`expected claim "${claim}" in "${sourceName}"`);
} else if (source[claim] !== undefined) {
target[claim] = source[claim];
}
delete target._claim_names[claim];
}
};
}

function verifyPresence(payload, jwt, prop) {
if (payload[prop] === undefined) {
throw new RPError({
Expand Down Expand Up @@ -103,41 +80,6 @@ function authorizationParams(params) {
return authParams;
}

async function claimJWT(label, jwt) {
try {
if (Buffer.isBuffer(jwt)) {
// eslint-disable-next-line no-param-reassign
jwt = jwt.toString('ascii');
}
const { header, payload } = decodeJWT(jwt, { complete: true });
const { iss } = payload;

if (header.alg === 'none') {
return payload;
}

let key;
if (!iss || iss === this.issuer.issuer) {
[{ keyObject: key }] = await this.issuer.queryKeyStore({ ...header, use: 'sig' });
} else if (issuerRegistry.has(iss)) {
[{ keyObject: key }] = await issuerRegistry.get(iss).queryKeyStore({ ...header, use: 'sig' });
} else {
const discovered = await this.issuer.constructor.discover(iss);
[{ keyObject: key }] = await discovered.queryKeyStore({ ...header, use: 'sig' });
}
return await jose.jwtVerify(jwt, key).then(((res) => res.payload));
} catch (err) {
if (err instanceof RPError || err instanceof OPError) {
throw err;
} else {
throw new RPError({
printf: ['failed to validate the %s JWT (%s: %s)', label, err.name, err.message],
jwt,
});
}
}
}

function getKeystore(jwks) {
if (!isPlainObject(jwks) || !Array.isArray(jwks.keys) || jwks.keys.some((k) => !isPlainObject(k) || !('kty' in k))) {
throw new TypeError('jwks must be a JSON Web Key Set formatted object');
Expand Down Expand Up @@ -304,6 +246,7 @@ module.exports = (issuer, aadIssValidation = false) => class Client extends Base
} = this.post_logout_redirect_uris || [];

const {
// eslint-disable-next-line camelcase
post_logout_redirect_uri = length === 1 ? postLogout : undefined,
} = params;

Expand Down Expand Up @@ -1354,92 +1297,6 @@ module.exports = (issuer, aadIssValidation = false) => class Client extends Base
return responseBody;
}

/**
* @name fetchDistributedClaims
* @api public
*/
async fetchDistributedClaims(claims, tokens = {}) {
if (!isPlainObject(claims)) {
throw new TypeError('claims argument must be a plain object');
}

if (!isPlainObject(claims._claim_sources)) {
return claims;
}

if (!isPlainObject(claims._claim_names)) {
return claims;
}

const distributedSources = Object.entries(claims._claim_sources)
.filter(([, value]) => value && value.endpoint);

await Promise.all(distributedSources.map(async ([sourceName, def]) => {
try {
const requestOpts = {
headers: {
Accept: 'application/jwt',
Authorization: authorizationHeaderValue(def.access_token || tokens[sourceName]),
},
};

const response = await request.call(this, {
...requestOpts,
method: 'GET',
url: def.endpoint,
});
const body = processResponse(response, { bearer: true });

const decoded = await claimJWT.call(this, 'distributed', body);
delete claims._claim_sources[sourceName];
Object.entries(claims._claim_names).forEach(
assignClaim(claims, decoded, sourceName, false),
);
} catch (err) {
err.src = sourceName;
throw err;
}
}));

cleanUpClaims(claims);
return claims;
}

/**
* @name unpackAggregatedClaims
* @api public
*/
async unpackAggregatedClaims(claims) {
if (!isPlainObject(claims)) {
throw new TypeError('claims argument must be a plain object');
}

if (!isPlainObject(claims._claim_sources)) {
return claims;
}

if (!isPlainObject(claims._claim_names)) {
return claims;
}

const aggregatedSources = Object.entries(claims._claim_sources)
.filter(([, value]) => value && value.JWT);

await Promise.all(aggregatedSources.map(async ([sourceName, def]) => {
try {
const decoded = await claimJWT.call(this, 'aggregated', def.JWT);
delete claims._claim_sources[sourceName];
Object.entries(claims._claim_names).forEach(assignClaim(claims, decoded, sourceName));
} catch (err) {
err.src = sourceName;
throw err;
}
}));

cleanUpClaims(claims);
return claims;
}

/**
* @name register
* @api public
Expand Down
Loading

0 comments on commit b7f261f

Please sign in to comment.