UNPKG

@financialforcedev/orizuru-auth

Version:

Salesforce authentication for Heroku Node applications.

106 lines 13.5 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/oauth2Jwt/jwt */ const jsonwebtoken_1 = require("jsonwebtoken"); const uuid_1 = require("uuid"); /** * The assertion types that can be created. */ var AssertionType; (function (AssertionType) { AssertionType["CLIENT"] = "client"; AssertionType["GRANT"] = "grant"; })(AssertionType || (AssertionType = {})); /** * Creates a [private_key_jwt](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) assertion for use in the authentication process. * * @param params The parameters required for either the [auth](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) or [refresh](https://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken) grant requests. * @param signingSecret The secret used to sign the payload. * @param tokenEndpoint The [OpenID token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint). */ async function createJwtBearerClientAssertion(params, signingSecret, tokenEndpoint) { const payload = createPayload(tokenEndpoint, params.clientId, params.clientId); return signClientAssertion(payload, signingSecret, AssertionType.CLIENT); } exports.createJwtBearerClientAssertion = createJwtBearerClientAssertion; /** * Creates a [private_key_jwt](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) assertion for use in the authentication process. * * Unlike the standard client assertion, this assertion provides the username as the * subject as defined by [Salesforce](https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm#create_token). * * @param env The auth environment parameters. * @param params The parameters required for the [JWT Bearer](https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm) grant request. * @param user The user to generate the assertion for. */ async function createJwtBearerGrantAssertion(env, params) { const payload = createPayload(env.issuerURI, params.clientId, params.user.username); return signClientAssertion(payload, params.signingSecret, AssertionType.GRANT); } exports.createJwtBearerGrantAssertion = createJwtBearerGrantAssertion; /** * Create the JWT payload. * * @param aud Audience(s) that this ID Token is intended for. * @param iss Issuer. * @param sub Subject identifier. */ function createPayload(aud, iss, sub) { const iat = Math.floor(Date.now() / 1000); const exp = iat + (60 * 4); return { aud, exp, iat, iss, jti: uuid_1.v4().toString(), sub }; } /** * Sign the payload using the given private key. * * @param payload The payload to sign. * @param signingSecret The private key to sign the payload with. * @param type The assertion type (either client or grant). */ function signClientAssertion(payload, signingSecret, type) { return new Promise((resolve, reject) => { jsonwebtoken_1.sign(payload, signingSecret, { algorithm: 'RS256' }, (err, encoded) => { if (err) { reject(new Error(`Failed to sign ${type} assertion.`)); } else { resolve(encoded); } }); }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiand0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2luZGV4L2NsaWVudC9vYXV0aDJKd3Qvand0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHOztBQUVIOztHQUVHO0FBRUgsK0NBQTRDO0FBQzVDLCtCQUFrQztBQU1sQzs7R0FFRztBQUNILElBQUssYUFHSjtBQUhELFdBQUssYUFBYTtJQUNqQixrQ0FBaUIsQ0FBQTtJQUNqQixnQ0FBZSxDQUFBO0FBQ2hCLENBQUMsRUFISSxhQUFhLEtBQWIsYUFBYSxRQUdqQjtBQUVEOzs7Ozs7R0FNRztBQUNJLEtBQUssVUFBVSw4QkFBOEIsQ0FBQyxNQUFtQixFQUFFLGFBQXFCLEVBQUUsYUFBcUI7SUFDckgsTUFBTSxPQUFPLEdBQUcsYUFBYSxDQUFDLGFBQWEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMvRSxPQUFPLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxhQUFhLEVBQUUsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBRTFFLENBQUM7QUFKRCx3RUFJQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNJLEtBQUssVUFBVSw2QkFBNkIsQ0FBQyxHQUFnQixFQUFFLE1BQXNCO0lBQzNGLE1BQU0sT0FBTyxHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNwRixPQUFPLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsYUFBYSxFQUFFLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUNoRixDQUFDO0FBSEQsc0VBR0M7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFTLGFBQWEsQ0FBQyxHQUFXLEVBQUUsR0FBVyxFQUFFLEdBQVc7SUFFM0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDMUMsTUFBTSxHQUFHLEdBQUcsR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBRTNCLE9BQU87UUFDTixHQUFHO1FBQ0gsR0FBRztRQUNILEdBQUc7UUFDSCxHQUFHO1FBQ0gsR0FBRyxFQUFFLFNBQUksRUFBRSxDQUFDLFFBQVEsRUFBRTtRQUN0QixHQUFHO0tBQ0gsQ0FBQztBQUVILENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFTLG1CQUFtQixDQUFDLE9BQVksRUFBRSxhQUFxQixFQUFFLElBQW1CO0lBRXBGLE9BQU8sSUFBSSxPQUFPLENBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFFOUMsbUJBQUksQ0FBQyxPQUFPLEVBQUUsYUFBYSxFQUFFLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxFQUFFO1lBQ3JFLElBQUksR0FBRyxFQUFFO2dCQUNSLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSxhQUFhLENBQUMsQ0FBQyxDQUFDO2FBQ3ZEO2lCQUFNO2dCQUNOLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQzthQUNqQjtRQUNGLENBQUMsQ0FBQyxDQUFDO0lBRUosQ0FBQyxDQUFDLENBQUM7QUFFSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCAoYykgMjAxOSwgRmluYW5jaWFsRm9yY2UuY29tLCBpbmNcbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0IG1vZGlmaWNhdGlvbixcbiAqICAgYXJlIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucyBhcmUgbWV0OlxuICpcbiAqIC0gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLFxuICogICAgICB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyLlxuICogLSBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsXG4gKiAgICAgIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlIGRvY3VtZW50YXRpb25cbiAqICAgICAgYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uXG4gKiAtIE5laXRoZXIgdGhlIG5hbWUgb2YgdGhlIEZpbmFuY2lhbEZvcmNlLmNvbSwgaW5jIG5vciB0aGUgbmFtZXMgb2YgaXRzIGNvbnRyaWJ1dG9yc1xuICogICAgICBtYXkgYmUgdXNlZCB0byBlbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmUgd2l0aG91dFxuICogICAgICBzcGVjaWZpYyBwcmlvciB3cml0dGVuIHBlcm1pc3Npb24uXG4gKlxuICogVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUyBcIkFTIElTXCIgQU5EXG4gKiAgQU5ZIEVYUFJFU1MgT1IgSU1QTElFRCBXQVJSQU5USUVTLCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgVEhFIElNUExJRUQgV0FSUkFOVElFU1xuICogIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMXG4gKiAgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1IgQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsXG4gKiAgRVhFTVBMQVJZLCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgKElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBQUk9DVVJFTUVOVCBPRiBTVUJTVElUVVRFIEdPT0RTXG4gKiAgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIgQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZXG4gKiAgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpXG4gKiAgQVJJU0lORyBJTiBBTlkgV0FZIE9VVCBPRiBUSEUgVVNFIE9GIFRISVMgU09GVFdBUkUsIEVWRU4gSUYgQURWSVNFRCBPRiBUSEUgUE9TU0lCSUxJVFkgT0YgU1VDSCBEQU1BR0UuXG4gKi9cblxuLyoqXG4gKiBAbW9kdWxlIGNsaWVudC9vYXV0aDJKd3Qvand0XG4gKi9cblxuaW1wb3J0IHsgU2VjcmV0LCBzaWduIH0gZnJvbSAnanNvbndlYnRva2VuJztcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcblxuaW1wb3J0IHsgRW52aXJvbm1lbnQgfSBmcm9tICcuLi9jYWNoZSc7XG5pbXBvcnQgeyBHcmFudFBhcmFtcyB9IGZyb20gJy4uL29hdXRoMic7XG5pbXBvcnQgeyBKV1QsIEp3dEdyYW50UGFyYW1zIH0gZnJvbSAnLi4vb2F1dGgySnd0JztcblxuLyoqXG4gKiBUaGUgYXNzZXJ0aW9uIHR5cGVzIHRoYXQgY2FuIGJlIGNyZWF0ZWQuXG4gKi9cbmVudW0gQXNzZXJ0aW9uVHlwZSB7XG5cdENMSUVOVCA9ICdjbGllbnQnLFxuXHRHUkFOVCA9ICdncmFudCdcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgW3ByaXZhdGVfa2V5X2p3dF0oaHR0cHM6Ly9vcGVuaWQubmV0L3NwZWNzL29wZW5pZC1jb25uZWN0LWNvcmUtMV8wLmh0bWwjQ2xpZW50QXV0aGVudGljYXRpb24pIGFzc2VydGlvbiBmb3IgdXNlIGluIHRoZSBhdXRoZW50aWNhdGlvbiBwcm9jZXNzLlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgVGhlIHBhcmFtZXRlcnMgcmVxdWlyZWQgZm9yIGVpdGhlciB0aGUgW2F1dGhdKGh0dHBzOi8vb3BlbmlkLm5ldC9zcGVjcy9vcGVuaWQtY29ubmVjdC1jb3JlLTFfMC5odG1sI0F1dGhSZXF1ZXN0KSBvciBbcmVmcmVzaF0oaHR0cHM6Ly9vcGVuaWQubmV0L3NwZWNzL29wZW5pZC1jb25uZWN0LWNvcmUtMV8wLmh0bWwjUmVmcmVzaGluZ0FjY2Vzc1Rva2VuKSBncmFudCByZXF1ZXN0cy5cbiAqIEBwYXJhbSBzaWduaW5nU2VjcmV0IFRoZSBzZWNyZXQgdXNlZCB0byBzaWduIHRoZSBwYXlsb2FkLlxuICogQHBhcmFtIHRva2VuRW5kcG9pbnQgVGhlIFtPcGVuSUQgdG9rZW4gZW5kcG9pbnRdKGh0dHBzOi8vb3BlbmlkLm5ldC9zcGVjcy9vcGVuaWQtY29ubmVjdC1jb3JlLTFfMC5odG1sI1Rva2VuRW5kcG9pbnQpLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlSnd0QmVhcmVyQ2xpZW50QXNzZXJ0aW9uKHBhcmFtczogR3JhbnRQYXJhbXMsIHNpZ25pbmdTZWNyZXQ6IFNlY3JldCwgdG9rZW5FbmRwb2ludDogc3RyaW5nKSB7XG5cdGNvbnN0IHBheWxvYWQgPSBjcmVhdGVQYXlsb2FkKHRva2VuRW5kcG9pbnQsIHBhcmFtcy5jbGllbnRJZCwgcGFyYW1zLmNsaWVudElkKTtcblx0cmV0dXJuIHNpZ25DbGllbnRBc3NlcnRpb24ocGF5bG9hZCwgc2lnbmluZ1NlY3JldCwgQXNzZXJ0aW9uVHlwZS5DTElFTlQpO1xuXG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIFtwcml2YXRlX2tleV9qd3RdKGh0dHBzOi8vb3BlbmlkLm5ldC9zcGVjcy9vcGVuaWQtY29ubmVjdC1jb3JlLTFfMC5odG1sI0NsaWVudEF1dGhlbnRpY2F0aW9uKSBhc3NlcnRpb24gZm9yIHVzZSBpbiB0aGUgYXV0aGVudGljYXRpb24gcHJvY2Vzcy5cbiAqXG4gKiBVbmxpa2UgdGhlIHN0YW5kYXJkIGNsaWVudCBhc3NlcnRpb24sIHRoaXMgYXNzZXJ0aW9uIHByb3ZpZGVzIHRoZSB1c2VybmFtZSBhcyB0aGVcbiAqIHN1YmplY3QgYXMgZGVmaW5lZCBieSBbU2FsZXNmb3JjZV0oaHR0cHM6Ly9oZWxwLnNhbGVzZm9yY2UuY29tL2FydGljbGVWaWV3P2lkPXJlbW90ZWFjY2Vzc19vYXV0aF9qd3RfZmxvdy5odG0jY3JlYXRlX3Rva2VuKS5cbiAqXG4gKiBAcGFyYW0gZW52IFRoZSBhdXRoIGVudmlyb25tZW50IHBhcmFtZXRlcnMuXG4gKiBAcGFyYW0gcGFyYW1zIFRoZSBwYXJhbWV0ZXJzIHJlcXVpcmVkIGZvciB0aGUgW0pXVCBCZWFyZXJdKGh0dHBzOi8vaGVscC5zYWxlc2ZvcmNlLmNvbS9hcnRpY2xlVmlldz9pZD1yZW1vdGVhY2Nlc3Nfb2F1dGhfand0X2Zsb3cuaHRtKSBncmFudCByZXF1ZXN0LlxuICogQHBhcmFtIHVzZXIgVGhlIHVzZXIgdG8gZ2VuZXJhdGUgdGhlIGFzc2VydGlvbiBmb3IuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVKd3RCZWFyZXJHcmFudEFzc2VydGlvbihlbnY6IEVudmlyb25tZW50LCBwYXJhbXM6IEp3dEdyYW50UGFyYW1zKSB7XG5cdGNvbnN0IHBheWxvYWQgPSBjcmVhdGVQYXlsb2FkKGVudi5pc3N1ZXJVUkksIHBhcmFtcy5jbGllbnRJZCwgcGFyYW1zLnVzZXIudXNlcm5hbWUpO1xuXHRyZXR1cm4gc2lnbkNsaWVudEFzc2VydGlvbihwYXlsb2FkLCBwYXJhbXMuc2lnbmluZ1NlY3JldCwgQXNzZXJ0aW9uVHlwZS5HUkFOVCk7XG59XG5cbi8qKlxuICogQ3JlYXRlIHRoZSBKV1QgcGF5bG9hZC5cbiAqXG4gKiBAcGFyYW0gYXVkIEF1ZGllbmNlKHMpIHRoYXQgdGhpcyBJRCBUb2tlbiBpcyBpbnRlbmRlZCBmb3IuXG4gKiBAcGFyYW0gaXNzIElzc3Vlci5cbiAqIEBwYXJhbSBzdWIgU3ViamVjdCBpZGVudGlmaWVyLlxuICovXG5mdW5jdGlvbiBjcmVhdGVQYXlsb2FkKGF1ZDogc3RyaW5nLCBpc3M6IHN0cmluZywgc3ViOiBzdHJpbmcpOiBKV1Qge1xuXG5cdGNvbnN0IGlhdCA9IE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApO1xuXHRjb25zdCBleHAgPSBpYXQgKyAoNjAgKiA0KTtcblxuXHRyZXR1cm4ge1xuXHRcdGF1ZCxcblx0XHRleHAsXG5cdFx0aWF0LFxuXHRcdGlzcyxcblx0XHRqdGk6IHV1aWQoKS50b1N0cmluZygpLFxuXHRcdHN1YlxuXHR9O1xuXG59XG5cbi8qKlxuICogU2lnbiB0aGUgcGF5bG9hZCB1c2luZyB0aGUgZ2l2ZW4gcHJpdmF0ZSBrZXkuXG4gKlxuICogQHBhcmFtIHBheWxvYWQgVGhlIHBheWxvYWQgdG8gc2lnbi5cbiAqIEBwYXJhbSBzaWduaW5nU2VjcmV0IFRoZSBwcml2YXRlIGtleSB0byBzaWduIHRoZSBwYXlsb2FkIHdpdGguXG4gKiBAcGFyYW0gdHlwZSBUaGUgYXNzZXJ0aW9uIHR5cGUgKGVpdGhlciBjbGllbnQgb3IgZ3JhbnQpLlxuICovXG5mdW5jdGlvbiBzaWduQ2xpZW50QXNzZXJ0aW9uKHBheWxvYWQ6IEpXVCwgc2lnbmluZ1NlY3JldDogU2VjcmV0LCB0eXBlOiBBc3NlcnRpb25UeXBlKSB7XG5cblx0cmV0dXJuIG5ldyBQcm9taXNlPHN0cmluZz4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXG5cdFx0c2lnbihwYXlsb2FkLCBzaWduaW5nU2VjcmV0LCB7IGFsZ29yaXRobTogJ1JTMjU2JyB9LCAoZXJyLCBlbmNvZGVkKSA9PiB7XG5cdFx0XHRpZiAoZXJyKSB7XG5cdFx0XHRcdHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBzaWduICR7dHlwZX0gYXNzZXJ0aW9uLmApKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJlc29sdmUoZW5jb2RlZCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cblx0fSk7XG5cbn1cbiJdfQ==