'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var msalCommon = require('@azure/msal-common');
var http = require('http');
var http__default = _interopDefault(http);
var https = _interopDefault(require('https'));
var uuid = require('uuid');
var crypto = _interopDefault(require('crypto'));
var jsonwebtoken = require('jsonwebtoken');

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

/**
 * http methods
 */
var HttpMethod;

(function (HttpMethod) {
  HttpMethod["GET"] = "get";
  HttpMethod["POST"] = "post";
})(HttpMethod || (HttpMethod = {}));

var HttpStatus;

(function (HttpStatus) {
  HttpStatus[HttpStatus["OK"] = 200] = "OK";
  HttpStatus[HttpStatus["REDIRECT"] = 302] = "REDIRECT";
})(HttpStatus || (HttpStatus = {}));
/**
 * Constants used for region discovery
 */


const REGION_ENVIRONMENT_VARIABLE = "REGION_NAME";
/**
 * Constant used for PKCE
 */

const RANDOM_OCTET_SIZE = 32;
/**
 * Constants used in PKCE
 */

const Hash = {
  SHA256: "sha256"
};
/**
 * Constants for encoding schemes
 */

const CharSet = {
  CV_CHARSET: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
};
/**
 * Constants
 */

const Constants = {
  MSAL_SKU: "msal.js.node",
  JWT_BEARER_ASSERTION_TYPE: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
  AUTHORIZATION_PENDING: "authorization_pending",
  HTTP_PROTOCOL: "http://",
  LOCALHOST: "localhost"
};
/**
 * API Codes for Telemetry purposes.
 * Before adding a new code you must claim it in the MSAL Telemetry tracker as these number spaces are shared across all MSALs
 * 0-99 Silent Flow
 * 600-699 Device Code Flow
 * 800-899 Auth Code Flow
 */

var ApiId;

(function (ApiId) {
  ApiId[ApiId["acquireTokenSilent"] = 62] = "acquireTokenSilent";
  ApiId[ApiId["acquireTokenByUsernamePassword"] = 371] = "acquireTokenByUsernamePassword";
  ApiId[ApiId["acquireTokenByDeviceCode"] = 671] = "acquireTokenByDeviceCode";
  ApiId[ApiId["acquireTokenByClientCredential"] = 771] = "acquireTokenByClientCredential";
  ApiId[ApiId["acquireTokenByCode"] = 871] = "acquireTokenByCode";
  ApiId[ApiId["acquireTokenByRefreshToken"] = 872] = "acquireTokenByRefreshToken";
})(ApiId || (ApiId = {}));
/**
 * JWT  constants
 */


const JwtConstants = {
  ALGORITHM: "alg",
  RSA_256: "RS256",
  X5T: "x5t",
  X5C: "x5c",
  AUDIENCE: "aud",
  EXPIRATION_TIME: "exp",
  ISSUER: "iss",
  SUBJECT: "sub",
  NOT_BEFORE: "nbf",
  JWT_ID: "jti"
};
const LOOPBACK_SERVER_CONSTANTS = {
  INTERVAL_MS: 100,
  TIMEOUT_MS: 5000
};

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * This class implements the API for network requests.
 */

class HttpClient {
  /**
   * Http Get request
   * @param url
   * @param options
   */
  async sendGetRequestAsync(url, options) {
    if (options != null && options.proxyUrl) {
      return networkRequestViaProxy(url, HttpMethod.GET, options);
    } else {
      return networkRequestViaHttps(url, HttpMethod.GET, options);
    }
  }
  /**
   * Http Post request
   * @param url
   * @param options
   */


  async sendPostRequestAsync(url, options, cancellationToken) {
    if (options != null && options.proxyUrl) {
      return networkRequestViaProxy(url, HttpMethod.POST, options, cancellationToken);
    } else {
      return networkRequestViaHttps(url, HttpMethod.POST, options, cancellationToken);
    }
  }

}

const networkRequestViaProxy = (url, httpMethod, options, timeout) => {
  const headers = (options == null ? void 0 : options.headers) || {};
  const proxyUrl = new URL((options == null ? void 0 : options.proxyUrl) || "");
  const destinationUrl = new URL(url); // "method: connect" must be used to establish a connection to the proxy

  const tunnelRequestOptions = {
    host: proxyUrl.hostname,
    port: proxyUrl.port,
    method: "CONNECT",
    path: destinationUrl.hostname,
    headers: headers
  };

  if (timeout) {
    tunnelRequestOptions.timeout = timeout;
  } // compose a request string for the socket


  let postRequestStringContent = "";

  if (httpMethod === HttpMethod.POST) {
    const body = (options == null ? void 0 : options.body) || "";
    postRequestStringContent = "Content-Type: application/x-www-form-urlencoded\r\n" + `Content-Length: ${body.length}\r\n` + `\r\n${body}`;
  }

  const outgoingRequestString = `${httpMethod.toUpperCase()} ${destinationUrl.href} HTTP/1.1\r\n` + `Host: ${destinationUrl.host}\r\n` + "Connection: close\r\n" + postRequestStringContent + "\r\n";
  return new Promise((resolve, reject) => {
    const request = http__default.request(tunnelRequestOptions);

    if (tunnelRequestOptions.timeout) {
      request.on("timeout", () => {
        request.destroy();
        reject(new Error("Request time out"));
      });
    }

    request.end(); // establish connection to the proxy

    request.on("connect", (response, socket) => {
      const statusCode = (response == null ? void 0 : response.statusCode) || 500;

      if (statusCode < 200 || statusCode > 299) {
        request.destroy();
        socket.destroy();
        reject(new Error(` Error connecting to proxy: ${response.statusCode}, ${response == null ? void 0 : response.statusMessage}`));
      }

      if (tunnelRequestOptions.timeout) {
        socket.setTimeout(tunnelRequestOptions.timeout);
        socket.on("timeout", () => {
          request.destroy();
          socket.destroy();
          reject(new Error("Request time out"));
        });
      } // make a request over an HTTP tunnel


      socket.write(outgoingRequestString);
      const data = [];
      socket.on("data", chunk => {
        data.push(chunk);
      });
      socket.on("end", () => {
        // combine all received buffer streams into one buffer, and then into a string
        const dataString = Buffer.concat([...data]).toString(); // separate each line into it's own entry in an arry

        const dataStringArray = dataString.split("\r\n"); // the first entry will contain the statusCode

        const statusCode = parseInt(dataStringArray[0].split(" ")[1]); // the last entry will contain the body

        const body = dataStringArray[dataStringArray.length - 1]; // everything in between the first and last entries are the headers

        const headersArray = dataStringArray.slice(1, dataStringArray.length - 2); // build an object out of all the headers

        const entries = new Map();
        headersArray.forEach(header => {
          /**
           * the header might look like "Content-Length: 1531", but that is just a string
           * it needs to be converted to a key/value pair
           * split the string at the first instance of ":"
           * there may be more than one ":" if the value of the header is supposed to be a JSON object
           */
          const headerKeyValue = header.split(new RegExp(/:\s(.*)/s));
          const headerKey = headerKeyValue[0];
          let headerValue = headerKeyValue[1]; // check if the value of the header is supposed to be a JSON object

          try {
            const object = JSON.parse(headerValue); // if it is, then convert it from a string to a JSON object

            if (object && typeof object === "object") {
              headerValue = object;
            }
          } catch (e) {// otherwise, leave it as a string
          }

          entries.set(headerKey, headerValue);
        });
        const headers = Object.fromEntries(entries);
        const networkResponse = {
          headers: headers,
          body: JSON.parse(body),
          status: statusCode
        };

        if ((statusCode < 200 || statusCode > 299) && // do not destroy the request for the device code flow
        networkResponse.body["error"] !== Constants.AUTHORIZATION_PENDING) {
          request.destroy();
        }

        resolve(networkResponse);
      });
      socket.on("error", chunk => {
        request.destroy();
        socket.destroy();
        reject(new Error(chunk.toString()));
      });
    });
    request.on("error", chunk => {
      request.destroy();
      reject(new Error(chunk.toString()));
    });
  });
};

