import { Constants as Constants$1, ProtocolMode, AzureCloudInstance, LogLevel, StringUtils, AccountEntity, CacheManager, IdTokenEntity, AccessTokenEntity, RefreshTokenEntity, AppMetadataEntity, ServerTelemetryEntity, AuthorityMetadataEntity, ThrottlingEntity, TokenCacheContext, AuthError, Logger, ResponseMode, AuthenticationScheme, AuthorizationCodeClient, RefreshTokenClient, SilentFlowClient, UsernamePasswordClient, ClientAuthError, OIDC_DEFAULT_SCOPES, ServerTelemetryManager, Authority, AuthorityFactory, UrlString, DeviceCodeClient, ServerError, CodeChallengeMethodValues, TimeUtils, ClientCredentialClient, OnBehalfOfClient } from '@azure/msal-common';
export { AuthError, AuthErrorMessage, AzureCloudInstance, ClientAuthError, ClientAuthErrorMessage, ClientConfigurationError, ClientConfigurationErrorMessage, InteractionRequiredAuthError, InteractionRequiredAuthErrorMessage, LogLevel, Logger, PromptValue, ProtocolMode, ResponseMode, ServerError, TokenCacheContext } from '@azure/msal-common';
import http, { createServer } from 'http';
import https from 'https';
import { v4 } from 'uuid';
import crypto from 'crypto';
import { sign } from '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["SUCCESS_RANGE_START"] = 200] = "SUCCESS_RANGE_START";
  HttpStatus[HttpStatus["SUCCESS_RANGE_END"] = 299] = "SUCCESS_RANGE_END";
  HttpStatus[HttpStatus["REDIRECT"] = 302] = "REDIRECT";
  HttpStatus[HttpStatus["CLIENT_ERROR_RANGE_START"] = 400] = "CLIENT_ERROR_RANGE_START";
  HttpStatus[HttpStatus["CLIENT_ERROR_RANGE_END"] = 499] = "CLIENT_ERROR_RANGE_END";
  HttpStatus[HttpStatus["SERVER_ERROR_RANGE_START"] = 500] = "SERVER_ERROR_RANGE_START";
  HttpStatus[HttpStatus["SERVER_ERROR_RANGE_END"] = 599] = "SERVER_ERROR_RANGE_END";
})(HttpStatus || (HttpStatus = {}));
var ProxyStatus;
(function (ProxyStatus) {
  ProxyStatus[ProxyStatus["SUCCESS_RANGE_START"] = 200] = "SUCCESS_RANGE_START";
  ProxyStatus[ProxyStatus["SUCCESS_RANGE_END"] = 299] = "SUCCESS_RANGE_END";
  ProxyStatus[ProxyStatus["SERVER_ERROR"] = 500] = "SERVER_ERROR";
})(ProxyStatus || (ProxyStatus = {}));
/**
 * 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.
 */
