UNPKG

@azure/msal-browser

Version:
254 lines (251 loc) 12.4 kB
/*! @azure/msal-browser v2.28.1 2022-08-01 */ 'use strict'; import { __awaiter, __generator } from '../_virtual/_tslib.js'; import { PerformanceEvents, JoseHeader } from '@azure/msal-common'; import { GuidGenerator } from './GuidGenerator.js'; import { Base64Encode } from '../encode/Base64Encode.js'; import { Base64Decode } from '../encode/Base64Decode.js'; import { PkceGenerator } from './PkceGenerator.js'; import { BrowserCrypto } from './BrowserCrypto.js'; import { BrowserStringUtils } from '../utils/BrowserStringUtils.js'; import { BrowserAuthError } from '../error/BrowserAuthError.js'; import { AsyncMemoryStorage } from '../cache/AsyncMemoryStorage.js'; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ var CryptoKeyStoreNames; (function (CryptoKeyStoreNames) { CryptoKeyStoreNames["asymmetricKeys"] = "asymmetricKeys"; CryptoKeyStoreNames["symmetricKeys"] = "symmetricKeys"; })(CryptoKeyStoreNames || (CryptoKeyStoreNames = {})); /** * This class implements MSAL's crypto interface, which allows it to perform base64 encoding and decoding, generating cryptographically random GUIDs and * implementing Proof Key for Code Exchange specs for the OAuth Authorization Code Flow using PKCE (rfc here: https://tools.ietf.org/html/rfc7636). */ var CryptoOps = /** @class */ (function () { function CryptoOps(logger, performanceClient) { this.logger = logger; // Browser crypto needs to be validated first before any other classes can be set. this.browserCrypto = new BrowserCrypto(this.logger); this.b64Encode = new Base64Encode(); this.b64Decode = new Base64Decode(); this.guidGenerator = new GuidGenerator(this.browserCrypto); this.pkceGenerator = new PkceGenerator(this.browserCrypto); this.cache = { asymmetricKeys: new AsyncMemoryStorage(this.logger, CryptoKeyStoreNames.asymmetricKeys), symmetricKeys: new AsyncMemoryStorage(this.logger, CryptoKeyStoreNames.symmetricKeys) }; this.performanceClient = performanceClient; } /** * Creates a new random GUID - used to populate state and nonce. * @returns string (GUID) */ CryptoOps.prototype.createNewGuid = function () { return this.guidGenerator.generateGuid(); }; /** * Encodes input string to base64. * @param input */ CryptoOps.prototype.base64Encode = function (input) { return this.b64Encode.encode(input); }; /** * Decodes input string from base64. * @param input */ CryptoOps.prototype.base64Decode = function (input) { return this.b64Decode.decode(input); }; /** * Generates PKCE codes used in Authorization Code Flow. */ CryptoOps.prototype.generatePkceCodes = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.pkceGenerator.generateCodes()]; }); }); }; /** * Generates a keypair, stores it and returns a thumbprint * @param request */ CryptoOps.prototype.getPublicKeyThumbprint = function (request) { var _a; return __awaiter(this, void 0, void 0, function () { var publicKeyThumbMeasurement, keyPair, publicKeyJwk, pubKeyThumprintObj, publicJwkString, publicJwkHash, privateKeyJwk, unextractablePrivateKey; return __generator(this, function (_b) { switch (_b.label) { case 0: publicKeyThumbMeasurement = (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.startMeasurement(PerformanceEvents.CryptoOptsGetPublicKeyThumbprint, request.correlationId); return [4 /*yield*/, this.browserCrypto.generateKeyPair(CryptoOps.EXTRACTABLE, CryptoOps.POP_KEY_USAGES)]; case 1: keyPair = _b.sent(); return [4 /*yield*/, this.browserCrypto.exportJwk(keyPair.publicKey)]; case 2: publicKeyJwk = _b.sent(); pubKeyThumprintObj = { e: publicKeyJwk.e, kty: publicKeyJwk.kty, n: publicKeyJwk.n }; publicJwkString = BrowserCrypto.getJwkString(pubKeyThumprintObj); return [4 /*yield*/, this.hashString(publicJwkString)]; case 3: publicJwkHash = _b.sent(); return [4 /*yield*/, this.browserCrypto.exportJwk(keyPair.privateKey)]; case 4: privateKeyJwk = _b.sent(); return [4 /*yield*/, this.browserCrypto.importJwk(privateKeyJwk, false, ["sign"])]; case 5: unextractablePrivateKey = _b.sent(); // Store Keypair data in keystore return [4 /*yield*/, this.cache.asymmetricKeys.setItem(publicJwkHash, { privateKey: unextractablePrivateKey, publicKey: keyPair.publicKey, requestMethod: request.resourceRequestMethod, requestUri: request.resourceRequestUri })]; case 6: // Store Keypair data in keystore _b.sent(); if (publicKeyThumbMeasurement) { publicKeyThumbMeasurement.endMeasurement({ success: true }); } return [2 /*return*/, publicJwkHash]; } }); }); }; /** * Removes cryptographic keypair from key store matching the keyId passed in * @param kid */ CryptoOps.prototype.removeTokenBindingKey = function (kid) { return __awaiter(this, void 0, void 0, function () { var keyFound; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.cache.asymmetricKeys.removeItem(kid)]; case 1: _a.sent(); return [4 /*yield*/, this.cache.asymmetricKeys.containsKey(kid)]; case 2: keyFound = _a.sent(); return [2 /*return*/, !keyFound]; } }); }); }; /** * Removes all cryptographic keys from IndexedDB storage */ CryptoOps.prototype.clearKeystore = function () { return __awaiter(this, void 0, void 0, function () { var e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); this.logger.verbose("Deleting in-memory and persistent asymmetric key stores"); return [4 /*yield*/, this.cache.asymmetricKeys.clear()]; case 1: _a.sent(); this.logger.verbose("Successfully deleted asymmetric key stores"); this.logger.verbose("Deleting in-memory and persistent symmetric key stores"); return [4 /*yield*/, this.cache.symmetricKeys.clear()]; case 2: _a.sent(); this.logger.verbose("Successfully deleted symmetric key stores"); return [2 /*return*/, true]; case 3: e_1 = _a.sent(); if (e_1 instanceof Error) { this.logger.error("Clearing keystore failed with error: " + e_1.message); } else { this.logger.error("Clearing keystore failed with unknown error"); } return [2 /*return*/, false]; case 4: return [2 /*return*/]; } }); }); }; /** * Signs the given object as a jwt payload with private key retrieved by given kid. * @param payload * @param kid */ CryptoOps.prototype.signJwt = function (payload, kid, correlationId) { var _a; return __awaiter(this, void 0, void 0, function () { var signJwtMeasurement, cachedKeyPair, publicKeyJwk, publicKeyJwkString, encodedKeyIdThumbprint, shrHeader, encodedShrHeader, encodedPayload, tokenString, tokenBuffer, signatureBuffer, encodedSignature, signedJwt; return __generator(this, function (_b) { switch (_b.label) { case 0: signJwtMeasurement = (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.startMeasurement(PerformanceEvents.CryptoOptsSignJwt, correlationId); return [4 /*yield*/, this.cache.asymmetricKeys.getItem(kid)]; case 1: cachedKeyPair = _b.sent(); if (!cachedKeyPair) { throw BrowserAuthError.createSigningKeyNotFoundInStorageError(kid); } return [4 /*yield*/, this.browserCrypto.exportJwk(cachedKeyPair.publicKey)]; case 2: publicKeyJwk = _b.sent(); publicKeyJwkString = BrowserCrypto.getJwkString(publicKeyJwk); encodedKeyIdThumbprint = this.b64Encode.urlEncode(JSON.stringify({ kid: kid })); shrHeader = JoseHeader.getShrHeaderString({ kid: encodedKeyIdThumbprint, alg: publicKeyJwk.alg }); encodedShrHeader = this.b64Encode.urlEncode(shrHeader); // Generate payload payload.cnf = { jwk: JSON.parse(publicKeyJwkString) }; encodedPayload = this.b64Encode.urlEncode(JSON.stringify(payload)); tokenString = encodedShrHeader + "." + encodedPayload; tokenBuffer = BrowserStringUtils.stringToArrayBuffer(tokenString); return [4 /*yield*/, this.browserCrypto.sign(cachedKeyPair.privateKey, tokenBuffer)]; case 3: signatureBuffer = _b.sent(); encodedSignature = this.b64Encode.urlEncodeArr(new Uint8Array(signatureBuffer)); signedJwt = tokenString + "." + encodedSignature; if (signJwtMeasurement) { signJwtMeasurement.endMeasurement({ success: true }); } return [2 /*return*/, signedJwt]; } }); }); }; /** * Returns the SHA-256 hash of an input string * @param plainText */ CryptoOps.prototype.hashString = function (plainText) { return __awaiter(this, void 0, void 0, function () { var hashBuffer, hashBytes; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.browserCrypto.sha256Digest(plainText)]; case 1: hashBuffer = _a.sent(); hashBytes = new Uint8Array(hashBuffer); return [2 /*return*/, this.b64Encode.urlEncodeArr(hashBytes)]; } }); }); }; CryptoOps.POP_KEY_USAGES = ["sign", "verify"]; CryptoOps.EXTRACTABLE = true; return CryptoOps; }()); export { CryptoKeyStoreNames, CryptoOps }; //# sourceMappingURL=CryptoOps.js.map