const networkRequestViaHttps = (url, httpMethod, options, timeout) => {
  const isPostRequest = httpMethod === HttpMethod.POST;
  const body = (options == null ? void 0 : options.body) || "";
  const emptyHeaders = {};
  const customOptions = {
    method: httpMethod,
    headers: (options == null ? void 0 : options.headers) || emptyHeaders
  };

  if (timeout) {
    customOptions.timeout = timeout;
  }

  if (isPostRequest) {
    // needed for post request to work
    customOptions.headers = { ...customOptions.headers,
      "Content-Length": body.length
    };
  }

  return new Promise((resolve, reject) => {
    const request = https.request(url, customOptions);

    if (timeout) {
      request.on("timeout", () => {
        request.destroy();
        reject(new Error("Request time out"));
      });
    }

    if (isPostRequest) {
      request.write(body);
    }

    request.end();
    request.on("response", response => {
      const headers = response.headers;
      const statusCode = response.statusCode;
      const data = [];
      response.on("data", chunk => {
        data.push(chunk);
      });
      response.on("end", () => {
        // combine all received buffer streams into one buffer, and then into a string
        const body = Buffer.concat([...data]).toString();
        const networkResponse = {
          headers: headers,
          body: JSON.parse(body),
          status: statusCode
        };

        if ((statusCode < 200 || statusCode > 299) && // do not destroy the request for the device code flow
        networkResponse.body["error"] !== Constants.AUTHORIZATION_PENDING) {
          request.destroy();
        }

        resolve(networkResponse);
      });
    });
    request.on("error", chunk => {
      request.destroy();
      reject(new Error(chunk.toString()));
    });
  });
};

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class NetworkUtils {
  /**
   * Returns best compatible network client object.
   */
  static getNetworkClient() {
    return new HttpClient();
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
const DEFAULT_AUTH_OPTIONS = {
  clientId: msalCommon.Constants.EMPTY_STRING,
  authority: msalCommon.Constants.DEFAULT_AUTHORITY,
  clientSecret: msalCommon.Constants.EMPTY_STRING,
  clientAssertion: msalCommon.Constants.EMPTY_STRING,
  clientCertificate: {
    thumbprint: msalCommon.Constants.EMPTY_STRING,
    privateKey: msalCommon.Constants.EMPTY_STRING,
    x5c: msalCommon.Constants.EMPTY_STRING
  },
  knownAuthorities: [],
  cloudDiscoveryMetadata: msalCommon.Constants.EMPTY_STRING,
  authorityMetadata: msalCommon.Constants.EMPTY_STRING,
  clientCapabilities: [],
  protocolMode: msalCommon.ProtocolMode.AAD,
  azureCloudOptions: {
    azureCloudInstance: msalCommon.AzureCloudInstance.None,
    tenant: msalCommon.Constants.EMPTY_STRING
  },
  skipAuthorityMetadataCache: false
};
const DEFAULT_CACHE_OPTIONS = {};
const DEFAULT_LOGGER_OPTIONS = {
  loggerCallback: () => {// allow users to not set logger call back
  },
  piiLoggingEnabled: false,
  logLevel: msalCommon.LogLevel.Info
};
const DEFAULT_SYSTEM_OPTIONS = {
  loggerOptions: DEFAULT_LOGGER_OPTIONS,
  networkClient: /*#__PURE__*/NetworkUtils.getNetworkClient(),
  proxyUrl: msalCommon.Constants.EMPTY_STRING
};
const DEFAULT_TELEMETRY_OPTIONS = {
  application: {
    appName: msalCommon.Constants.EMPTY_STRING,
    appVersion: msalCommon.Constants.EMPTY_STRING
  }
};
/**
 * Sets the default options when not explicitly configured from app developer
 *
 * @param auth - Authentication options
 * @param cache - Cache options
 * @param system - System options
 * @param telemetry - Telemetry options
 *
 * @returns Configuration
 * @public
 */

function buildAppConfiguration({
  auth,
  cache,
  system,
  telemetry
}) {
  return {
    auth: { ...DEFAULT_AUTH_OPTIONS,
      ...auth
    },
    cache: { ...DEFAULT_CACHE_OPTIONS,
      ...cache
    },
    system: { ...DEFAULT_SYSTEM_OPTIONS,
      ...system
    },
    telemetry: { ...DEFAULT_TELEMETRY_OPTIONS,
      ...telemetry
    }
  };
}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class GuidGenerator {
  /**
   *
   * RFC4122: The version 4 UUID is meant for generating UUIDs from truly-random or pseudo-random numbers.
   * uuidv4 generates guids from cryprtographically-string random
   */
  generateGuid() {
    return uuid.v4();
  }
  /**
   * verifies if a string is  GUID
   * @param guid
   */


  isGuid(guid) {
    const regexGuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    return regexGuid.test(guid);
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class EncodingUtils {
  /**
   * 'utf8': Multibyte encoded Unicode characters. Many web pages and other document formats use UTF-8.
   * 'base64': Base64 encoding.
   *
   * @param str text
   */
  static base64Encode(str, encoding) {
    return Buffer.from(str, encoding).toString("base64");
  }
  /**
   * encode a URL
   * @param str
   */


  static base64EncodeUrl(str, encoding) {
    return EncodingUtils.base64Encode(str, encoding).replace(/=/g, msalCommon.Constants.EMPTY_STRING).replace(/\+/g, "-").replace(/\//g, "_");
  }
  /**
   * 'utf8': Multibyte encoded Unicode characters. Many web pages and other document formats use UTF-8.
   * 'base64': Base64 encoding.
   *
   * @param base64Str Base64 encoded text
   */


  static base64Decode(base64Str) {
    return Buffer.from(base64Str, "base64").toString("utf8");
  }
  /**
   * @param base64Str Base64 encoded Url
   */


  static base64DecodeUrl(base64Str) {
    let str = base64Str.replace(/-/g, "+").replace(/_/g, "/");

    while (str.length % 4) {
      str += "=";
    }

    return EncodingUtils.base64Decode(str);
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class HashUtils {
  /**
   * generate 'SHA256' hash
   * @param buffer
   */
  sha256(buffer) {
    return crypto.createHash(Hash.SHA256).update(buffer).digest();
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * https://tools.ietf.org/html/rfc7636#page-8
 */

class PkceGenerator {
  constructor() {
    this.hashUtils = new HashUtils();
  }
  /**
   * generates the codeVerfier and the challenge from the codeVerfier
   * reference: https://tools.ietf.org/html/rfc7636#section-4.1 and https://tools.ietf.org/html/rfc7636#section-4.2
   */


  async generatePkceCodes() {
    const verifier = this.generateCodeVerifier();
    const challenge = this.generateCodeChallengeFromVerifier(verifier);
    return {
      verifier,
      challenge
    };
  }
  /**
   * generates the codeVerfier; reference: https://tools.ietf.org/html/rfc7636#section-4.1
   */


  generateCodeVerifier() {
    const charArr = [];
    const maxNumber = 256 - 256 % CharSet.CV_CHARSET.length;

    while (charArr.length <= RANDOM_OCTET_SIZE) {
      const byte = crypto.randomBytes(1)[0];

      if (byte >= maxNumber) {
        /*
         * Ignore this number to maintain randomness.
         * Including it would result in an unequal distribution of characters after doing the modulo
         */
        continue;
      }

      const index = byte % CharSet.CV_CHARSET.length;
      charArr.push(CharSet.CV_CHARSET[index]);
    }

    const verifier = charArr.join(msalCommon.Constants.EMPTY_STRING);
    return EncodingUtils.base64EncodeUrl(verifier);
  }
  /**
   * generate the challenge from the codeVerfier; reference: https://tools.ietf.org/html/rfc7636#section-4.2
   * @param codeVerifier
   */


  generateCodeChallengeFromVerifier(codeVerifier) {
    return EncodingUtils.base64EncodeUrl(this.hashUtils.sha256(codeVerifier).toString("base64"), "base64");
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * This class implements MSAL node'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).
 * @public
 */

class CryptoProvider {
  constructor() {
    // Browser crypto needs to be validated first before any other classes can be set.
    this.pkceGenerator = new PkceGenerator();
    this.guidGenerator = new GuidGenerator();
    this.hashUtils = new HashUtils();
  }
  /**
   * Creates a new random GUID - used to populate state and nonce.
   * @returns string (GUID)
   */


  createNewGuid() {
    return this.guidGenerator.generateGuid();
  }
  /**
   * Encodes input string to base64.
   * @param input - string to be encoded
   */


  base64Encode(input) {
    return EncodingUtils.base64Encode(input);
  }
  /**
   * Decodes input string from base64.
   * @param input - string to be decoded
   */


  base64Decode(input) {
    return EncodingUtils.base64Decode(input);
  }
  /**
   * Generates PKCE codes used in Authorization Code Flow.
   */


  generatePkceCodes() {
    return this.pkceGenerator.generatePkceCodes();
  }
  /**
   * Generates a keypair, stores it and returns a thumbprint - not yet implemented for node
   */


  getPublicKeyThumbprint() {
    throw new Error("Method not implemented.");
  }
  /**
   * Removes cryptographic keypair from key store matching the keyId passed in
   * @param kid
   */


  removeTokenBindingKey() {
    throw new Error("Method not implemented.");
  }
  /**
   * Removes all cryptographic keys from Keystore
   */


  clearKeystore() {
    throw new Error("Method not implemented.");
  }
  /**
   * Signs the given object as a jwt payload with private key retrieved by given kid - currently not implemented for node
   */


  signJwt() {
    throw new Error("Method not implemented.");
  }
  /**
   * Returns the SHA-256 hash of an input string
   */


  async hashString(plainText) {
    return EncodingUtils.base64EncodeUrl(this.hashUtils.sha256(plainText).toString("base64"), "base64");
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * This class deserializes cache entities read from the file into in memory object types defined internally
 */

class Deserializer {
  /**
   * Parse the JSON blob in memory and deserialize the content
   * @param cachedJson
   */
  static deserializeJSONBlob(jsonFile) {
    const deserializedCache = msalCommon.StringUtils.isEmpty(jsonFile) ? {} : JSON.parse(jsonFile);
    return deserializedCache;
  }
  /**
   * Deserializes accounts to AccountEntity objects
   * @param accounts
   */


  static deserializeAccounts(accounts) {
    const accountObjects = {};

    if (accounts) {
      Object.keys(accounts).map(function (key) {
        const serializedAcc = accounts[key];
        const mappedAcc = {
          homeAccountId: serializedAcc.home_account_id,
          environment: serializedAcc.environment,
          realm: serializedAcc.realm,
          localAccountId: serializedAcc.local_account_id,
          username: serializedAcc.username,
          authorityType: serializedAcc.authority_type,
          name: serializedAcc.name,
          clientInfo: serializedAcc.client_info,
          lastModificationTime: serializedAcc.last_modification_time,
          lastModificationApp: serializedAcc.last_modification_app
        };
        const account = new msalCommon.AccountEntity();
        msalCommon.CacheManager.toObject(account, mappedAcc);
        accountObjects[key] = account;
      });
    }

    return accountObjects;
  }
  /**
   * Deserializes id tokens to IdTokenEntity objects
   * @param idTokens
   */


  static deserializeIdTokens(idTokens) {
    const idObjects = {};

    if (idTokens) {
      Object.keys(idTokens).map(function (key) {
        const serializedIdT = idTokens[key];
        const mappedIdT = {
          homeAccountId: serializedIdT.home_account_id,
          environment: serializedIdT.environment,
          credentialType: serializedIdT.credential_type,
          clientId: serializedIdT.client_id,
          secret: serializedIdT.secret,
          realm: serializedIdT.realm
        };
        const idToken = new msalCommon.IdTokenEntity();
        msalCommon.CacheManager.toObject(idToken, mappedIdT);
        idObjects[key] = idToken;
      });
    }

    return idObjects;
  }
  /**
   * Deserializes access tokens to AccessTokenEntity objects
   * @param accessTokens
   */


  static deserializeAccessTokens(accessTokens) {
    const atObjects = {};

    if (accessTokens) {
      Object.keys(accessTokens).map(function (key) {
        const serializedAT = accessTokens[key];
        const mappedAT = {
          homeAccountId: serializedAT.home_account_id,
          environment: serializedAT.environment,
          credentialType: serializedAT.credential_type,
          clientId: serializedAT.client_id,
          secret: serializedAT.secret,
          realm: serializedAT.realm,
          target: serializedAT.target,
          cachedAt: serializedAT.cached_at,
          expiresOn: serializedAT.expires_on,
          extendedExpiresOn: serializedAT.extended_expires_on,
          refreshOn: serializedAT.refresh_on,
          keyId: serializedAT.key_id,
          tokenType: serializedAT.token_type,
          requestedClaims: serializedAT.requestedClaims,
          requestedClaimsHash: serializedAT.requestedClaimsHash
        };
        const accessToken = new msalCommon.AccessTokenEntity();
        msalCommon.CacheManager.toObject(accessToken, mappedAT);
        atObjects[key] = accessToken;
      });
    }

    return atObjects;
  }
  /**
   * Deserializes refresh tokens to RefreshTokenEntity objects
   * @param refreshTokens
   */


  static deserializeRefreshTokens(refreshTokens) {
    const rtObjects = {};

    if (refreshTokens) {
      Object.keys(refreshTokens).map(function (key) {
        const serializedRT = refreshTokens[key];
        const mappedRT = {
          homeAccountId: serializedRT.home_account_id,
          environment: serializedRT.environment,
          credentialType: serializedRT.credential_type,
          clientId: serializedRT.client_id,
          secret: serializedRT.secret,
          familyId: serializedRT.family_id,
          target: serializedRT.target,
          realm: serializedRT.realm
        };
        const refreshToken = new msalCommon.RefreshTokenEntity();
        msalCommon.CacheManager.toObject(refreshToken, mappedRT);
        rtObjects[key] = refreshToken;
      });
    }

    return rtObjects;
  }
  /**
   * Deserializes appMetadata to AppMetaData objects
   * @param appMetadata
   */


  static deserializeAppMetadata(appMetadata) {
    const appMetadataObjects = {};

    if (appMetadata) {
      Object.keys(appMetadata).map(function (key) {
        const serializedAmdt = appMetadata[key];
        const mappedAmd = {
          clientId: serializedAmdt.client_id,
          environment: serializedAmdt.environment,
          familyId: serializedAmdt.family_id
        };
        const amd = new msalCommon.AppMetadataEntity();
        msalCommon.CacheManager.toObject(amd, mappedAmd);
        appMetadataObjects[key] = amd;
      });
    }

    return appMetadataObjects;
  }
  /**
   * Deserialize an inMemory Cache
   * @param jsonCache
   */


  static deserializeAllCache(jsonCache) {
    return {
      accounts: jsonCache.Account ? this.deserializeAccounts(jsonCache.Account) : {},
      idTokens: jsonCache.IdToken ? this.deserializeIdTokens(jsonCache.IdToken) : {},
      accessTokens: jsonCache.AccessToken ? this.deserializeAccessTokens(jsonCache.AccessToken) : {},
      refreshTokens: jsonCache.RefreshToken ? this.deserializeRefreshTokens(jsonCache.RefreshToken) : {},
      appMetadata: jsonCache.AppMetadata ? this.deserializeAppMetadata(jsonCache.AppMetadata) : {}
    };
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class Serializer {
  /**
   * serialize the JSON blob
   * @param data
   */
  static serializeJSONBlob(data) {
    return JSON.stringify(data);
  }
  /**
   * Serialize Accounts
   * @param accCache
   */


  static serializeAccounts(accCache) {
    const accounts = {};
    Object.keys(accCache).map(function (key) {
      const accountEntity = accCache[key];
      accounts[key] = {
        home_account_id: accountEntity.homeAccountId,
        environment: accountEntity.environment,
        realm: accountEntity.realm,
        local_account_id: accountEntity.localAccountId,
        username: accountEntity.username,
        authority_type: accountEntity.authorityType,
        name: accountEntity.name,
        client_info: accountEntity.clientInfo,
        last_modification_time: accountEntity.lastModificationTime,
        last_modification_app: accountEntity.lastModificationApp
      };
    });
    return accounts;
  }
  /**
   * Serialize IdTokens
   * @param idTCache
   */


  static serializeIdTokens(idTCache) {
    const idTokens = {};
    Object.keys(idTCache).map(function (key) {
      const idTEntity = idTCache[key];
      idTokens[key] = {
        home_account_id: idTEntity.homeAccountId,
        environment: idTEntity.environment,
        credential_type: idTEntity.credentialType,
        client_id: idTEntity.clientId,
        secret: idTEntity.secret,
        realm: idTEntity.realm
      };
    });
    return idTokens;
  }
  /**
   * Serializes AccessTokens
   * @param atCache
   */


  static serializeAccessTokens(atCache) {
    const accessTokens = {};
    Object.keys(atCache).map(function (key) {
      const atEntity = atCache[key];
      accessTokens[key] = {
        home_account_id: atEntity.homeAccountId,
        environment: atEntity.environment,
        credential_type: atEntity.credentialType,
        client_id: atEntity.clientId,
        secret: atEntity.secret,
        realm: atEntity.realm,
        target: atEntity.target,
        cached_at: atEntity.cachedAt,
        expires_on: atEntity.expiresOn,
        extended_expires_on: atEntity.extendedExpiresOn,
        refresh_on: atEntity.refreshOn,
        key_id: atEntity.keyId,
        token_type: atEntity.tokenType,
        requestedClaims: atEntity.requestedClaims,
        requestedClaimsHash: atEntity.requestedClaimsHash
      };
    });
    return accessTokens;
  }
  /**
   * Serialize refreshTokens
   * @param rtCache
   */


  static serializeRefreshTokens(rtCache) {
    const refreshTokens = {};
    Object.keys(rtCache).map(function (key) {
      const rtEntity = rtCache[key];
      refreshTokens[key] = {
        home_account_id: rtEntity.homeAccountId,
        environment: rtEntity.environment,
        credential_type: rtEntity.credentialType,
        client_id: rtEntity.clientId,
        secret: rtEntity.secret,
        family_id: rtEntity.familyId,
        target: rtEntity.target,
        realm: rtEntity.realm
      };
    });
    return refreshTokens;
  }
  /**
   * Serialize amdtCache
   * @param amdtCache
   */


  static serializeAppMetadata(amdtCache) {
    const appMetadata = {};
    Object.keys(amdtCache).map(function (key) {
      const amdtEntity = amdtCache[key];
      appMetadata[key] = {
        client_id: amdtEntity.clientId,
        environment: amdtEntity.environment,
        family_id: amdtEntity.familyId
      };
    });
    return appMetadata;
  }
  /**
   * Serialize the cache
   * @param jsonContent
   */


  static serializeAllCache(inMemCache) {
    return {
      Account: this.serializeAccounts(inMemCache.accounts),
      IdToken: this.serializeIdTokens(inMemCache.idTokens),
      AccessToken: this.serializeAccessTokens(inMemCache.accessTokens),
      RefreshToken: this.serializeRefreshTokens(inMemCache.refreshTokens),
      AppMetadata: this.serializeAppMetadata(inMemCache.appMetadata)
    };
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * This class implements Storage for node, reading cache from user specified storage location or an  extension library
 * @public
 */

class NodeStorage extends msalCommon.CacheManager {
  constructor(logger, clientId, cryptoImpl) {
    super(clientId, cryptoImpl);
    this.cache = {};
    this.changeEmitters = [];
    this.logger = logger;
  }
  /**
   * Queue up callbacks
   * @param func - a callback function for cache change indication
   */


  registerChangeEmitter(func) {
    this.changeEmitters.push(func);
  }
  /**
   * Invoke the callback when cache changes
   */


  emitChange() {
    this.changeEmitters.forEach(func => func.call(null));
  }
  /**
   * Converts cacheKVStore to InMemoryCache
   * @param cache - key value store
   */


  cacheToInMemoryCache(cache) {
    const inMemoryCache = {
      accounts: {},
      idTokens: {},
      accessTokens: {},
      refreshTokens: {},
      appMetadata: {}
    };

    for (const key in cache) {
      if (cache[key] instanceof msalCommon.AccountEntity) {
        inMemoryCache.accounts[key] = cache[key];
      } else if (cache[key] instanceof msalCommon.IdTokenEntity) {
        inMemoryCache.idTokens[key] = cache[key];
      } else if (cache[key] instanceof msalCommon.AccessTokenEntity) {
        inMemoryCache.accessTokens[key] = cache[key];
      } else if (cache[key] instanceof msalCommon.RefreshTokenEntity) {
        inMemoryCache.refreshTokens[key] = cache[key];
      } else if (cache[key] instanceof msalCommon.AppMetadataEntity) {
        inMemoryCache.appMetadata[key] = cache[key];
      } else {
        continue;
      }
    }

    return inMemoryCache;
  }
  /**
   * converts inMemoryCache to CacheKVStore
   * @param inMemoryCache - kvstore map for inmemory
   */


  inMemoryCacheToCache(inMemoryCache) {
    // convert in memory cache to a flat Key-Value map
    let cache = this.getCache();
    cache = { ...cache,
      ...inMemoryCache.accounts,
      ...inMemoryCache.idTokens,
      ...inMemoryCache.accessTokens,
      ...inMemoryCache.refreshTokens,
      ...inMemoryCache.appMetadata
    }; // convert in memory cache to a flat Key-Value map

    return cache;
  }
  /**
   * gets the current in memory cache for the client
   */


  getInMemoryCache() {
    this.logger.trace("Getting in-memory cache"); // convert the cache key value store to inMemoryCache

    const inMemoryCache = this.cacheToInMemoryCache(this.getCache());
    return inMemoryCache;
  }
  /**
   * sets the current in memory cache for the client
   * @param inMemoryCache - key value map in memory
   */


  setInMemoryCache(inMemoryCache) {
    this.logger.trace("Setting in-memory cache"); // convert and append the inMemoryCache to cacheKVStore

    const cache = this.inMemoryCacheToCache(inMemoryCache);
    this.setCache(cache);
    this.emitChange();
  }
  /**
   * get the current cache key-value store
   */


  getCache() {
    this.logger.trace("Getting cache key-value store");
    return this.cache;
  }
  /**
   * sets the current cache (key value store)
   * @param cacheMap - key value map
   */


  setCache(cache) {
    this.logger.trace("Setting cache key value store");
    this.cache = cache; // mark change in cache

    this.emitChange();
  }
  /**
   * Gets cache item with given key.
   * @param key - lookup key for the cache entry
   */


  getItem(key) {
    this.logger.tracePii(`Item key: ${key}`); // read cache

    const cache = this.getCache();
    return cache[key];
  }
  /**
   * Gets cache item with given key-value
   * @param key - lookup key for the cache entry
   * @param value - value of the cache entry
   */


  setItem(key, value) {
    this.logger.tracePii(`Item key: ${key}`); // read cache

    const cache = this.getCache();
    cache[key] = value; // write to cache

    this.setCache(cache);
  }
  /**
   * fetch the account entity
   * @param accountKey - lookup key to fetch cache type AccountEntity
   */


  getAccount(accountKey) {
    const account = this.getItem(accountKey);

    if (msalCommon.AccountEntity.isAccountEntity(account)) {
      return account;
    }

    return null;
  }
  /**
   * set account entity
   * @param account - cache value to be set of type AccountEntity
   */


  setAccount(account) {
    const accountKey = account.generateAccountKey();
    this.setItem(accountKey, account);
  }
  /**
   * fetch the idToken credential
   * @param idTokenKey - lookup key to fetch cache type IdTokenEntity
   */


  getIdTokenCredential(idTokenKey) {
    const idToken = this.getItem(idTokenKey);

    if (msalCommon.IdTokenEntity.isIdTokenEntity(idToken)) {
      return idToken;
    }

    return null;
  }
  /**
   * set idToken credential
   * @param idToken - cache value to be set of type IdTokenEntity
   */


  setIdTokenCredential(idToken) {
    const idTokenKey = idToken.generateCredentialKey();
    this.setItem(idTokenKey, idToken);
  }
  /**
   * fetch the accessToken credential
   * @param accessTokenKey - lookup key to fetch cache type AccessTokenEntity
   */


  getAccessTokenCredential(accessTokenKey) {
    const accessToken = this.getItem(accessTokenKey);

    if (msalCommon.AccessTokenEntity.isAccessTokenEntity(accessToken)) {
      return accessToken;
    }

    return null;
  }
  /**
   * set accessToken credential
   * @param accessToken -  cache value to be set of type AccessTokenEntity
   */


  setAccessTokenCredential(accessToken) {
    const accessTokenKey = accessToken.generateCredentialKey();
    this.setItem(accessTokenKey, accessToken);
  }
  /**
   * fetch the refreshToken credential
   * @param refreshTokenKey - lookup key to fetch cache type RefreshTokenEntity
   */


  getRefreshTokenCredential(refreshTokenKey) {
    const refreshToken = this.getItem(refreshTokenKey);

    if (msalCommon.RefreshTokenEntity.isRefreshTokenEntity(refreshToken)) {
      return refreshToken;
    }

    return null;
  }
  /**
   * set refreshToken credential
   * @param refreshToken - cache value to be set of type RefreshTokenEntity
   */


  setRefreshTokenCredential(refreshToken) {
    const refreshTokenKey = refreshToken.generateCredentialKey();
    this.setItem(refreshTokenKey, refreshToken);
  }
  /**
   * fetch appMetadata entity from the platform cache
   * @param appMetadataKey - lookup key to fetch cache type AppMetadataEntity
   */


  getAppMetadata(appMetadataKey) {
    const appMetadata = this.getItem(appMetadataKey);

    if (msalCommon.AppMetadataEntity.isAppMetadataEntity(appMetadataKey, appMetadata)) {
      return appMetadata;
    }

    return null;
  }
  /**
   * set appMetadata entity to the platform cache
   * @param appMetadata - cache value to be set of type AppMetadataEntity
   */


  setAppMetadata(appMetadata) {
    const appMetadataKey = appMetadata.generateAppMetadataKey();
    this.setItem(appMetadataKey, appMetadata);
  }
  /**
   * fetch server telemetry entity from the platform cache
   * @param serverTelemetrykey - lookup key to fetch cache type ServerTelemetryEntity
   */


  getServerTelemetry(serverTelemetrykey) {
    const serverTelemetryEntity = this.getItem(serverTelemetrykey);

    if (serverTelemetryEntity && msalCommon.ServerTelemetryEntity.isServerTelemetryEntity(serverTelemetrykey, serverTelemetryEntity)) {
      return serverTelemetryEntity;
    }

    return null;
  }
  /**
   * set server telemetry entity to the platform cache
   * @param serverTelemetryKey - lookup key to fetch cache type ServerTelemetryEntity
   * @param serverTelemetry - cache value to be set of type ServerTelemetryEntity
   */


  setServerTelemetry(serverTelemetryKey, serverTelemetry) {
    this.setItem(serverTelemetryKey, serverTelemetry);
  }
  /**
   * fetch authority metadata entity from the platform cache
   * @param key - lookup key to fetch cache type AuthorityMetadataEntity
   */


  getAuthorityMetadata(key) {
    const authorityMetadataEntity = this.getItem(key);

    if (authorityMetadataEntity && msalCommon.AuthorityMetadataEntity.isAuthorityMetadataEntity(key, authorityMetadataEntity)) {
      return authorityMetadataEntity;
    }

    return null;
  }
  /**
   * Get all authority metadata keys
   */


  getAuthorityMetadataKeys() {
    return this.getKeys().filter(key => {
      return this.isAuthorityMetadata(key);
    });
  }
  /**
   * set authority metadata entity to the platform cache
   * @param key - lookup key to fetch cache type AuthorityMetadataEntity
   * @param metadata - cache value to be set of type AuthorityMetadataEntity
   */


  setAuthorityMetadata(key, metadata) {
    this.setItem(key, metadata);
  }
  /**
   * fetch throttling entity from the platform cache
   * @param throttlingCacheKey - lookup key to fetch cache type ThrottlingEntity
   */


  getThrottlingCache(throttlingCacheKey) {
    const throttlingCache = this.getItem(throttlingCacheKey);

    if (throttlingCache && msalCommon.ThrottlingEntity.isThrottlingEntity(throttlingCacheKey, throttlingCache)) {
      return throttlingCache;
    }

    return null;
  }
  /**
   * set throttling entity to the platform cache
   * @param throttlingCacheKey - lookup key to fetch cache type ThrottlingEntity
   * @param throttlingCache - cache value to be set of type ThrottlingEntity
   */


  setThrottlingCache(throttlingCacheKey, throttlingCache) {
    this.setItem(throttlingCacheKey, throttlingCache);
  }
  /**
   * Removes the cache item from memory with the given key.
   * @param key - lookup key to remove a cache entity
   * @param inMemory - key value map of the cache
   */


  removeItem(key) {
    this.logger.tracePii(`Item key: ${key}`); // read inMemoryCache

    let result = false;
    const cache = this.getCache();

    if (!!cache[key]) {
      delete cache[key];
      result = true;
    } // write to the cache after removal


    if (result) {
      this.setCache(cache);
      this.emitChange();
    }

    return result;
  }
  /**
   * Checks whether key is in cache.
   * @param key - look up key for a cache entity
   */


  containsKey(key) {
    return this.getKeys().includes(key);
  }
  /**
   * Gets all keys in window.
   */


  getKeys() {
    this.logger.trace("Retrieving all cache keys"); // read cache

    const cache = this.getCache();
    return [...Object.keys(cache)];
  }
  /**
   * Clears all cache entries created by MSAL (except tokens).
   */


  async clear() {
    this.logger.trace("Clearing cache entries created by MSAL"); // read inMemoryCache

    const cacheKeys = this.getKeys(); // delete each element

    cacheKeys.forEach(key => {
      this.removeItem(key);
    });
    this.emitChange();
  }
  /**
   * Initialize in memory cache from an exisiting cache vault
   * @param cache - blob formatted cache (JSON)
   */


  static generateInMemoryCache(cache) {
    return Deserializer.deserializeAllCache(Deserializer.deserializeJSONBlob(cache));
  }
  /**
   * retrieves the final JSON
   * @param inMemoryCache - itemised cache read from the JSON
   */


  static generateJsonCache(inMemoryCache) {
    return Serializer.serializeAllCache(inMemoryCache);
  }
  /**
   * Updates a credential's cache key if the current cache key is outdated
   */


  updateCredentialCacheKey(currentCacheKey, credential) {
    const updatedCacheKey = credential.generateCredentialKey();

    if (currentCacheKey !== updatedCacheKey) {
      const cacheItem = this.getItem(currentCacheKey);

      if (cacheItem) {
        this.removeItem(currentCacheKey);
        this.setItem(updatedCacheKey, cacheItem);
        this.logger.verbose(`Updated an outdated ${credential.credentialType} cache key`);
        return updatedCacheKey;
      } else {
        this.logger.error(`Attempted to update an outdated ${credential.credentialType} cache key but no item matching the outdated key was found in storage`);
      }
    }

    return currentCacheKey;
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
const defaultSerializedCache = {
  Account: {},
  IdToken: {},
  AccessToken: {},
  RefreshToken: {},
  AppMetadata: {}
};
/**
 * In-memory token cache manager
 * @public
 */

class TokenCache {
  constructor(storage, logger, cachePlugin) {
    this.cacheHasChanged = false;
    this.storage = storage;
    this.storage.registerChangeEmitter(this.handleChangeEvent.bind(this));

    if (cachePlugin) {
      this.persistence = cachePlugin;
    }

    this.logger = logger;
  }
  /**
   * Set to true if cache state has changed since last time serialize or writeToPersistence was called
   */


  hasChanged() {
    return this.cacheHasChanged;
  }
  /**
   * Serializes in memory cache to JSON
   */


  serialize() {
    this.logger.trace("Serializing in-memory cache");
    let finalState = Serializer.serializeAllCache(this.storage.getInMemoryCache()); // if cacheSnapshot not null or empty, merge

    if (!msalCommon.StringUtils.isEmpty(this.cacheSnapshot)) {
      this.logger.trace("Reading cache snapshot from disk");
      finalState = this.mergeState(JSON.parse(this.cacheSnapshot), finalState);
    } else {
      this.logger.trace("No cache snapshot to merge");
    }

    this.cacheHasChanged = false;
    return JSON.stringify(finalState);
  }
  /**
   * Deserializes JSON to in-memory cache. JSON should be in MSAL cache schema format
   * @param cache - blob formatted cache
   */


  deserialize(cache) {
    this.logger.trace("Deserializing JSON to in-memory cache");
    this.cacheSnapshot = cache;

    if (!msalCommon.StringUtils.isEmpty(this.cacheSnapshot)) {
      this.logger.trace("Reading cache snapshot from disk");
      const deserializedCache = Deserializer.deserializeAllCache(this.overlayDefaults(JSON.parse(this.cacheSnapshot)));
      this.storage.setInMemoryCache(deserializedCache);
    } else {
      this.logger.trace("No cache snapshot to deserialize");
    }
  }
  /**
   * Fetches the cache key-value map
   */


  getKVStore() {
    return this.storage.getCache();
  }
  /**
   * API that retrieves all accounts currently in cache to the user
   */


  async getAllAccounts() {
    this.logger.trace("getAllAccounts called");
    let cacheContext;

    try {
      if (this.persistence) {
        cacheContext = new msalCommon.TokenCacheContext(this, false);
        await this.persistence.beforeCacheAccess(cacheContext);
      }

      return this.storage.getAllAccounts();
    } finally {
      if (this.persistence && cacheContext) {
        await this.persistence.afterCacheAccess(cacheContext);
      }
    }
  }
  /**
   * Returns the signed in account matching homeAccountId.
   * (the account object is created at the time of successful login)
   * or null when no matching account is found
   * @param homeAccountId - unique identifier for an account (uid.utid)
   */


  async getAccountByHomeId(homeAccountId) {
    const allAccounts = await this.getAllAccounts();

    if (!msalCommon.StringUtils.isEmpty(homeAccountId) && allAccounts && allAccounts.length) {
      return allAccounts.filter(accountObj => accountObj.homeAccountId === homeAccountId)[0] || null;
    } else {
      return null;
    }
  }
  /**
   * Returns the signed in account matching localAccountId.
   * (the account object is created at the time of successful login)
   * or null when no matching account is found
   * @param localAccountId - unique identifier of an account (sub/obj when homeAccountId cannot be populated)
   */


  async getAccountByLocalId(localAccountId) {
    const allAccounts = await this.getAllAccounts();

    if (!msalCommon.StringUtils.isEmpty(localAccountId) && allAccounts && allAccounts.length) {
      return allAccounts.filter(accountObj => accountObj.localAccountId === localAccountId)[0] || null;
    } else {
      return null;
    }
  }
  /**
   * API to remove a specific account and the relevant data from cache
   * @param account - AccountInfo passed by the user
   */


  async removeAccount(account) {
    this.logger.trace("removeAccount called");
    let cacheContext;

    try {
      if (this.persistence) {
        cacheContext = new msalCommon.TokenCacheContext(this, true);
        await this.persistence.beforeCacheAccess(cacheContext);
      }

      await this.storage.removeAccount(msalCommon.AccountEntity.generateAccountCacheKey(account));
    } finally {
      if (this.persistence && cacheContext) {
        await this.persistence.afterCacheAccess(cacheContext);
      }
    }
  }
  /**
   * Called when the cache has changed state.
   */


  handleChangeEvent() {
    this.cacheHasChanged = true;
  }
  /**
   * Merge in memory cache with the cache snapshot.
   * @param oldState - cache before changes
   * @param currentState - current cache state in the library
   */


  mergeState(oldState, currentState) {
    this.logger.trace("Merging in-memory cache with cache snapshot");
    const stateAfterRemoval = this.mergeRemovals(oldState, currentState);
    return this.mergeUpdates(stateAfterRemoval, currentState);
  }
  /**
   * Deep update of oldState based on newState values
   * @param oldState - cache before changes
   * @param newState - updated cache
   */


  mergeUpdates(oldState, newState) {
    Object.keys(newState).forEach(newKey => {
      const newValue = newState[newKey]; // if oldState does not contain value but newValue does, add it

      if (!oldState.hasOwnProperty(newKey)) {
        if (newValue !== null) {
          oldState[newKey] = newValue;
        }
      } else {
        // both oldState and newState contain the key, do deep update
        const newValueNotNull = newValue !== null;
        const newValueIsObject = typeof newValue === "object";
        const newValueIsNotArray = !Array.isArray(newValue);
        const oldStateNotUndefinedOrNull = typeof oldState[newKey] !== "undefined" && oldState[newKey] !== null;

        if (newValueNotNull && newValueIsObject && newValueIsNotArray && oldStateNotUndefinedOrNull) {
          this.mergeUpdates(oldState[newKey], newValue);
        } else {
          oldState[newKey] = newValue;
        }
      }
    });
    return oldState;
  }
  /**
   * Removes entities in oldState that the were removed from newState. If there are any unknown values in root of
   * oldState that are not recognized, they are left untouched.
   * @param oldState - cache before changes
   * @param newState - updated cache
   */


  mergeRemovals(oldState, newState) {
    this.logger.trace("Remove updated entries in cache");
    const accounts = oldState.Account ? this.mergeRemovalsDict(oldState.Account, newState.Account) : oldState.Account;
    const accessTokens = oldState.AccessToken ? this.mergeRemovalsDict(oldState.AccessToken, newState.AccessToken) : oldState.AccessToken;
    const refreshTokens = oldState.RefreshToken ? this.mergeRemovalsDict(oldState.RefreshToken, newState.RefreshToken) : oldState.RefreshToken;
    const idTokens = oldState.IdToken ? this.mergeRemovalsDict(oldState.IdToken, newState.IdToken) : oldState.IdToken;
    const appMetadata = oldState.AppMetadata ? this.mergeRemovalsDict(oldState.AppMetadata, newState.AppMetadata) : oldState.AppMetadata;
    return { ...oldState,
      Account: accounts,
      AccessToken: accessTokens,
      RefreshToken: refreshTokens,
      IdToken: idTokens,
      AppMetadata: appMetadata
    };
  }
  /**
   * Helper to merge new cache with the old one
   * @param oldState - cache before changes
   * @param newState - updated cache
   */


  mergeRemovalsDict(oldState, newState) {
    const finalState = { ...oldState
    };
    Object.keys(oldState).forEach(oldKey => {
      if (!newState || !newState.hasOwnProperty(oldKey)) {
        delete finalState[oldKey];
      }
    });
    return finalState;
  }
  /**
   * Helper to overlay as a part of cache merge
   * @param passedInCache - cache read from the blob
   */


  overlayDefaults(passedInCache) {
    this.logger.trace("Overlaying input cache with the default cache");
    return {
      Account: { ...defaultSerializedCache.Account,
        ...passedInCache.Account
      },
      IdToken: { ...defaultSerializedCache.IdToken,
        ...passedInCache.IdToken
      },
      AccessToken: { ...defaultSerializedCache.AccessToken,
        ...passedInCache.AccessToken
      },
      RefreshToken: { ...defaultSerializedCache.RefreshToken,
        ...passedInCache.RefreshToken
      },
      AppMetadata: { ...defaultSerializedCache.AppMetadata,
        ...passedInCache.AppMetadata
      }
    };
  }

}

/* eslint-disable header/header */
const name = "@azure/msal-node";
const version = "1.14.2";

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * NodeAuthErrorMessage class containing string constants used by error codes and messages.
 */

const NodeAuthErrorMessage = {
  invalidLoopbackAddressType: {
    code: "invalid_loopback_server_address_type",
    desc: "Loopback server address is not type string. This is unexpected."
  },
  unableToLoadRedirectUri: {
    code: "unable_to_load_redirectUrl",
    desc: "Loopback server callback was invoked without a url. This is unexpected."
  },
  noAuthCodeInResponse: {
    code: "no_auth_code_in_response",
    desc: "No auth code found in the server response. Please check your network trace to determine what happened."
  },
  noLoopbackServerExists: {
    code: "no_loopback_server_exists",
    desc: "No loopback server exists yet."
  },
  loopbackServerAlreadyExists: {
    code: "loopback_server_already_exists",
    desc: "Loopback server already exists. Cannot create another."
  },
  loopbackServerTimeout: {
    code: "loopback_server_timeout",
    desc: "Timed out waiting for auth code listener to be registered."
  },
  stateNotFoundError: {
    code: "state_not_found",
    desc: "State not found. Please verify that the request originated from msal."
  }
};
class NodeAuthError extends msalCommon.AuthError {
  constructor(errorCode, errorMessage) {
    super(errorCode, errorMessage);
    this.name = "NodeAuthError";
  }
  /**
   * Creates an error thrown if loopback server address is of type string.
   */


  static createInvalidLoopbackAddressTypeError() {
    return new NodeAuthError(NodeAuthErrorMessage.invalidLoopbackAddressType.code, `${NodeAuthErrorMessage.invalidLoopbackAddressType.desc}`);
  }
  /**
   * Creates an error thrown if the loopback server is unable to get a url.
   */


  static createUnableToLoadRedirectUrlError() {
    return new NodeAuthError(NodeAuthErrorMessage.unableToLoadRedirectUri.code, `${NodeAuthErrorMessage.unableToLoadRedirectUri.desc}`);
  }
  /**
   * Creates an error thrown if the server response does not contain an auth code.
   */


  static createNoAuthCodeInResponseError() {
    return new NodeAuthError(NodeAuthErrorMessage.noAuthCodeInResponse.code, `${NodeAuthErrorMessage.noAuthCodeInResponse.desc}`);
  }
  /**
   * Creates an error thrown if the loopback server has not been spun up yet.
   */


  static createNoLoopbackServerExistsError() {
    return new NodeAuthError(NodeAuthErrorMessage.noLoopbackServerExists.code, `${NodeAuthErrorMessage.noLoopbackServerExists.desc}`);
  }
  /**
   * Creates an error thrown if a loopback server already exists when attempting to create another one.
   */


  static createLoopbackServerAlreadyExistsError() {
    return new NodeAuthError(NodeAuthErrorMessage.loopbackServerAlreadyExists.code, `${NodeAuthErrorMessage.loopbackServerAlreadyExists.desc}`);
  }
  /**
   * Creates an error thrown if the loopback server times out registering the auth code listener.
   */


  static createLoopbackServerTimeoutError() {
    return new NodeAuthError(NodeAuthErrorMessage.loopbackServerTimeout.code, `${NodeAuthErrorMessage.loopbackServerTimeout.desc}`);
  }
  /**
   * Creates an error thrown when the state is not present.
   */


  static createStateNotFoundError() {
    return new NodeAuthError(NodeAuthErrorMessage.stateNotFoundError.code, NodeAuthErrorMessage.stateNotFoundError.desc);
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * Base abstract class for all ClientApplications - public and confidential
 * @public
 */

class ClientApplication {
  /**
   * Constructor for the ClientApplication
   */
  constructor(configuration) {
    this.config = buildAppConfiguration(configuration);
    this.cryptoProvider = new CryptoProvider();
    this.logger = new msalCommon.Logger(this.config.system.loggerOptions, name, version);
    this.storage = new NodeStorage(this.logger, this.config.auth.clientId, this.cryptoProvider);
    this.tokenCache = new TokenCache(this.storage, this.logger, this.config.cache.cachePlugin);
  }
  /**
   * Creates the URL of the authorization request, letting the user input credentials and consent to the
   * application. The URL targets the /authorize endpoint of the authority configured in the
   * application object.
   *
   * Once the user inputs their credentials and consents, the authority will send a response to the redirect URI
   * sent in the request and should contain an authorization code, which can then be used to acquire tokens via
   * `acquireTokenByCode(AuthorizationCodeRequest)`.
   */


  async getAuthCodeUrl(request) {
    this.logger.info("getAuthCodeUrl called", request.correlationId);
    const validRequest = { ...request,
      ...(await this.initializeBaseRequest(request)),
      responseMode: request.responseMode || msalCommon.ResponseMode.QUERY,
      authenticationScheme: msalCommon.AuthenticationScheme.BEARER
    };
    const authClientConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, undefined, undefined, request.azureCloudOptions);
    const authorizationCodeClient = new msalCommon.AuthorizationCodeClient(authClientConfig);
    this.logger.verbose("Auth code client created", validRequest.correlationId);
    return authorizationCodeClient.getAuthCodeUrl(validRequest);
  }
  /**
   * Acquires a token by exchanging the Authorization Code received from the first step of OAuth2.0
   * Authorization Code flow.
   *
   * `getAuthCodeUrl(AuthorizationCodeUrlRequest)` can be used to create the URL for the first step of OAuth2.0
   * Authorization Code flow. Ensure that values for redirectUri and scopes in AuthorizationCodeUrlRequest and
   * AuthorizationCodeRequest are the same.
   */


  async acquireTokenByCode(request, authCodePayLoad) {
    this.logger.info("acquireTokenByCode called");

    if (request.state && authCodePayLoad) {
      this.logger.info("acquireTokenByCode - validating state");
      this.validateState(request.state, authCodePayLoad.state || ""); // eslint-disable-next-line no-param-reassign

      authCodePayLoad = { ...authCodePayLoad,
        state: ""
      };
    }

    const validRequest = { ...request,
      ...(await this.initializeBaseRequest(request)),
      authenticationScheme: msalCommon.AuthenticationScheme.BEARER
    };
    const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByCode, validRequest.correlationId);

    try {
      const authClientConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, serverTelemetryManager, undefined, request.azureCloudOptions);
      const authorizationCodeClient = new msalCommon.AuthorizationCodeClient(authClientConfig);
      this.logger.verbose("Auth code client created", validRequest.correlationId);
      return authorizationCodeClient.acquireToken(validRequest, authCodePayLoad);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Acquires a token by exchanging the refresh token provided for a new set of tokens.
   *
   * This API is provided only for scenarios where you would like to migrate from ADAL to MSAL. Otherwise, it is
   * recommended that you use `acquireTokenSilent()` for silent scenarios. When using `acquireTokenSilent()`, MSAL will
   * handle the caching and refreshing of tokens automatically.
   */


  async acquireTokenByRefreshToken(request) {
    this.logger.info("acquireTokenByRefreshToken called", request.correlationId);
    const validRequest = { ...request,
      ...(await this.initializeBaseRequest(request)),
      authenticationScheme: msalCommon.AuthenticationScheme.BEARER
    };
    const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByRefreshToken, validRequest.correlationId);

    try {
      const refreshTokenClientConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, serverTelemetryManager, undefined, request.azureCloudOptions);
      const refreshTokenClient = new msalCommon.RefreshTokenClient(refreshTokenClientConfig);
      this.logger.verbose("Refresh token client created", validRequest.correlationId);
      return refreshTokenClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Acquires a token silently when a user specifies the account the token is requested for.
   *
   * This API expects the user to provide an account object and looks into the cache to retrieve the token if present.
   * There is also an optional "forceRefresh" boolean the user can send to bypass the cache for access_token and id_token.
   * In case the refresh_token is expired or not found, an error is thrown
   * and the guidance is for the user to call any interactive token acquisition API (eg: `acquireTokenByCode()`).
   */


  async acquireTokenSilent(request) {
    const validRequest = { ...request,
      ...(await this.initializeBaseRequest(request)),
      forceRefresh: request.forceRefresh || false
    };
    const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent, validRequest.correlationId, validRequest.forceRefresh);

    try {
      const silentFlowClientConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, serverTelemetryManager, undefined, request.azureCloudOptions);
      const silentFlowClient = new msalCommon.SilentFlowClient(silentFlowClientConfig);
      this.logger.verbose("Silent flow client created", validRequest.correlationId);
      return silentFlowClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Acquires tokens with password grant by exchanging client applications username and password for credentials
   *
   * The latest OAuth 2.0 Security Best Current Practice disallows the password grant entirely.
   * More details on this recommendation at https://tools.ietf.org/html/draft-ietf-oauth-security-topics-13#section-3.4
   * Microsoft's documentation and recommendations are at:
   * https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-authentication-flows#usernamepassword
   *
   * @param request - UsenamePasswordRequest
   */


  async acquireTokenByUsernamePassword(request) {
    this.logger.info("acquireTokenByUsernamePassword called", request.correlationId);
    const validRequest = { ...request,
      ...(await this.initializeBaseRequest(request))
    };
    const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByUsernamePassword, validRequest.correlationId);

    try {
      const usernamePasswordClientConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, serverTelemetryManager, undefined, request.azureCloudOptions);
      const usernamePasswordClient = new msalCommon.UsernamePasswordClient(usernamePasswordClientConfig);
      this.logger.verbose("Username password client created", validRequest.correlationId);
      return usernamePasswordClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Gets the token cache for the application.
   */


  getTokenCache() {
    this.logger.info("getTokenCache called");
    return this.tokenCache;
  }
  /**
   * Validates OIDC state by comparing the user cached state with the state received from the server.
   *
   * This API is provided for scenarios where you would use OAuth2.0 state parameter to mitigate against
   * CSRF attacks.
   * For more information about state, visit https://datatracker.ietf.org/doc/html/rfc6819#section-3.6.
   * @param state
   * @param cachedState
   */


  validateState(state, cachedState) {
    if (!state) {
      throw NodeAuthError.createStateNotFoundError();
    }

    if (state !== cachedState) {
      throw msalCommon.ClientAuthError.createStateMismatchError();
    }
  }
  /**
   * Returns the logger instance
   */


  getLogger() {
    return this.logger;
  }
  /**
   * Replaces the default logger set in configurations with new Logger with new configurations
   * @param logger - Logger instance
   */


  setLogger(logger) {
    this.logger = logger;
  }
  /**
   * Builds the common configuration to be passed to the common component based on the platform configurarion
   * @param authority - user passed authority in configuration
   * @param serverTelemetryManager - initializes servertelemetry if passed
   */


  async buildOauthClientConfiguration(authority, requestCorrelationId, serverTelemetryManager, azureRegionConfiguration, azureCloudOptions) {
    this.logger.verbose("buildOauthClientConfiguration called", requestCorrelationId); // precedence - azureCloudInstance + tenant >> authority and request  >> config

    const userAzureCloudOptions = azureCloudOptions ? azureCloudOptions : this.config.auth.azureCloudOptions; // using null assertion operator as we ensure that all config values have default values in buildConfiguration()

    this.logger.verbose(`building oauth client configuration with the authority: ${authority}`, requestCorrelationId);
    const discoveredAuthority = await this.createAuthority(authority, azureRegionConfiguration, requestCorrelationId, userAzureCloudOptions);
    serverTelemetryManager == null ? void 0 : serverTelemetryManager.updateRegionDiscoveryMetadata(discoveredAuthority.regionDiscoveryMetadata);
    const clientConfiguration = {
      authOptions: {
        clientId: this.config.auth.clientId,
        authority: discoveredAuthority,
        clientCapabilities: this.config.auth.clientCapabilities
      },
      systemOptions: {
        proxyUrl: this.config.system.proxyUrl
      },
      loggerOptions: {
        logLevel: this.config.system.loggerOptions.logLevel,
        loggerCallback: this.config.system.loggerOptions.loggerCallback,
        piiLoggingEnabled: this.config.system.loggerOptions.piiLoggingEnabled,
        correlationId: requestCorrelationId
      },
      cryptoInterface: this.cryptoProvider,
      networkInterface: this.config.system.networkClient,
      storageInterface: this.storage,
      serverTelemetryManager: serverTelemetryManager,
      clientCredentials: {
        clientSecret: this.clientSecret,
        clientAssertion: this.clientAssertion ? this.getClientAssertion(discoveredAuthority) : undefined
      },
      libraryInfo: {
        sku: Constants.MSAL_SKU,
        version: version,
        cpu: process.arch || msalCommon.Constants.EMPTY_STRING,
        os: process.platform || msalCommon.Constants.EMPTY_STRING
      },
      telemetry: this.config.telemetry,
      persistencePlugin: this.config.cache.cachePlugin,
      serializableCache: this.tokenCache
    };
    return clientConfiguration;
  }

  getClientAssertion(authority) {
    return {
      assertion: this.clientAssertion.getJwt(this.cryptoProvider, this.config.auth.clientId, authority.tokenEndpoint),
      assertionType: Constants.JWT_BEARER_ASSERTION_TYPE
    };
  }
  /**
   * Generates a request with the default scopes & generates a correlationId.
   * @param authRequest - BaseAuthRequest for initialization
   */


  async initializeBaseRequest(authRequest) {
    this.logger.verbose("initializeRequestScopes called", authRequest.correlationId); // Default authenticationScheme to Bearer, log that POP isn't supported yet

    if (authRequest.authenticationScheme && authRequest.authenticationScheme === msalCommon.AuthenticationScheme.POP) {
      this.logger.verbose("Authentication Scheme 'pop' is not supported yet, setting Authentication Scheme to 'Bearer' for request", authRequest.correlationId);
    }

    authRequest.authenticationScheme = msalCommon.AuthenticationScheme.BEARER; // Set requested claims hash if claims were requested

    if (authRequest.claims && !msalCommon.StringUtils.isEmpty(authRequest.claims)) {
      authRequest.requestedClaimsHash = await this.cryptoProvider.hashString(authRequest.claims);
    }

    return { ...authRequest,
      scopes: [...(authRequest && authRequest.scopes || []), ...msalCommon.OIDC_DEFAULT_SCOPES],
      correlationId: authRequest && authRequest.correlationId || this.cryptoProvider.createNewGuid(),
      authority: authRequest.authority || this.config.auth.authority
    };
  }
  /**
   * Initializes the server telemetry payload
   * @param apiId - Id for a specific request
   * @param correlationId - GUID
   * @param forceRefresh - boolean to indicate network call
   */


  initializeServerTelemetryManager(apiId, correlationId, forceRefresh) {
    const telemetryPayload = {
      clientId: this.config.auth.clientId,
      correlationId: correlationId,
      apiId: apiId,
      forceRefresh: forceRefresh || false
    };
    return new msalCommon.ServerTelemetryManager(telemetryPayload, this.storage);
  }
  /**
   * Create authority instance. If authority not passed in request, default to authority set on the application
   * object. If no authority set in application object, then default to common authority.
   * @param authorityString - authority from user configuration
   */


  async createAuthority(authorityString, azureRegionConfiguration, requestCorrelationId, azureCloudOptions) {
    this.logger.verbose("createAuthority called", requestCorrelationId); // build authority string based on auth params - azureCloudInstance is prioritized if provided

    const authorityUrl = msalCommon.Authority.generateAuthority(authorityString, azureCloudOptions);
    const authorityOptions = {
      protocolMode: this.config.auth.protocolMode,
      knownAuthorities: this.config.auth.knownAuthorities,
      cloudDiscoveryMetadata: this.config.auth.cloudDiscoveryMetadata,
      authorityMetadata: this.config.auth.authorityMetadata,
      azureRegionConfiguration,
      skipAuthorityMetadataCache: this.config.auth.skipAuthorityMetadataCache
    };
    return await msalCommon.AuthorityFactory.createDiscoveredInstance(authorityUrl, this.config.system.networkClient, this.storage, authorityOptions, this.config.system.proxyUrl);
  }
  /**
   * Clear the cache
   */


  clearCache() {
    this.storage.clear();
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class LoopbackClient {
  /**
   * Spins up a loopback server which returns the server response when the localhost redirectUri is hit
   * @param successTemplate
   * @param errorTemplate
   * @returns
   */
  async listenForAuthCode(successTemplate, errorTemplate) {
    if (!!this.server) {
      throw NodeAuthError.createLoopbackServerAlreadyExistsError();
    }

    const authCodeListener = new Promise((resolve, reject) => {
      this.server = http.createServer(async (req, res) => {
        const url = req.url;

        if (!url) {
          res.end(errorTemplate || "Error occurred loading redirectUrl");
          reject(NodeAuthError.createUnableToLoadRedirectUrlError());
          return;
        } else if (url === msalCommon.Constants.FORWARD_SLASH) {
          res.end(successTemplate || "Auth code was successfully acquired. You can close this window now.");
          return;
        }

        const authCodeResponse = msalCommon.UrlString.getDeserializedQueryString(url);

        if (authCodeResponse.code) {
          const redirectUri = await this.getRedirectUri();
          res.writeHead(HttpStatus.REDIRECT, {
            location: redirectUri
          }); // Prevent auth code from being saved in the browser history

          res.end();
        }

        resolve(authCodeResponse);
      });
      this.server.listen(0); // Listen on any available port
    }); // Wait for server to be listening

    await new Promise(resolve => {
      let ticks = 0;
      const id = setInterval(() => {
        if (LOOPBACK_SERVER_CONSTANTS.TIMEOUT_MS / LOOPBACK_SERVER_CONSTANTS.INTERVAL_MS < ticks) {
          throw NodeAuthError.createLoopbackServerTimeoutError();
        }

        if (this.server.listening) {
          clearInterval(id);
          resolve();
        }

        ticks++;
      }, LOOPBACK_SERVER_CONSTANTS.INTERVAL_MS);
    });
    return authCodeListener;
  }
  /**
   * Get the port that the loopback server is running on
   * @returns
   */


  getRedirectUri() {
    if (!this.server) {
      throw NodeAuthError.createNoLoopbackServerExistsError();
    }

    const address = this.server.address();

    if (!address || typeof address === "string" || !address.port) {
      this.closeServer();
      throw NodeAuthError.createInvalidLoopbackAddressTypeError();
    }

    const port = address && address.port;
    return `${Constants.HTTP_PROTOCOL}${Constants.LOCALHOST}:${port}`;
  }
  /**
   * Close the loopback server
   */


  closeServer() {
    if (!!this.server) {
      this.server.close();
    }
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * This class is to be used to acquire tokens for public client applications (desktop, mobile). Public client applications
 * are not trusted to safely store application secrets, and therefore can only request tokens in the name of an user.
 * @public
 */

class PublicClientApplication extends ClientApplication {
  /**
   * Important attributes in the Configuration object for auth are:
   * - clientID: the application ID of your application. You can obtain one by registering your application with our Application registration portal.
   * - authority: the authority URL for your application.
   *
   * AAD authorities are of the form https://login.microsoftonline.com/\{Enter_the_Tenant_Info_Here\}.
   * - If your application supports Accounts in one organizational directory, replace "Enter_the_Tenant_Info_Here" value with the Tenant Id or Tenant name (for example, contoso.microsoft.com).
   * - If your application supports Accounts in any organizational directory, replace "Enter_the_Tenant_Info_Here" value with organizations.
   * - If your application supports Accounts in any organizational directory and personal Microsoft accounts, replace "Enter_the_Tenant_Info_Here" value with common.
   * - To restrict support to Personal Microsoft accounts only, replace "Enter_the_Tenant_Info_Here" value with consumers.
   *
   * Azure B2C authorities are of the form https://\{instance\}/\{tenant\}/\{policy\}. Each policy is considered
   * its own authority. You will have to set the all of the knownAuthorities at the time of the client application
   * construction.
   *
   * ADFS authorities are of the form https://\{instance\}/adfs.
   */
  constructor(configuration) {
    super(configuration);
  }
  /**
   * Acquires a token from the authority using OAuth2.0 device code flow.
   * This flow is designed for devices that do not have access to a browser or have input constraints.
   * The authorization server issues a DeviceCode object with a verification code, an end-user code,
   * and the end-user verification URI. The DeviceCode object is provided through a callback, and the end-user should be
   * instructed to use another device to navigate to the verification URI to input credentials.
   * Since the client cannot receive incoming requests, it polls the authorization server repeatedly
   * until the end-user completes input of credentials.
   */


  async acquireTokenByDeviceCode(request) {
    this.logger.info("acquireTokenByDeviceCode called", request.correlationId);
    const validRequest = Object.assign(request, await this.initializeBaseRequest(request));
    const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByDeviceCode, validRequest.correlationId);

    try {
      const deviceCodeConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, serverTelemetryManager, undefined, request.azureCloudOptions);
      const deviceCodeClient = new msalCommon.DeviceCodeClient(deviceCodeConfig);
      this.logger.verbose("Device code client created", validRequest.correlationId);
      return deviceCodeClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Acquires a token by requesting an Authorization code then exchanging it for a token.
   */


  async acquireTokenInteractive(request) {
    const {
      verifier,
      challenge
    } = await this.cryptoProvider.generatePkceCodes();
    const {
      openBrowser,
      successTemplate,
      errorTemplate,
      ...remainingProperties
    } = request;
    const loopbackClient = new LoopbackClient();
    const authCodeListener = loopbackClient.listenForAuthCode(successTemplate, errorTemplate);
    const redirectUri = loopbackClient.getRedirectUri();
    const validRequest = { ...remainingProperties,
      scopes: request.scopes || [],
      redirectUri: redirectUri,
      responseMode: msalCommon.ResponseMode.QUERY,
      codeChallenge: challenge,
      codeChallengeMethod: msalCommon.CodeChallengeMethodValues.S256
    };
    const authCodeUrl = await this.getAuthCodeUrl(validRequest);
    await openBrowser(authCodeUrl);
    const authCodeResponse = await authCodeListener.finally(() => {
      loopbackClient.closeServer();
    });

    if (authCodeResponse.error) {
      throw new msalCommon.ServerError(authCodeResponse.error, authCodeResponse.error_description, authCodeResponse.suberror);
    } else if (!authCodeResponse.code) {
      throw NodeAuthError.createNoAuthCodeInResponseError();
    }

    const clientInfo = authCodeResponse.client_info;
    const tokenRequest = {
      code: authCodeResponse.code,
      scopes: msalCommon.OIDC_DEFAULT_SCOPES,
      redirectUri: validRequest.redirectUri,
      codeVerifier: verifier,
      clientInfo: clientInfo || msalCommon.Constants.EMPTY_STRING
    };
    return this.acquireTokenByCode(tokenRequest);
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * Client assertion of type jwt-bearer used in confidential client flows
 * @public
 */

class ClientAssertion {
  /**
   * Initialize the ClientAssertion class from the clientAssertion passed by the user
   * @param assertion - refer https://tools.ietf.org/html/rfc7521
   */
  static fromAssertion(assertion) {
    const clientAssertion = new ClientAssertion();
    clientAssertion.jwt = assertion;
    return clientAssertion;
  }
  /**
   * Initialize the ClientAssertion class from the certificate passed by the user
   * @param thumbprint - identifier of a certificate
   * @param privateKey - secret key
   * @param publicCertificate - electronic document provided to prove the ownership of the public key
   */


  static fromCertificate(thumbprint, privateKey, publicCertificate) {
    const clientAssertion = new ClientAssertion();
    clientAssertion.privateKey = privateKey;
    clientAssertion.thumbprint = thumbprint;

    if (publicCertificate) {
      clientAssertion.publicCertificate = this.parseCertificate(publicCertificate);
    }

    return clientAssertion;
  }
  /**
   * Update JWT for certificate based clientAssertion, if passed by the user, uses it as is
   * @param cryptoProvider - library's crypto helper
   * @param issuer - iss claim
   * @param jwtAudience - aud claim
   */


  getJwt(cryptoProvider, issuer, jwtAudience) {
    // if assertion was created from certificate, check if jwt is expired and create new one.
    if (this.privateKey && this.thumbprint) {
      if (this.jwt && !this.isExpired() && issuer === this.issuer && jwtAudience === this.jwtAudience) {
        return this.jwt;
      }

      return this.createJwt(cryptoProvider, issuer, jwtAudience);
    }
    /*
     * if assertion was created by caller, then we just append it. It is up to the caller to
     * ensure that it contains necessary claims and that it is not expired.
     */


    if (this.jwt) {
      return this.jwt;
    }

    throw msalCommon.ClientAuthError.createInvalidAssertionError();
  }
  /**
   * JWT format and required claims specified: https://tools.ietf.org/html/rfc7523#section-3
   */


  createJwt(cryptoProvider, issuer, jwtAudience) {
    this.issuer = issuer;
    this.jwtAudience = jwtAudience;
    const issuedAt = msalCommon.TimeUtils.nowSeconds();
    this.expirationTime = issuedAt + 600;
    const header = {
      alg: JwtConstants.RSA_256,
      x5t: EncodingUtils.base64EncodeUrl(this.thumbprint, "hex")
    };

    if (this.publicCertificate) {
      Object.assign(header, {
        x5c: this.publicCertificate
      });
    }

    const payload = {
      [JwtConstants.AUDIENCE]: this.jwtAudience,
      [JwtConstants.EXPIRATION_TIME]: this.expirationTime,
      [JwtConstants.ISSUER]: this.issuer,
      [JwtConstants.SUBJECT]: this.issuer,
      [JwtConstants.NOT_BEFORE]: issuedAt,
      [JwtConstants.JWT_ID]: cryptoProvider.createNewGuid()
    };
    this.jwt = jsonwebtoken.sign(payload, this.privateKey, {
      header
    });
    return this.jwt;
  }
  /**
   * Utility API to check expiration
   */


  isExpired() {
    return this.expirationTime < msalCommon.TimeUtils.nowSeconds();
  }
  /**
   * Extracts the raw certs from a given certificate string and returns them in an array.
   * @param publicCertificate - electronic document provided to prove the ownership of the public key
   */


  static parseCertificate(publicCertificate) {
    /**
     * This is regex to identify the certs in a given certificate string.
     * We want to look for the contents between the BEGIN and END certificate strings, without the associated newlines.
     * The information in parens "(.+?)" is the capture group to represent the cert we want isolated.
     * "." means any string character, "+" means match 1 or more times, and "?" means the shortest match.
     * The "g" at the end of the regex means search the string globally, and the "s" enables the "." to match newlines.
     */
    const regexToFindCerts = /-----BEGIN CERTIFICATE-----\r*\n(.+?)\r*\n-----END CERTIFICATE-----/gs;
    const certs = [];
    let matches;

    while ((matches = regexToFindCerts.exec(publicCertificate)) !== null) {
      // matches[1] represents the first parens capture group in the regex.
      certs.push(matches[1].replace(/\r*\n/g, msalCommon.Constants.EMPTY_STRING));
    }

    return certs;
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 *  This class is to be used to acquire tokens for confidential client applications (webApp, webAPI). Confidential client applications
 *  will configure application secrets, client certificates/assertions as applicable
 * @public
 */

class ConfidentialClientApplication extends ClientApplication {
  /**
   * Constructor for the ConfidentialClientApplication
   *
   * Required attributes in the Configuration object are:
   * - clientID: the application ID of your application. You can obtain one by registering your application with our application registration portal
   * - authority: the authority URL for your application.
   * - client credential: Must set either client secret, certificate, or assertion for confidential clients. You can obtain a client secret from the application registration portal.
   *
   * In Azure AD, authority is a URL indicating of the form https://login.microsoftonline.com/\{Enter_the_Tenant_Info_Here\}.
   * If your application supports Accounts in one organizational directory, replace "Enter_the_Tenant_Info_Here" value with the Tenant Id or Tenant name (for example, contoso.microsoft.com).
   * If your application supports Accounts in any organizational directory, replace "Enter_the_Tenant_Info_Here" value with organizations.
   * If your application supports Accounts in any organizational directory and personal Microsoft accounts, replace "Enter_the_Tenant_Info_Here" value with common.
   * To restrict support to Personal Microsoft accounts only, replace "Enter_the_Tenant_Info_Here" value with consumers.
   *
   * In Azure B2C, authority is of the form https://\{instance\}/tfp/\{tenant\}/\{policyName\}/
   * Full B2C functionality will be available in this library in future versions.
   *
   * @param Configuration - configuration object for the MSAL ConfidentialClientApplication instance
   */
  constructor(configuration) {
    super(configuration);
    this.setClientCredential(this.config);
    this.appTokenProvider = undefined;
  }
  /**
   * This extensibility point only works for the client_credential flow, i.e. acquireTokenByClientCredential and
   * is meant for Azure SDK to enhance Managed Identity support.
   *
   * @param IAppTokenProvider  - Extensibility interface, which allows the app developer to return a token from a custom source.
   */


  SetAppTokenProvider(provider) {
    this.appTokenProvider = provider;
  }
  /**
   * Acquires tokens from the authority for the application (not for an end user).
   */


  async acquireTokenByClientCredential(request) {
    this.logger.info("acquireTokenByClientCredential called", request.correlationId); // If there is a client assertion present in the request, it overrides the one present in the client configuration

    let clientAssertion;

    if (request.clientAssertion) {
      clientAssertion = {
        assertion: request.clientAssertion,
        assertionType: Constants.JWT_BEARER_ASSERTION_TYPE
      };
    }

    const baseRequest = await this.initializeBaseRequest(request); // valid base request should not contain oidc scopes in this grant type

    const validBaseRequest = { ...baseRequest,
      scopes: baseRequest.scopes.filter(scope => !msalCommon.OIDC_DEFAULT_SCOPES.includes(scope))
    };
    const validRequest = { ...request,
      ...validBaseRequest,
      clientAssertion
    };
    const azureRegionConfiguration = {
      azureRegion: validRequest.azureRegion,
      environmentRegion: process.env[REGION_ENVIRONMENT_VARIABLE]
    };
    const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByClientCredential, validRequest.correlationId, validRequest.skipCache);

    try {
      const clientCredentialConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, serverTelemetryManager, azureRegionConfiguration, request.azureCloudOptions);
      const clientCredentialClient = new msalCommon.ClientCredentialClient(clientCredentialConfig, this.appTokenProvider);
      this.logger.verbose("Client credential client created", validRequest.correlationId);
      return clientCredentialClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Acquires tokens from the authority for the application.
   *
   * Used in scenarios where the current app is a middle-tier service which was called with a token
   * representing an end user. The current app can use the token (oboAssertion) to request another
   * token to access downstream web API, on behalf of that user.
   *
   * The current middle-tier app has no user interaction to obtain consent.
   * See how to gain consent upfront for your middle-tier app from this article.
   * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#gaining-consent-for-the-middle-tier-application
   */


  async acquireTokenOnBehalfOf(request) {
    this.logger.info("acquireTokenOnBehalfOf called", request.correlationId);
    const validRequest = { ...request,
      ...(await this.initializeBaseRequest(request))
    };

    try {
      const onBehalfOfConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, undefined, undefined, request.azureCloudOptions);
      const oboClient = new msalCommon.OnBehalfOfClient(onBehalfOfConfig);
      this.logger.verbose("On behalf of client created", validRequest.correlationId);
      return oboClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof msalCommon.AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }

      throw e;
    }
  }

  setClientCredential(configuration) {
    const clientSecretNotEmpty = !msalCommon.StringUtils.isEmpty(configuration.auth.clientSecret);
    const clientAssertionNotEmpty = !msalCommon.StringUtils.isEmpty(configuration.auth.clientAssertion);
    const certificate = configuration.auth.clientCertificate || {
      thumbprint: msalCommon.Constants.EMPTY_STRING,
      privateKey: msalCommon.Constants.EMPTY_STRING
    };
    const certificateNotEmpty = !msalCommon.StringUtils.isEmpty(certificate.thumbprint) || !msalCommon.StringUtils.isEmpty(certificate.privateKey);
    /*
     * If app developer configures this callback, they don't need a credential
     * i.e. AzureSDK can get token from Managed Identity without a cert / secret
     */

    if (this.appTokenProvider) {
      return;
    } // Check that at most one credential is set on the application


    if (clientSecretNotEmpty && clientAssertionNotEmpty || clientAssertionNotEmpty && certificateNotEmpty || clientSecretNotEmpty && certificateNotEmpty) {
      throw msalCommon.ClientAuthError.createInvalidCredentialError();
    }

    if (configuration.auth.clientSecret) {
      this.clientSecret = configuration.auth.clientSecret;
      return;
    }

    if (configuration.auth.clientAssertion) {
      this.clientAssertion = ClientAssertion.fromAssertion(configuration.auth.clientAssertion);
      return;
    }

    if (!certificateNotEmpty) {
      throw msalCommon.ClientAuthError.createInvalidCredentialError();
    } else {
      var _configuration$auth$c;

      this.clientAssertion = ClientAssertion.fromCertificate(certificate.thumbprint, certificate.privateKey, (_configuration$auth$c = configuration.auth.clientCertificate) == null ? void 0 : _configuration$auth$c.x5c);
    }
  }

}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
class DistributedCachePlugin {
  constructor(client, partitionManager) {
    this.client = client;
    this.partitionManager = partitionManager;
  }

  async beforeCacheAccess(cacheContext) {
    const partitionKey = await this.partitionManager.getKey();
    const cacheData = await this.client.get(partitionKey);
    cacheContext.tokenCache.deserialize(cacheData);
  }

  async afterCacheAccess(cacheContext) {
    if (cacheContext.cacheHasChanged) {
      const kvStore = cacheContext.tokenCache.getKVStore();
      const accountEntities = Object.values(kvStore).filter(value => msalCommon.AccountEntity.isAccountEntity(value));

      if (accountEntities.length > 0) {
        const accountEntity = accountEntities[0];
        const partitionKey = await this.partitionManager.extractKey(accountEntity);
        await this.client.set(partitionKey, cacheContext.tokenCache.serialize());
      }
    }
  }

}

Object.defineProperty(exports, 'AuthError', {
    enumerable: true,
    get: function () {
        return msalCommon.AuthError;
    }
});
Object.defineProperty(exports, 'AuthErrorMessage', {
    enumerable: true,
    get: function () {
        return msalCommon.AuthErrorMessage;
    }
});
Object.defineProperty(exports, 'AzureCloudInstance', {
    enumerable: true,
    get: function () {
        return msalCommon.AzureCloudInstance;
    }
});
Object.defineProperty(exports, 'ClientAuthError', {
    enumerable: true,
    get: function () {
        return msalCommon.ClientAuthError;
    }
});
Object.defineProperty(exports, 'ClientAuthErrorMessage', {
    enumerable: true,
    get: function () {
        return msalCommon.ClientAuthErrorMessage;
    }
});
Object.defineProperty(exports, 'ClientConfigurationError', {
    enumerable: true,
    get: function () {
        return msalCommon.ClientConfigurationError;
    }
});
Object.defineProperty(exports, 'ClientConfigurationErrorMessage', {
    enumerable: true,
    get: function () {
        return msalCommon.ClientConfigurationErrorMessage;
    }
});
Object.defineProperty(exports, 'InteractionRequiredAuthError', {
    enumerable: true,
    get: function () {
        return msalCommon.InteractionRequiredAuthError;
    }
});
Object.defineProperty(exports, 'InteractionRequiredAuthErrorMessage', {
    enumerable: true,
    get: function () {
        return msalCommon.InteractionRequiredAuthErrorMessage;
    }
});
Object.defineProperty(exports, 'LogLevel', {
    enumerable: true,
    get: function () {
        return msalCommon.LogLevel;
    }
});
Object.defineProperty(exports, 'Logger', {
    enumerable: true,
    get: function () {
        return msalCommon.Logger;
    }
});
Object.defineProperty(exports, 'PromptValue', {
    enumerable: true,
    get: function () {
        return msalCommon.PromptValue;
    }
});
Object.defineProperty(exports, 'ProtocolMode', {
    enumerable: true,
    get: function () {
        return msalCommon.ProtocolMode;
    }
});
Object.defineProperty(exports, 'ResponseMode', {
    enumerable: true,
    get: function () {
        return msalCommon.ResponseMode;
    }
});
Object.defineProperty(exports, 'ServerError', {
    enumerable: true,
    get: function () {
        return msalCommon.ServerError;
    }
});
Object.defineProperty(exports, 'TokenCacheContext', {
    enumerable: true,
    get: function () {
        return msalCommon.TokenCacheContext;
    }
});
exports.ClientApplication = ClientApplication;
exports.ClientAssertion = ClientAssertion;
exports.ConfidentialClientApplication = ConfidentialClientApplication;
exports.CryptoProvider = CryptoProvider;
exports.DistributedCachePlugin = DistributedCachePlugin;
exports.NodeStorage = NodeStorage;
exports.PublicClientApplication = PublicClientApplication;
exports.TokenCache = TokenCache;
exports.buildAppConfiguration = buildAppConfiguration;
exports.version = version;
//# sourceMappingURL=msal-node.cjs.development.js.map
