@financialforcedev/orizuru-auth
Version:
Salesforce authentication for Heroku Node applications.
116 lines • 14.4 kB
JavaScript
;
/*
* Copyright (c) 2019, FinancialForce.com, inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the FinancialForce.com, inc nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @module client/openid/identity
*/
const jsonwebtoken_1 = require("jsonwebtoken");
/**
* Decode the `id_token` from an access token response.
*
* This does not verify the token.
*
* @param accessTokenResponse The access token response.
*/
function decodeIdToken(accessTokenResponse) {
if (accessTokenResponse.id_token) {
if (typeof accessTokenResponse.id_token === 'string') {
accessTokenResponse.id_token = jsonwebtoken_1.decode(accessTokenResponse.id_token);
}
}
else if (accessTokenResponse.scope) {
const scopes = accessTokenResponse.scope.split(' ');
if (scopes.includes('openid')) {
throw new Error('No id_token present');
}
}
else {
throw new Error('No id_token present');
}
}
exports.decodeIdToken = decodeIdToken;
/**
* Verify the `id_token` from an access token response.
*
* @param accessTokenResponse The access token response.
* @param [jwkPemFormatMap] Map of the retrieved JSON Web Keys, keyed by the Key ID Parameter, in PEM format.
*/
function verifyIdToken(accessTokenResponse, jwkPemFormatMap) {
if (!jwkPemFormatMap) {
throw new Error('Unable to verify ID token: No JWKs provided');
}
if (accessTokenResponse.id_token) {
if (typeof accessTokenResponse.id_token !== 'string') {
throw new Error('Unable to verify ID token: id_token is not a string');
}
const decodedIdToken = jsonwebtoken_1.decode(accessTokenResponse.id_token, {
complete: true
});
if (!decodedIdToken || typeof decodedIdToken !== 'object') {
throw new Error('Unable to verify ID token: decoded token is not an object');
}
if (!decodedIdToken.header) {
throw new Error('Unable to verify ID token: decoded token does not contain the header');
}
if (!decodedIdToken.header.kid) {
throw new Error('Unable to verify ID token: decoded token header does not contain the kid');
}
const publicKey = jwkPemFormatMap[decodedIdToken.header.kid];
jsonwebtoken_1.verify(accessTokenResponse.id_token, publicKey);
}
else if (accessTokenResponse.scope) {
const scopes = accessTokenResponse.scope.split(' ');
if (scopes.includes('openid')) {
throw new Error('No id_token present');
}
}
else {
throw new Error('No id_token present');
}
}
exports.verifyIdToken = verifyIdToken;
/**
* Determines whether the id token is an OpenID token with standard claims.
*
* @param token The id token.
* @returns A boolean indicating if this id token is an OpenID token with standard claims.
*/
function isOpenIdTokenWithStandardClaims(token) {
return token.email !== undefined;
}
exports.isOpenIdTokenWithStandardClaims = isOpenIdTokenWithStandardClaims;
/**
* Determines whether the token is a Salesforce access token response.
*
* @param token The access token response.
* @returns A boolean indicating if this token is a Salesforce access token response.
*/
function isOpenIdAccessTokenResponse(token) {
return token.id_token !== undefined;
}
exports.isOpenIdAccessTokenResponse = isOpenIdAccessTokenResponse;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWRlbnRpdHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvaW5kZXgvY2xpZW50L29wZW5pZC9pZGVudGl0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRzs7QUFFSDs7R0FFRztBQUVILCtDQUE4QztBQU05Qzs7Ozs7O0dBTUc7QUFDSCxTQUFnQixhQUFhLENBQUMsbUJBQThDO0lBRTNFLElBQUksbUJBQW1CLENBQUMsUUFBUSxFQUFFO1FBRWpDLElBQUksT0FBTyxtQkFBbUIsQ0FBQyxRQUFRLEtBQUssUUFBUSxFQUFFO1lBQ3JELG1CQUFtQixDQUFDLFFBQVEsR0FBRyxxQkFBTSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBZ0QsQ0FBQztTQUNuSDtLQUVEO1NBQU0sSUFBSSxtQkFBbUIsQ0FBQyxLQUFLLEVBQUU7UUFFckMsTUFBTSxNQUFNLEdBQUcsbUJBQW1CLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNwRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUU7WUFDOUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1NBQ3ZDO0tBRUQ7U0FBTTtRQUNOLE1BQU0sSUFBSSxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQztLQUN2QztBQUVGLENBQUM7QUFuQkQsc0NBbUJDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFnQixhQUFhLENBQUMsbUJBQThDLEVBQUUsZUFBd0M7SUFFckgsSUFBSSxDQUFDLGVBQWUsRUFBRTtRQUNyQixNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7S0FDL0Q7SUFFRCxJQUFJLG1CQUFtQixDQUFDLFFBQVEsRUFBRTtRQUVqQyxJQUFJLE9BQU8sbUJBQW1CLENBQUMsUUFBUSxLQUFLLFFBQVEsRUFBRTtZQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLHFEQUFxRCxDQUFDLENBQUM7U0FDdkU7UUFFRCxNQUFNLGNBQWMsR0FBRyxxQkFBTSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsRUFBRTtZQUMzRCxRQUFRLEVBQUUsSUFBSTtTQUNkLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxjQUFjLElBQUksT0FBTyxjQUFjLEtBQUssUUFBUSxFQUFFO1lBQzFELE1BQU0sSUFBSSxLQUFLLENBQUMsMkRBQTJELENBQUMsQ0FBQztTQUM3RTtRQUVELElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFO1lBQzNCLE1BQU0sSUFBSSxLQUFLLENBQUMsc0VBQXNFLENBQUMsQ0FBQztTQUN4RjtRQUVELElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRTtZQUMvQixNQUFNLElBQUksS0FBSyxDQUFDLDBFQUEwRSxDQUFDLENBQUM7U0FDNUY7UUFFRCxNQUFNLFNBQVMsR0FBRyxlQUFlLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM3RCxxQkFBTSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQztLQUVoRDtTQUFNLElBQUksbUJBQW1CLENBQUMsS0FBSyxFQUFFO1FBRXJDLE1BQU0sTUFBTSxHQUFHLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDcEQsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQzlCLE1BQU0sSUFBSSxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQztTQUN2QztLQUVEO1NBQU07UUFDTixNQUFNLElBQUksS0FBSyxDQUFDLHFCQUFxQixDQUFDLENBQUM7S0FDdkM7QUFFRixDQUFDO0FBMUNELHNDQTBDQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBZ0IsK0JBQStCLENBQUMsS0FBbUU7SUFDbEgsT0FBUSxLQUF1QyxDQUFDLEtBQUssS0FBSyxTQUFTLENBQUM7QUFDckUsQ0FBQztBQUZELDBFQUVDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFnQiwyQkFBMkIsQ0FBQyxLQUFzRDtJQUNqRyxPQUFRLEtBQW1DLENBQUMsUUFBUSxLQUFLLFNBQVMsQ0FBQztBQUNwRSxDQUFDO0FBRkQsa0VBRUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29weXJpZ2h0IChjKSAyMDE5LCBGaW5hbmNpYWxGb3JjZS5jb20sIGluY1xuICogQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqXG4gKiBSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQgbW9kaWZpY2F0aW9uLFxuICogICBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zIGFyZSBtZXQ6XG4gKlxuICogLSBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsXG4gKiAgICAgIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuXG4gKiAtIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSxcbiAqICAgICAgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUgZG9jdW1lbnRhdGlvblxuICogICAgICBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi5cbiAqIC0gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgRmluYW5jaWFsRm9yY2UuY29tLCBpbmMgbm9yIHRoZSBuYW1lcyBvZiBpdHMgY29udHJpYnV0b3JzXG4gKiAgICAgIG1heSBiZSB1c2VkIHRvIGVuZG9yc2Ugb3IgcHJvbW90ZSBwcm9kdWN0cyBkZXJpdmVkIGZyb20gdGhpcyBzb2Z0d2FyZSB3aXRob3V0XG4gKiAgICAgIHNwZWNpZmljIHByaW9yIHdyaXR0ZW4gcGVybWlzc2lvbi5cbiAqXG4gKiBUSElTIFNPRlRXQVJFIElTIFBST1ZJREVEIEJZIFRIRSBDT1BZUklHSFQgSE9MREVSUyBBTkQgQ09OVFJJQlVUT1JTIFwiQVMgSVNcIiBBTkRcbiAqICBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTXG4gKiAgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTExcbiAqICBUSEUgQ09QWVJJR0hUIEhPTERFUiBPUiBDT05UUklCVVRPUlMgQkUgTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5DSURFTlRBTCwgU1BFQ0lBTCxcbiAqICBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFNcbiAqICBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OIEFOWSBUSEVPUllcbiAqICBPRiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQ09OVFJBQ1QsIFNUUklDVCBMSUFCSUxJVFksIE9SIFRPUlQgKElOQ0xVRElORyBORUdMSUdFTkNFIE9SIE9USEVSV0lTRSlcbiAqICBBUklTSU5HIElOIEFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRSBQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS5cbiAqL1xuXG4vKipcbiAqIEBtb2R1bGUgY2xpZW50L29wZW5pZC9pZGVudGl0eVxuICovXG5cbmltcG9ydCB7IGRlY29kZSwgdmVyaWZ5IH0gZnJvbSAnanNvbndlYnRva2VuJztcblxuaW1wb3J0IHsgQWNjZXNzVG9rZW5SZXNwb25zZSB9IGZyb20gJy4uL29hdXRoMic7XG5pbXBvcnQgeyBPcGVuSURBY2Nlc3NUb2tlblJlc3BvbnNlLCBPcGVuSURUb2tlbiwgT3BlbklEVG9rZW5XaXRoU3RhbmRhcmRDbGFpbXMgfSBmcm9tICcuLi9vcGVuaWQnO1xuaW1wb3J0IHsgSnNvbldlYktleVBlbUZvcm1hdE1hcCB9IGZyb20gJy4vandrJztcblxuLyoqXG4gKiBEZWNvZGUgdGhlIGBpZF90b2tlbmAgZnJvbSBhbiBhY2Nlc3MgdG9rZW4gcmVzcG9uc2UuXG4gKlxuICogVGhpcyBkb2VzIG5vdCB2ZXJpZnkgdGhlIHRva2VuLlxuICpcbiAqIEBwYXJhbSBhY2Nlc3NUb2tlblJlc3BvbnNlIFRoZSBhY2Nlc3MgdG9rZW4gcmVzcG9uc2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVJZFRva2VuKGFjY2Vzc1Rva2VuUmVzcG9uc2U6IE9wZW5JREFjY2Vzc1Rva2VuUmVzcG9uc2UpIHtcblxuXHRpZiAoYWNjZXNzVG9rZW5SZXNwb25zZS5pZF90b2tlbikge1xuXG5cdFx0aWYgKHR5cGVvZiBhY2Nlc3NUb2tlblJlc3BvbnNlLmlkX3Rva2VuID09PSAnc3RyaW5nJykge1xuXHRcdFx0YWNjZXNzVG9rZW5SZXNwb25zZS5pZF90b2tlbiA9IGRlY29kZShhY2Nlc3NUb2tlblJlc3BvbnNlLmlkX3Rva2VuKSBhcyBPcGVuSURUb2tlbiB8IE9wZW5JRFRva2VuV2l0aFN0YW5kYXJkQ2xhaW1zO1xuXHRcdH1cblxuXHR9IGVsc2UgaWYgKGFjY2Vzc1Rva2VuUmVzcG9uc2Uuc2NvcGUpIHtcblxuXHRcdGNvbnN0IHNjb3BlcyA9IGFjY2Vzc1Rva2VuUmVzcG9uc2Uuc2NvcGUuc3BsaXQoJyAnKTtcblx0XHRpZiAoc2NvcGVzLmluY2x1ZGVzKCdvcGVuaWQnKSkge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdObyBpZF90b2tlbiBwcmVzZW50Jyk7XG5cdFx0fVxuXG5cdH0gZWxzZSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKCdObyBpZF90b2tlbiBwcmVzZW50Jyk7XG5cdH1cblxufVxuXG4vKipcbiAqIFZlcmlmeSB0aGUgYGlkX3Rva2VuYCBmcm9tIGFuIGFjY2VzcyB0b2tlbiByZXNwb25zZS5cbiAqXG4gKiBAcGFyYW0gYWNjZXNzVG9rZW5SZXNwb25zZSBUaGUgYWNjZXNzIHRva2VuIHJlc3BvbnNlLlxuICogQHBhcmFtIFtqd2tQZW1Gb3JtYXRNYXBdIE1hcCBvZiB0aGUgcmV0cmlldmVkIEpTT04gV2ViIEtleXMsIGtleWVkIGJ5IHRoZSBLZXkgSUQgUGFyYW1ldGVyLCBpbiBQRU0gZm9ybWF0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmVyaWZ5SWRUb2tlbihhY2Nlc3NUb2tlblJlc3BvbnNlOiBPcGVuSURBY2Nlc3NUb2tlblJlc3BvbnNlLCBqd2tQZW1Gb3JtYXRNYXA/OiBKc29uV2ViS2V5UGVtRm9ybWF0TWFwKSB7XG5cblx0aWYgKCFqd2tQZW1Gb3JtYXRNYXApIHtcblx0XHR0aHJvdyBuZXcgRXJyb3IoJ1VuYWJsZSB0byB2ZXJpZnkgSUQgdG9rZW46IE5vIEpXS3MgcHJvdmlkZWQnKTtcblx0fVxuXG5cdGlmIChhY2Nlc3NUb2tlblJlc3BvbnNlLmlkX3Rva2VuKSB7XG5cblx0XHRpZiAodHlwZW9mIGFjY2Vzc1Rva2VuUmVzcG9uc2UuaWRfdG9rZW4gIT09ICdzdHJpbmcnKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1VuYWJsZSB0byB2ZXJpZnkgSUQgdG9rZW46IGlkX3Rva2VuIGlzIG5vdCBhIHN0cmluZycpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGRlY29kZWRJZFRva2VuID0gZGVjb2RlKGFjY2Vzc1Rva2VuUmVzcG9uc2UuaWRfdG9rZW4sIHtcblx0XHRcdGNvbXBsZXRlOiB0cnVlXG5cdFx0fSk7XG5cblx0XHRpZiAoIWRlY29kZWRJZFRva2VuIHx8IHR5cGVvZiBkZWNvZGVkSWRUb2tlbiAhPT0gJ29iamVjdCcpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIHZlcmlmeSBJRCB0b2tlbjogZGVjb2RlZCB0b2tlbiBpcyBub3QgYW4gb2JqZWN0Jyk7XG5cdFx0fVxuXG5cdFx0aWYgKCFkZWNvZGVkSWRUb2tlbi5oZWFkZXIpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIHZlcmlmeSBJRCB0b2tlbjogZGVjb2RlZCB0b2tlbiBkb2VzIG5vdCBjb250YWluIHRoZSBoZWFkZXInKTtcblx0XHR9XG5cblx0XHRpZiAoIWRlY29kZWRJZFRva2VuLmhlYWRlci5raWQpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIHZlcmlmeSBJRCB0b2tlbjogZGVjb2RlZCB0b2tlbiBoZWFkZXIgZG9lcyBub3QgY29udGFpbiB0aGUga2lkJyk7XG5cdFx0fVxuXG5cdFx0Y29uc3QgcHVibGljS2V5ID0gandrUGVtRm9ybWF0TWFwW2RlY29kZWRJZFRva2VuLmhlYWRlci5raWRdO1xuXHRcdHZlcmlmeShhY2Nlc3NUb2tlblJlc3BvbnNlLmlkX3Rva2VuLCBwdWJsaWNLZXkpO1xuXG5cdH0gZWxzZSBpZiAoYWNjZXNzVG9rZW5SZXNwb25zZS5zY29wZSkge1xuXG5cdFx0Y29uc3Qgc2NvcGVzID0gYWNjZXNzVG9rZW5SZXNwb25zZS5zY29wZS5zcGxpdCgnICcpO1xuXHRcdGlmIChzY29wZXMuaW5jbHVkZXMoJ29wZW5pZCcpKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ05vIGlkX3Rva2VuIHByZXNlbnQnKTtcblx0XHR9XG5cblx0fSBlbHNlIHtcblx0XHR0aHJvdyBuZXcgRXJyb3IoJ05vIGlkX3Rva2VuIHByZXNlbnQnKTtcblx0fVxuXG59XG5cbi8qKlxuICogRGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBpZCB0b2tlbiBpcyBhbiBPcGVuSUQgdG9rZW4gd2l0aCBzdGFuZGFyZCBjbGFpbXMuXG4gKlxuICogQHBhcmFtIHRva2VuIFRoZSBpZCB0b2tlbi5cbiAqIEByZXR1cm5zIEEgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIHRoaXMgaWQgdG9rZW4gaXMgYW4gT3BlbklEIHRva2VuIHdpdGggc3RhbmRhcmQgY2xhaW1zLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNPcGVuSWRUb2tlbldpdGhTdGFuZGFyZENsYWltcyh0b2tlbj86IHN0cmluZyB8IE9wZW5JRFRva2VuIHwgT3BlbklEVG9rZW5XaXRoU3RhbmRhcmRDbGFpbXMgfCBudWxsKTogdG9rZW4gaXMgT3BlbklEVG9rZW5XaXRoU3RhbmRhcmRDbGFpbXMge1xuXHRyZXR1cm4gKHRva2VuIGFzIE9wZW5JRFRva2VuV2l0aFN0YW5kYXJkQ2xhaW1zKS5lbWFpbCAhPT0gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIERldGVybWluZXMgd2hldGhlciB0aGUgdG9rZW4gaXMgYSBTYWxlc2ZvcmNlIGFjY2VzcyB0b2tlbiByZXNwb25zZS5cbiAqXG4gKiBAcGFyYW0gdG9rZW4gVGhlIGFjY2VzcyB0b2tlbiByZXNwb25zZS5cbiAqIEByZXR1cm5zIEEgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIHRoaXMgdG9rZW4gaXMgYSBTYWxlc2ZvcmNlIGFjY2VzcyB0b2tlbiByZXNwb25zZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzT3BlbklkQWNjZXNzVG9rZW5SZXNwb25zZSh0b2tlbjogQWNjZXNzVG9rZW5SZXNwb25zZSB8IE9wZW5JREFjY2Vzc1Rva2VuUmVzcG9uc2UpOiB0b2tlbiBpcyBPcGVuSURBY2Nlc3NUb2tlblJlc3BvbnNlIHtcblx0cmV0dXJuICh0b2tlbiBhcyBPcGVuSURBY2Nlc3NUb2tlblJlc3BvbnNlKS5pZF90b2tlbiAhPT0gdW5kZWZpbmVkO1xufVxuIl19