UNPKG

@financialforcedev/orizuru-auth

Version:

Salesforce authentication for Heroku Node applications.

116 lines 14.4 kB
"use strict"; /* * 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