class NetworkUtils {
  static getNetworkResponse(headers, body, statusCode) {
    return {
      headers: headers,
      body: body,
      status: statusCode
    };
  }
  /*
   * Utility function that converts a URL object into an ordinary options object as expected by the
   * http.request and https.request APIs.
   * https://github.com/nodejs/node/blob/main/lib/internal/url.js#L1090
   */
  static urlToHttpOptions(url) {
    const options = {
      protocol: url.protocol,
      hostname: url.hostname && url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname,
      hash: url.hash,
      search: url.search,
      pathname: url.pathname,
      path: `${url.pathname || ""}${url.search || ""}`,
      href: url.href
    };
    if (url.port !== "") {
      options.port = Number(url.port);
    }
    if (url.username || url.password) {
      options.auth = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`;
    }
    return options;
  }
}

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * This class implements the API for network requests.
 */
class HttpClient {
  constructor(proxyUrl, customAgentOptions) {
    this.proxyUrl = proxyUrl || "";
    this.customAgentOptions = customAgentOptions || {};
  }
  /**
   * Http Get request
   * @param url
   * @param options
   */
  async sendGetRequestAsync(url, options) {
    if (this.proxyUrl) {
      return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.GET, options, this.customAgentOptions);
    } else {
      return networkRequestViaHttps(url, HttpMethod.GET, options, this.customAgentOptions);
    }
  }
  /**
   * Http Post request
   * @param url
   * @param options
   */
  async sendPostRequestAsync(url, options, cancellationToken) {
    if (this.proxyUrl) {
      return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.POST, options, this.customAgentOptions, cancellationToken);
    } else {
      return networkRequestViaHttps(url, HttpMethod.POST, options, this.customAgentOptions, cancellationToken);
    }
  }
}
const networkRequestViaProxy = (destinationUrlString, proxyUrlString, httpMethod, options, agentOptions, timeout) => {
  const destinationUrl = new URL(destinationUrlString);
  const proxyUrl = new URL(proxyUrlString);
  // "method: connect" must be used to establish a connection to the proxy
  const headers = (options == null ? void 0 : options.headers) || {};
  const tunnelRequestOptions = {
    host: proxyUrl.hostname,
    port: proxyUrl.port,
    method: "CONNECT",
    path: destinationUrl.hostname,
    headers: headers
  };
  if (timeout) {
    tunnelRequestOptions.timeout = timeout;
  }
  if (agentOptions && Object.keys(agentOptions).length) {
    tunnelRequestOptions.agent = new http.Agent(agentOptions);
  }
  // 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.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 proxyStatusCode = (response == null ? void 0 : response.statusCode) || ProxyStatus.SERVER_ERROR;
      if (proxyStatusCode < ProxyStatus.SUCCESS_RANGE_START || proxyStatusCode > ProxyStatus.SUCCESS_RANGE_END) {
        request.destroy();
        socket.destroy();
        reject(new Error(`Error connecting to proxy. Http status code: ${response.statusCode}. Http status message: ${(response == null ? void 0 : response.statusMessage) || "Unknown"}`));
      }
      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 and statusMessage
        const httpStatusCode = parseInt(dataStringArray[0].split(" ")[1]);
        // remove "HTTP/1.1" and the status code to get the status message
        const statusMessage = dataStringArray[0].split(" ").slice(2).join(" ");
        // 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 parsedHeaders = headers;
        const networkResponse = NetworkUtils.getNetworkResponse(parsedHeaders, parseBody(httpStatusCode, statusMessage, parsedHeaders, body), httpStatusCode);
        if ((httpStatusCode < HttpStatus.SUCCESS_RANGE_START || httpStatusCode > HttpStatus.SUCCESS_RANGE_END) &&
        // 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 = (urlString, httpMethod, options, agentOptions, timeout) => {
  const isPostRequest = httpMethod === HttpMethod.POST;
  const body = (options == null ? void 0 : options.body) || "";
  const url = new URL(urlString);
  const headers = (options == null ? void 0 : options.headers) || {};
  const customOptions = {
    method: httpMethod,
    headers: headers,
    ...NetworkUtils.urlToHttpOptions(url)
  };
  if (timeout) {
    customOptions.timeout = timeout;
  }
  if (agentOptions && Object.keys(agentOptions).length) {
    customOptions.agent = new https.Agent(agentOptions);
  }
  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(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 statusMessage = response.statusMessage;
      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 parsedHeaders = headers;
        const networkResponse = NetworkUtils.getNetworkResponse(parsedHeaders, parseBody(statusCode, statusMessage, parsedHeaders, body), statusCode);
        if ((statusCode < HttpStatus.SUCCESS_RANGE_START || statusCode > HttpStatus.SUCCESS_RANGE_END) &&
        // 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()));
    });
  });
};
/**
 * Check if extra parsing is needed on the repsonse from the server
 * @param statusCode {number} the status code of the response from the server
 * @param statusMessage {string | undefined} the status message of the response from the server
 * @param headers {Record<string, string>} the headers of the response from the server
 * @param body {string} the body from the response of the server
 * @returns {Object} JSON parsed body or error object
 */
const parseBody = (statusCode, statusMessage, headers, body) => {
  /*
   * Informational responses (100 – 199)
   * Successful responses (200 – 299)
   * Redirection messages (300 – 399)
   * Client error responses (400 – 499)
   * Server error responses (500 – 599)
   */
  let parsedBody;
  try {
    parsedBody = JSON.parse(body);
  } catch (error) {
    let errorType;
    let errorDescriptionHelper;
    if (statusCode >= HttpStatus.CLIENT_ERROR_RANGE_START && statusCode <= HttpStatus.CLIENT_ERROR_RANGE_END) {
      errorType = "client_error";
      errorDescriptionHelper = "A client";
    } else if (statusCode >= HttpStatus.SERVER_ERROR_RANGE_START && statusCode <= HttpStatus.SERVER_ERROR_RANGE_END) {
      errorType = "server_error";
      errorDescriptionHelper = "A server";
    } else {
      errorType = "unknown_error";
      errorDescriptionHelper = "An unknown";
    }
    parsedBody = {
      error: errorType,
      error_description: `${errorDescriptionHelper} error occured.\nHttp status code: ${statusCode}\nHttp status message: ${statusMessage || "Unknown"}\nHeaders: ${JSON.stringify(headers)}`
    };
  }
  return parsedBody;
};

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
const DEFAULT_AUTH_OPTIONS = {
  clientId: Constants$1.EMPTY_STRING,
  authority: Constants$1.DEFAULT_AUTHORITY,
  clientSecret: Constants$1.EMPTY_STRING,
  clientAssertion: Constants$1.EMPTY_STRING,
  clientCertificate: {
    thumbprint: Constants$1.EMPTY_STRING,
    privateKey: Constants$1.EMPTY_STRING,
    x5c: Constants$1.EMPTY_STRING
  },
  knownAuthorities: [],
  cloudDiscoveryMetadata: Constants$1.EMPTY_STRING,
  authorityMetadata: Constants$1.EMPTY_STRING,
  clientCapabilities: [],
  protocolMode: ProtocolMode.AAD,
  azureCloudOptions: {
    azureCloudInstance: AzureCloudInstance.None,
    tenant: Constants$1.EMPTY_STRING
  },
  skipAuthorityMetadataCache: false
};
const DEFAULT_CACHE_OPTIONS = {};
const DEFAULT_LOGGER_OPTIONS = {
  loggerCallback: () => {
    // allow users to not set logger call back 
  },
  piiLoggingEnabled: false,
  logLevel: LogLevel.Info
};
const DEFAULT_SYSTEM_OPTIONS = {
  loggerOptions: DEFAULT_LOGGER_OPTIONS,
  networkClient: /*#__PURE__*/new HttpClient(),
  proxyUrl: Constants$1.EMPTY_STRING,
  customAgentOptions: {}
};
const DEFAULT_TELEMETRY_OPTIONS = {
  application: {
    appName: Constants$1.EMPTY_STRING,
    appVersion: Constants$1.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,
  broker,
  cache,
  system,
  telemetry
}) {
  const systemOptions = {
    ...DEFAULT_SYSTEM_OPTIONS,
    networkClient: new HttpClient(system == null ? void 0 : system.proxyUrl, system == null ? void 0 : system.customAgentOptions),
    loggerOptions: (system == null ? void 0 : system.loggerOptions) || DEFAULT_LOGGER_OPTIONS
  };
  return {
    auth: {
      ...DEFAULT_AUTH_OPTIONS,
      ...auth
    },
    broker: {
      ...broker
    },
    cache: {
      ...DEFAULT_CACHE_OPTIONS,
      ...cache
    },
    system: {
      ...systemOptions,
      ...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 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, Constants$1.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(Constants$1.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 = 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 AccountEntity();
        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 IdTokenEntity();
        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,
          userAssertionHash: serializedAT.userAssertionHash
        };
        const accessToken = new AccessTokenEntity();
        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 RefreshTokenEntity();
        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 AppMetadataEntity();
        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,
        userAssertionHash: atEntity.userAssertionHash
      };
    });
    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 CacheManager {
  constructor(logger, clientId, cryptoImpl) {
    super(clientId, cryptoImpl, logger);
    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 AccountEntity) {
        inMemoryCache.accounts[key] = cache[key];
      } else if (cache[key] instanceof IdTokenEntity) {
        inMemoryCache.idTokens[key] = cache[key];
      } else if (cache[key] instanceof AccessTokenEntity) {
        inMemoryCache.accessTokens[key] = cache[key];
      } else if (cache[key] instanceof RefreshTokenEntity) {
        inMemoryCache.refreshTokens[key] = cache[key];
      } else if (cache[key] instanceof 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);
  }
  getAccountKeys() {
    const inMemoryCache = this.getInMemoryCache();
    const accountKeys = Object.keys(inMemoryCache.accounts);
    return accountKeys;
  }
  getTokenKeys() {
    const inMemoryCache = this.getInMemoryCache();
    const tokenKeys = {
      idToken: Object.keys(inMemoryCache.idTokens),
      accessToken: Object.keys(inMemoryCache.accessTokens),
      refreshToken: Object.keys(inMemoryCache.refreshTokens)
    };
    return tokenKeys;
  }
  /**
   * fetch the account entity
   * @param accountKey - lookup key to fetch cache type AccountEntity
   */
  getAccount(accountKey) {
    const account = this.getItem(accountKey);
    if (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 (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 (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 (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 (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 && 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 && 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 && 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 (!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 (!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 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 (!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 (!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 TokenCacheContext(this, true);
        await this.persistence.beforeCacheAccess(cacheContext);
      }
      await this.storage.removeAccount(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.17.0";

/*
 * 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 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 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 || ResponseMode.QUERY,
      authenticationScheme: AuthenticationScheme.BEARER
    };
    const authClientConfig = await this.buildOauthClientConfiguration(validRequest.authority, validRequest.correlationId, undefined, undefined, request.azureCloudOptions);
    const authorizationCodeClient = new 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: 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 AuthorizationCodeClient(authClientConfig);
      this.logger.verbose("Auth code client created", validRequest.correlationId);
      return authorizationCodeClient.acquireToken(validRequest, authCodePayLoad);
    } catch (e) {
      if (e instanceof 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: 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 RefreshTokenClient(refreshTokenClientConfig);
      this.logger.verbose("Refresh token client created", validRequest.correlationId);
      return refreshTokenClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof 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 SilentFlowClient(silentFlowClientConfig);
      this.logger.verbose("Silent flow client created", validRequest.correlationId);
      return silentFlowClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof 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 UsernamePasswordClient(usernamePasswordClientConfig);
      this.logger.verbose("Username password client created", validRequest.correlationId);
      return usernamePasswordClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof 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 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
      },
      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 || Constants$1.EMPTY_STRING,
        os: process.platform || Constants$1.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 === AuthenticationScheme.POP) {
      this.logger.verbose("Authentication Scheme 'pop' is not supported yet, setting Authentication Scheme to 'Bearer' for request", authRequest.correlationId);
    }
    authRequest.authenticationScheme = AuthenticationScheme.BEARER;
    // Set requested claims hash if claims were requested
    if (authRequest.claims && !StringUtils.isEmpty(authRequest.claims)) {
      authRequest.requestedClaimsHash = await this.cryptoProvider.hashString(authRequest.claims);
    }
    return {
      ...authRequest,
      scopes: [...(authRequest && authRequest.scopes || []), ...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 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 = 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 AuthorityFactory.createDiscoveredInstance(authorityUrl, this.config.system.networkClient, this.storage, authorityOptions, this.logger);
  }
  /**
   * 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 = createServer(async (req, res) => {
        const url = req.url;
        if (!url) {
          res.end(errorTemplate || "Error occurred loading redirectUrl");
          reject(NodeAuthError.createUnableToLoadRedirectUrlError());
          return;
        } else if (url === Constants$1.FORWARD_SLASH) {
          res.end(successTemplate || "Auth code was successfully acquired. You can close this window now.");
          return;
        }
        const authCodeResponse = 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);
    if (this.config.broker.nativeBrokerPlugin) {
      if (this.config.broker.nativeBrokerPlugin.isBrokerAvailable) {
        this.nativeBrokerPlugin = this.config.broker.nativeBrokerPlugin;
        this.nativeBrokerPlugin.setLogger(this.config.system.loggerOptions);
      } else {
        this.logger.warning("NativeBroker implementation was provided but the broker is unavailable.");
      }
    }
  }
  /**
   * 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 DeviceCodeClient(deviceCodeConfig);
      this.logger.verbose("Device code client created", validRequest.correlationId);
      return deviceCodeClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }
      serverTelemetryManager.cacheFailedRequest(e);
      throw e;
    }
  }
  /**
   * Acquires a token interactively via the browser by requesting an authorization code then exchanging it for a token.
   */
  async acquireTokenInteractive(request) {
    const correlationId = request.correlationId || this.cryptoProvider.createNewGuid();
    this.logger.trace("acquireTokenInteractive called", correlationId);
    const {
      openBrowser,
      successTemplate,
      errorTemplate,
      windowHandle,
      loopbackClient: customLoopbackClient,
      ...remainingProperties
    } = request;
    if (this.nativeBrokerPlugin) {
      var _remainingProperties$;
      const brokerRequest = {
        ...remainingProperties,
        clientId: this.config.auth.clientId,
        scopes: request.scopes || OIDC_DEFAULT_SCOPES,
        redirectUri: `${Constants.HTTP_PROTOCOL}${Constants.LOCALHOST}`,
        authority: request.authority || this.config.auth.authority,
        correlationId: correlationId,
        extraParameters: {
          ...remainingProperties.extraQueryParameters,
          ...remainingProperties.tokenQueryParameters
        },
        accountId: (_remainingProperties$ = remainingProperties.account) == null ? void 0 : _remainingProperties$.nativeAccountId
      };
      return this.nativeBrokerPlugin.acquireTokenInteractive(brokerRequest, windowHandle);
    }
    const {
      verifier,
      challenge
    } = await this.cryptoProvider.generatePkceCodes();
    const loopbackClient = customLoopbackClient || new LoopbackClient();
    const authCodeListener = loopbackClient.listenForAuthCode(successTemplate, errorTemplate);
    const redirectUri = loopbackClient.getRedirectUri();
    const validRequest = {
      ...remainingProperties,
      correlationId: correlationId,
      scopes: request.scopes || OIDC_DEFAULT_SCOPES,
      redirectUri: redirectUri,
      responseMode: ResponseMode.QUERY,
      codeChallenge: challenge,
      codeChallengeMethod: CodeChallengeMethodValues.S256
    };
    const authCodeUrl = await this.getAuthCodeUrl(validRequest);
    await openBrowser(authCodeUrl);
    const authCodeResponse = await authCodeListener.finally(() => {
      loopbackClient.closeServer();
    });
    if (authCodeResponse.error) {
      throw new 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,
      codeVerifier: verifier,
      clientInfo: clientInfo || Constants$1.EMPTY_STRING,
      ...validRequest
    };
    return this.acquireTokenByCode(tokenRequest);
  }
  /**
   * Returns a token retrieved either from the cache or by exchanging the refresh token for a fresh access token. If brokering is enabled the token request will be serviced by the broker.
   * @param request
   * @returns
   */
  async acquireTokenSilent(request) {
    const correlationId = request.correlationId || this.cryptoProvider.createNewGuid();
    this.logger.trace("acquireTokenSilent called", correlationId);
    if (this.nativeBrokerPlugin) {
      const brokerRequest = {
        ...request,
        clientId: this.config.auth.clientId,
        scopes: request.scopes || OIDC_DEFAULT_SCOPES,
        redirectUri: `${Constants.HTTP_PROTOCOL}${Constants.LOCALHOST}`,
        authority: request.authority || this.config.auth.authority,
        correlationId: correlationId,
        extraParameters: request.tokenQueryParameters,
        accountId: request.account.nativeAccountId,
        forceRefresh: request.forceRefresh || false
      };
      return this.nativeBrokerPlugin.acquireTokenSilent(brokerRequest);
    }
    return super.acquireTokenSilent(request);
  }
  /**
   * Removes cache artifacts associated with the given account
   * @param request
   * @returns
   */
  async signOut(request) {
    if (this.nativeBrokerPlugin && request.account.nativeAccountId) {
      const signoutRequest = {
        clientId: this.config.auth.clientId,
        accountId: request.account.nativeAccountId,
        correlationId: request.correlationId || this.cryptoProvider.createNewGuid()
      };
      await this.nativeBrokerPlugin.signOut(signoutRequest);
    }
    await this.getTokenCache().removeAccount(request.account);
  }
  /**
   * Returns all cached accounts for this application. If brokering is enabled this request will be serviced by the broker.
   * @returns
   */
  async getAllAccounts() {
    if (this.nativeBrokerPlugin) {
      const correlationId = this.cryptoProvider.createNewGuid();
      return this.nativeBrokerPlugin.getAllAccounts(this.config.auth.clientId, correlationId);
    }
    return this.getTokenCache().getAllAccounts();
  }
}

/*
 * 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 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 = 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 = sign(payload, this.privateKey, {
      header
    });
    return this.jwt;
  }
  /**
   * Utility API to check expiration
   */
  isExpired() {
    return this.expirationTime < 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, Constants$1.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 => !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 ClientCredentialClient(clientCredentialConfig, this.appTokenProvider);
      this.logger.verbose("Client credential client created", validRequest.correlationId);
      return clientCredentialClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof 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 OnBehalfOfClient(onBehalfOfConfig);
      this.logger.verbose("On behalf of client created", validRequest.correlationId);
      return oboClient.acquireToken(validRequest);
    } catch (e) {
      if (e instanceof AuthError) {
        e.setCorrelationId(validRequest.correlationId);
      }
      throw e;
    }
  }
  setClientCredential(configuration) {
    const clientSecretNotEmpty = !StringUtils.isEmpty(configuration.auth.clientSecret);
    const clientAssertionNotEmpty = !StringUtils.isEmpty(configuration.auth.clientAssertion);
    const certificate = configuration.auth.clientCertificate || {
      thumbprint: Constants$1.EMPTY_STRING,
      privateKey: Constants$1.EMPTY_STRING
    };
    const certificateNotEmpty = !StringUtils.isEmpty(certificate.thumbprint) || !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 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 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 => 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());
      }
    }
  }
}

export { ClientApplication, ClientAssertion, ConfidentialClientApplication, CryptoProvider, DistributedCachePlugin, NodeStorage, PublicClientApplication, TokenCache, buildAppConfiguration, version };
//# sourceMappingURL=msal-node.esm.js.map
