UNPKG

@azure/msal-node-extensions

Version:

![npm (scoped)](https://img.shields.io/npm/v/@azure/msal-node-extensions) ![npm](https://img.shields.io/npm/dw/@azure/msal-node-extensions)

1,192 lines (1,168 loc) 92.2 kB
/*! @azure/msal-node-extensions v1.5.9 2025-03-25 */ 'use strict'; 'use strict'; var fs = require('fs'); var process$1 = require('process'); var path = require('path'); var module$1 = require('module'); var keytar = require('keytar'); var msalNodeRuntime = require('@azure/msal-node-runtime'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const Constants$1 = { /** * An existing file was the target of an operation that required that the target not exist */ EEXIST_ERROR: "EEXIST", /** * No such file or directory: Commonly raised by fs operations to indicate that a component * of the specified pathname does not exist. No entity (file or directory) could be found * by the given path */ ENOENT_ERROR: "ENOENT", /** * Operation not permitted. An attempt was made to perform an operation that requires * elevated privileges. */ EPERM_ERROR: "EPERM", /** * Default service name for using MSAL Keytar */ DEFAULT_SERVICE_NAME: "msal-node-extensions", /** * Test data used to verify underlying persistence mechanism */ PERSISTENCE_TEST_DATA: "Dummy data to verify underlying persistence mechanism", /** * This is the value of a the guid if the process is being ran by the root user */ LINUX_ROOT_USER_GUID: 0, /** * List of environment variables */ ENVIRONMENT: { HOME: "HOME", LOGNAME: "LOGNAME", USER: "USER", LNAME: "LNAME", USERNAME: "USERNAME", PLATFORM: "platform", LOCAL_APPLICATION_DATA: "LOCALAPPDATA", }, // Name of the default cache file DEFAULT_CACHE_FILE_NAME: "cache.json", }; const Platform = { WINDOWS: "win32", LINUX: "linux", MACOS: "darwin", }; const ErrorCodes = { INTERATION_REQUIRED_ERROR_CODE: "interaction_required", SERVER_UNAVAILABLE: "server_unavailable", UNKNOWN: "unknown_error", }; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Error thrown when trying to write MSAL cache to persistence. */ class PersistenceError extends Error { constructor(errorCode, errorMessage) { const errorString = errorMessage ? `${errorCode}: ${errorMessage}` : errorCode; super(errorString); Object.setPrototypeOf(this, PersistenceError.prototype); this.errorCode = errorCode; this.errorMessage = errorMessage; this.name = "PersistenceError"; } /** * Error thrown when trying to access the file system. */ static createFileSystemError(errorCode, errorMessage) { return new PersistenceError(errorCode, errorMessage); } /** * Error thrown when trying to write, load, or delete data from secret service on linux. * Libsecret is used to access secret service. */ static createLibSecretError(errorMessage) { return new PersistenceError("GnomeKeyringError", errorMessage); } /** * Error thrown when trying to write, load, or delete data from keychain on macOs. */ static createKeychainPersistenceError(errorMessage) { return new PersistenceError("KeychainError", errorMessage); } /** * Error thrown when trying to encrypt or decrypt data using DPAPI on Windows. */ static createFilePersistenceWithDPAPIError(errorMessage) { return new PersistenceError("DPAPIEncryptedFileError", errorMessage); } /** * Error thrown when using the cross platform lock. */ static createCrossPlatformLockError(errorMessage) { return new PersistenceError("CrossPlatformLockError", errorMessage); } /** * Throw cache persistence error * * @param errorMessage string * @returns PersistenceError */ static createCachePersistenceError(errorMessage) { return new PersistenceError("CachePersistenceError", errorMessage); } /** * Throw unsupported error * * @param errorMessage string * @returns PersistenceError */ static createNotSupportedError(errorMessage) { return new PersistenceError("NotSupportedError", errorMessage); } /** * Throw persistence not verified error * * @param errorMessage string * @returns PersistenceError */ static createPersistenceNotVerifiedError(errorMessage) { return new PersistenceError("PersistenceNotVerifiedError", errorMessage); } /** * Throw persistence creation validation error * * @param errorMessage string * @returns PersistenceError */ static createPersistenceNotValidatedError(errorMessage) { return new PersistenceError("PersistenceNotValidatedError", errorMessage); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Returns whether or not the given object is a Node.js error */ const isNodeError = (error) => { return !!error && typeof error === "object" && error.hasOwnProperty("code"); }; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Cross-process lock that works on all platforms. */ class CrossPlatformLock { constructor(lockFilePath, logger, lockOptions) { this.lockFilePath = lockFilePath; this.retryNumber = lockOptions ? lockOptions.retryNumber : 500; this.retryDelay = lockOptions ? lockOptions.retryDelay : 100; this.logger = logger; } /** * Locks cache from read or writes by creating file with same path and name as * cache file but with .lockfile extension. If another process has already created * the lockfile, will back off and retry based on configuration settings set by CrossPlatformLockOptions */ async lock() { for (let tryCount = 0; tryCount < this.retryNumber; tryCount++) { try { this.logger.info(`Pid ${process$1.pid} trying to acquire lock`); this.lockFileHandle = await fs.promises.open(this.lockFilePath, "wx+"); this.logger.info(`Pid ${process$1.pid} acquired lock`); await this.lockFileHandle.write(process$1.pid.toString()); return; } catch (err) { if (isNodeError(err)) { if (err.code === Constants$1.EEXIST_ERROR || err.code === Constants$1.EPERM_ERROR) { this.logger.info(err.message); await this.sleep(this.retryDelay); } else { this.logger.error(`${process$1.pid} was not able to acquire lock. Ran into error: ${err.message}`); throw PersistenceError.createCrossPlatformLockError(err.message); } } else { throw err; } } } this.logger.error(`${process$1.pid} was not able to acquire lock. Exceeded amount of retries set in the options`); throw PersistenceError.createCrossPlatformLockError("Not able to acquire lock. Exceeded amount of retries set in options"); } /** * unlocks cache file by deleting .lockfile. */ async unlock() { try { if (this.lockFileHandle) { // if we have a file handle to the .lockfile, delete lock file await fs.promises.unlink(this.lockFilePath); await this.lockFileHandle.close(); this.logger.info("lockfile deleted"); } else { this.logger.warning("lockfile handle does not exist, so lockfile could not be deleted"); } } catch (err) { if (isNodeError(err)) { if (err.code === Constants$1.ENOENT_ERROR) { this.logger.info("Tried to unlock but lockfile does not exist"); } else { this.logger.error(`${process$1.pid} was not able to release lock. Ran into error: ${err.message}`); throw PersistenceError.createCrossPlatformLockError(err.message); } } else { throw err; } } } sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * MSAL cache plugin which enables callers to write the MSAL cache to disk on Windows, * macOs, and Linux. * * - Persistence can be one of: * - FilePersistence: Writes and reads from an unencrypted file. Can be used on Windows, * macOs, or Linux. * - FilePersistenceWithDataProtection: Used on Windows, writes and reads from file encrypted * with windows dpapi-addon. * - KeychainPersistence: Used on macOs, writes and reads from keychain. * - LibSecretPersistence: Used on linux, writes and reads from secret service API. Requires * libsecret be installed. */ class PersistenceCachePlugin { constructor(persistence, lockOptions) { this.persistence = persistence; // initialize logger this.logger = persistence.getLogger(); // create file lock this.lockFilePath = `${this.persistence.getFilePath()}.lockfile`; this.crossPlatformLock = new CrossPlatformLock(this.lockFilePath, this.logger, lockOptions); // initialize default values this.lastSync = 0; this.currentCache = null; } /** * Reads from storage and saves an in-memory copy. If persistence has not been updated * since last time data was read, in memory copy is used. * * If cacheContext.cacheHasChanged === true, then file lock is created and not deleted until * afterCacheAccess() is called, to prevent the cache file from changing in between * beforeCacheAccess() and afterCacheAccess(). */ async beforeCacheAccess(cacheContext) { this.logger.info("Executing before cache access"); const reloadNecessary = await this.persistence.reloadNecessary(this.lastSync); if (!reloadNecessary && this.currentCache !== null) { if (cacheContext.cacheHasChanged) { this.logger.verbose("Cache context has changed"); await this.crossPlatformLock.lock(); } return; } try { this.logger.info(`Reload necessary. Last sync time: ${this.lastSync}`); await this.crossPlatformLock.lock(); this.currentCache = await this.persistence.load(); this.lastSync = new Date().getTime(); if (this.currentCache) { cacheContext.tokenCache.deserialize(this.currentCache); } else { this.logger.info("Cache empty."); } this.logger.info(`Last sync time updated to: ${this.lastSync}`); } finally { if (!cacheContext.cacheHasChanged) { await this.crossPlatformLock.unlock(); this.logger.info(`Pid ${process$1.pid} released lock`); } else { this.logger.info(`Pid ${process$1.pid} beforeCacheAccess did not release lock`); } } } /** * Writes to storage if MSAL in memory copy of cache has been changed. */ async afterCacheAccess(cacheContext) { this.logger.info("Executing after cache access"); try { if (cacheContext.cacheHasChanged) { this.logger.info("Msal in-memory cache has changed. Writing changes to persistence"); this.currentCache = cacheContext.tokenCache.serialize(); await this.persistence.save(this.currentCache); } else { this.logger.info("Msal in-memory cache has not changed. Did not write to persistence"); } } finally { await this.crossPlatformLock.unlock(); this.logger.info(`Pid ${process$1.pid} afterCacheAccess released lock`); } } } /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const Constants = { LIBRARY_NAME: "MSAL.JS", SKU: "msal.js.common", // Prefix for all library cache entries CACHE_PREFIX: "msal", // default authority DEFAULT_AUTHORITY: "https://login.microsoftonline.com/common/", DEFAULT_AUTHORITY_HOST: "login.microsoftonline.com", DEFAULT_COMMON_TENANT: "common", // ADFS String ADFS: "adfs", DSTS: "dstsv2", // Default AAD Instance Discovery Endpoint AAD_INSTANCE_DISCOVERY_ENDPT: "https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=", // CIAM URL CIAM_AUTH_URL: ".ciamlogin.com", AAD_TENANT_DOMAIN_SUFFIX: ".onmicrosoft.com", // Resource delimiter - used for certain cache entries RESOURCE_DELIM: "|", // Placeholder for non-existent account ids/objects NO_ACCOUNT: "NO_ACCOUNT", // Claims CLAIMS: "claims", // Consumer UTID CONSUMER_UTID: "9188040d-6c67-4c5b-b112-36a304b66dad", // Default scopes OPENID_SCOPE: "openid", PROFILE_SCOPE: "profile", OFFLINE_ACCESS_SCOPE: "offline_access", EMAIL_SCOPE: "email", CODE_GRANT_TYPE: "authorization_code", RT_GRANT_TYPE: "refresh_token", S256_CODE_CHALLENGE_METHOD: "S256", URL_FORM_CONTENT_TYPE: "application/x-www-form-urlencoded;charset=utf-8", AUTHORIZATION_PENDING: "authorization_pending", NOT_DEFINED: "not_defined", EMPTY_STRING: "", NOT_APPLICABLE: "N/A", NOT_AVAILABLE: "Not Available", FORWARD_SLASH: "/", IMDS_ENDPOINT: "http://169.254.169.254/metadata/instance/compute/location", IMDS_VERSION: "2020-06-01", IMDS_TIMEOUT: 2000, AZURE_REGION_AUTO_DISCOVER_FLAG: "TryAutoDetect", REGIONAL_AUTH_PUBLIC_CLOUD_SUFFIX: "login.microsoft.com", KNOWN_PUBLIC_CLOUDS: [ "login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net", ], SHR_NONCE_VALIDITY: 240, INVALID_INSTANCE: "invalid_instance", }; /** * we considered making this "enum" in the request instead of string, however it looks like the allowed list of * prompt values kept changing over past couple of years. There are some undocumented prompt values for some * internal partners too, hence the choice of generic "string" type instead of the "enum" */ const PromptValue = { LOGIN: "login", SELECT_ACCOUNT: "select_account", CONSENT: "consent", NONE: "none", CREATE: "create", NO_SESSION: "no_session", }; /** * Separators used in cache */ const Separators = { CACHE_KEY_SEPARATOR: "-", CLIENT_INFO_SEPARATOR: ".", }; const SERVER_TELEM_CONSTANTS = { SCHEMA_VERSION: 5, MAX_CUR_HEADER_BYTES: 80, MAX_LAST_HEADER_BYTES: 330, MAX_CACHED_ERRORS: 50, CACHE_KEY: "server-telemetry", CATEGORY_SEPARATOR: "|", VALUE_SEPARATOR: ",", OVERFLOW_TRUE: "1", OVERFLOW_FALSE: "0", UNKNOWN_ERROR: "unknown_error", }; /** * Type of the authentication request */ const AuthenticationScheme = { BEARER: "Bearer", POP: "pop", SSH: "ssh-cert", }; /** * Specifies the reason for fetching the access token from the identity provider */ const CacheOutcome = { // When a token is found in the cache or the cache is not supposed to be hit when making the request NOT_APPLICABLE: "0", // When the token request goes to the identity provider because force_refresh was set to true. Also occurs if claims were requested FORCE_REFRESH_OR_CLAIMS: "1", // When the token request goes to the identity provider because no cached access token exists NO_CACHED_ACCESS_TOKEN: "2", // When the token request goes to the identity provider because cached access token expired CACHED_ACCESS_TOKEN_EXPIRED: "3", // When the token request goes to the identity provider because refresh_in was used and the existing token needs to be refreshed PROACTIVELY_REFRESHED: "4", }; /*! @azure/msal-common v15.4.0 2025-03-25 */ /** * General error class thrown by the MSAL.js library. */ class AuthError extends Error { constructor(errorCode, errorMessage, suberror) { const errorString = errorMessage ? `${errorCode}: ${errorMessage}` : errorCode; super(errorString); Object.setPrototypeOf(this, AuthError.prototype); this.errorCode = errorCode || Constants.EMPTY_STRING; this.errorMessage = errorMessage || Constants.EMPTY_STRING; this.subError = suberror || Constants.EMPTY_STRING; this.name = "AuthError"; } setCorrelationId(correlationId) { this.correlationId = correlationId; } } /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const clientInfoDecodingError = "client_info_decoding_error"; const clientInfoEmptyError = "client_info_empty_error"; const tokenParsingError = "token_parsing_error"; const nullOrEmptyToken = "null_or_empty_token"; const endpointResolutionError = "endpoints_resolution_error"; const networkError = "network_error"; const openIdConfigError = "openid_config_error"; const hashNotDeserialized = "hash_not_deserialized"; const invalidState = "invalid_state"; const stateMismatch = "state_mismatch"; const stateNotFound = "state_not_found"; const nonceMismatch = "nonce_mismatch"; const authTimeNotFound = "auth_time_not_found"; const maxAgeTranspired = "max_age_transpired"; const multipleMatchingTokens = "multiple_matching_tokens"; const multipleMatchingAccounts = "multiple_matching_accounts"; const multipleMatchingAppMetadata = "multiple_matching_appMetadata"; const requestCannotBeMade = "request_cannot_be_made"; const cannotRemoveEmptyScope = "cannot_remove_empty_scope"; const cannotAppendScopeSet = "cannot_append_scopeset"; const emptyInputScopeSet = "empty_input_scopeset"; const deviceCodePollingCancelled = "device_code_polling_cancelled"; const deviceCodeExpired = "device_code_expired"; const deviceCodeUnknownError = "device_code_unknown_error"; const noAccountInSilentRequest = "no_account_in_silent_request"; const invalidCacheRecord = "invalid_cache_record"; const invalidCacheEnvironment = "invalid_cache_environment"; const noAccountFound = "no_account_found"; const noCryptoObject = "no_crypto_object"; const unexpectedCredentialType = "unexpected_credential_type"; const invalidAssertion = "invalid_assertion"; const invalidClientCredential = "invalid_client_credential"; const tokenRefreshRequired = "token_refresh_required"; const userTimeoutReached = "user_timeout_reached"; const tokenClaimsCnfRequiredForSignedJwt = "token_claims_cnf_required_for_signedjwt"; const authorizationCodeMissingFromServerResponse = "authorization_code_missing_from_server_response"; const bindingKeyNotRemoved = "binding_key_not_removed"; const endSessionEndpointNotSupported = "end_session_endpoint_not_supported"; const keyIdMissing = "key_id_missing"; const noNetworkConnectivity = "no_network_connectivity"; const userCanceled = "user_canceled"; const missingTenantIdError = "missing_tenant_id_error"; const methodNotImplemented = "method_not_implemented"; const nestedAppAuthBridgeDisabled = "nested_app_auth_bridge_disabled"; /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * ClientAuthErrorMessage class containing string constants used by error codes and messages. */ const ClientAuthErrorMessages = { [clientInfoDecodingError]: "The client info could not be parsed/decoded correctly", [clientInfoEmptyError]: "The client info was empty", [tokenParsingError]: "Token cannot be parsed", [nullOrEmptyToken]: "The token is null or empty", [endpointResolutionError]: "Endpoints cannot be resolved", [networkError]: "Network request failed", [openIdConfigError]: "Could not retrieve endpoints. Check your authority and verify the .well-known/openid-configuration endpoint returns the required endpoints.", [hashNotDeserialized]: "The hash parameters could not be deserialized", [invalidState]: "State was not the expected format", [stateMismatch]: "State mismatch error", [stateNotFound]: "State not found", [nonceMismatch]: "Nonce mismatch error", [authTimeNotFound]: "Max Age was requested and the ID token is missing the auth_time variable." + " auth_time is an optional claim and is not enabled by default - it must be enabled." + " See https://aka.ms/msaljs/optional-claims for more information.", [maxAgeTranspired]: "Max Age is set to 0, or too much time has elapsed since the last end-user authentication.", [multipleMatchingTokens]: "The cache contains multiple tokens satisfying the requirements. " + "Call AcquireToken again providing more requirements such as authority or account.", [multipleMatchingAccounts]: "The cache contains multiple accounts satisfying the given parameters. Please pass more info to obtain the correct account", [multipleMatchingAppMetadata]: "The cache contains multiple appMetadata satisfying the given parameters. Please pass more info to obtain the correct appMetadata", [requestCannotBeMade]: "Token request cannot be made without authorization code or refresh token.", [cannotRemoveEmptyScope]: "Cannot remove null or empty scope from ScopeSet", [cannotAppendScopeSet]: "Cannot append ScopeSet", [emptyInputScopeSet]: "Empty input ScopeSet cannot be processed", [deviceCodePollingCancelled]: "Caller has cancelled token endpoint polling during device code flow by setting DeviceCodeRequest.cancel = true.", [deviceCodeExpired]: "Device code is expired.", [deviceCodeUnknownError]: "Device code stopped polling for unknown reasons.", [noAccountInSilentRequest]: "Please pass an account object, silent flow is not supported without account information", [invalidCacheRecord]: "Cache record object was null or undefined.", [invalidCacheEnvironment]: "Invalid environment when attempting to create cache entry", [noAccountFound]: "No account found in cache for given key.", [noCryptoObject]: "No crypto object detected.", [unexpectedCredentialType]: "Unexpected credential type.", [invalidAssertion]: "Client assertion must meet requirements described in https://tools.ietf.org/html/rfc7515", [invalidClientCredential]: "Client credential (secret, certificate, or assertion) must not be empty when creating a confidential client. An application should at most have one credential", [tokenRefreshRequired]: "Cannot return token from cache because it must be refreshed. This may be due to one of the following reasons: forceRefresh parameter is set to true, claims have been requested, there is no cached access token or it is expired.", [userTimeoutReached]: "User defined timeout for device code polling reached", [tokenClaimsCnfRequiredForSignedJwt]: "Cannot generate a POP jwt if the token_claims are not populated", [authorizationCodeMissingFromServerResponse]: "Server response does not contain an authorization code to proceed", [bindingKeyNotRemoved]: "Could not remove the credential's binding key from storage.", [endSessionEndpointNotSupported]: "The provided authority does not support logout", [keyIdMissing]: "A keyId value is missing from the requested bound token's cache record and is required to match the token to it's stored binding key.", [noNetworkConnectivity]: "No network connectivity. Check your internet connection.", [userCanceled]: "User cancelled the flow.", [missingTenantIdError]: "A tenant id - not common, organizations, or consumers - must be specified when using the client_credentials flow.", [methodNotImplemented]: "This method has not been implemented", [nestedAppAuthBridgeDisabled]: "The nested app auth bridge is disabled", }; /** * Error thrown when there is an error in the client code running on the browser. */ class ClientAuthError extends AuthError { constructor(errorCode, additionalMessage) { super(errorCode, additionalMessage ? `${ClientAuthErrorMessages[errorCode]}: ${additionalMessage}` : ClientAuthErrorMessages[errorCode]); this.name = "ClientAuthError"; Object.setPrototypeOf(this, ClientAuthError.prototype); } } function createClientAuthError(errorCode, additionalMessage) { return new ClientAuthError(errorCode, additionalMessage); } /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Log message level. */ var LogLevel; (function (LogLevel) { LogLevel[LogLevel["Error"] = 0] = "Error"; LogLevel[LogLevel["Warning"] = 1] = "Warning"; LogLevel[LogLevel["Info"] = 2] = "Info"; LogLevel[LogLevel["Verbose"] = 3] = "Verbose"; LogLevel[LogLevel["Trace"] = 4] = "Trace"; })(LogLevel || (LogLevel = {})); /** * Class which facilitates logging of messages to a specific place. */ class Logger { constructor(loggerOptions, packageName, packageVersion) { // Current log level, defaults to info. this.level = LogLevel.Info; const setLoggerOptions = loggerOptions || Logger.createDefaultLoggerOptions(); this.localCallback = setLoggerOptions.loggerCallback; this.piiLoggingEnabled = setLoggerOptions.piiLoggingEnabled || false; this.level = typeof setLoggerOptions.logLevel === "number" ? setLoggerOptions.logLevel : LogLevel.Info; this.correlationId = setLoggerOptions.correlationId || Constants.EMPTY_STRING; this.packageName = packageName || Constants.EMPTY_STRING; this.packageVersion = packageVersion || Constants.EMPTY_STRING; } static createDefaultLoggerOptions() { return { loggerCallback: () => { // allow users to not set loggerCallback }, piiLoggingEnabled: false, logLevel: LogLevel.Info, }; } /** * Create new Logger with existing configurations. */ clone(packageName, packageVersion, correlationId) { return new Logger({ loggerCallback: this.localCallback, piiLoggingEnabled: this.piiLoggingEnabled, logLevel: this.level, correlationId: correlationId || this.correlationId, }, packageName, packageVersion); } /** * Log message with required options. */ logMessage(logMessage, options) { if (options.logLevel > this.level || (!this.piiLoggingEnabled && options.containsPii)) { return; } const timestamp = new Date().toUTCString(); // Add correlationId to logs if set, correlationId provided on log messages take precedence const logHeader = `[${timestamp}] : [${options.correlationId || this.correlationId || ""}]`; const log = `${logHeader} : ${this.packageName}@${this.packageVersion} : ${LogLevel[options.logLevel]} - ${logMessage}`; // debug(`msal:${LogLevel[options.logLevel]}${options.containsPii ? "-Pii": Constants.EMPTY_STRING}${options.context ? `:${options.context}` : Constants.EMPTY_STRING}`)(logMessage); this.executeCallback(options.logLevel, log, options.containsPii || false); } /** * Execute callback with message. */ executeCallback(level, message, containsPii) { if (this.localCallback) { this.localCallback(level, message, containsPii); } } /** * Logs error messages. */ error(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Error, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs error messages with PII. */ errorPii(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Error, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs warning messages. */ warning(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Warning, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs warning messages with PII. */ warningPii(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Warning, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs info messages. */ info(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Info, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs info messages with PII. */ infoPii(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Info, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs verbose messages. */ verbose(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Verbose, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs verbose messages with PII. */ verbosePii(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Verbose, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs trace messages. */ trace(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Trace, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs trace messages with PII. */ tracePii(message, correlationId) { this.logMessage(message, { logLevel: LogLevel.Trace, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Returns whether PII Logging is enabled or not. */ isPiiLoggingEnabled() { return this.piiLoggingEnabled || false; } } /*! @azure/msal-common v15.4.0 2025-03-25 */ /** * Convert seconds to JS Date object. Seconds can be in a number or string format or undefined (will still return a date). * @param seconds */ function toDateFromSeconds(seconds) { if (seconds) { return new Date(Number(seconds) * 1000); } return new Date(); } /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const redirectUriEmpty = "redirect_uri_empty"; const claimsRequestParsingError = "claims_request_parsing_error"; const authorityUriInsecure = "authority_uri_insecure"; const urlParseError = "url_parse_error"; const urlEmptyError = "empty_url_error"; const emptyInputScopesError = "empty_input_scopes_error"; const invalidPromptValue = "invalid_prompt_value"; const invalidClaims = "invalid_claims"; const tokenRequestEmpty = "token_request_empty"; const logoutRequestEmpty = "logout_request_empty"; const invalidCodeChallengeMethod = "invalid_code_challenge_method"; const pkceParamsMissing = "pkce_params_missing"; const invalidCloudDiscoveryMetadata = "invalid_cloud_discovery_metadata"; const invalidAuthorityMetadata = "invalid_authority_metadata"; const untrustedAuthority = "untrusted_authority"; const missingSshJwk = "missing_ssh_jwk"; const missingSshKid = "missing_ssh_kid"; const missingNonceAuthenticationHeader = "missing_nonce_authentication_header"; const invalidAuthenticationHeader = "invalid_authentication_header"; const cannotSetOIDCOptions = "cannot_set_OIDCOptions"; const cannotAllowPlatformBroker = "cannot_allow_platform_broker"; const authorityMismatch = "authority_mismatch"; /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const ClientConfigurationErrorMessages = { [redirectUriEmpty]: "A redirect URI is required for all calls, and none has been set.", [claimsRequestParsingError]: "Could not parse the given claims request object.", [authorityUriInsecure]: "Authority URIs must use https. Please see here for valid authority configuration options: https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-js-initializing-client-applications#configuration-options", [urlParseError]: "URL could not be parsed into appropriate segments.", [urlEmptyError]: "URL was empty or null.", [emptyInputScopesError]: "Scopes cannot be passed as null, undefined or empty array because they are required to obtain an access token.", [invalidPromptValue]: "Please see here for valid configuration options: https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_common.html#commonauthorizationurlrequest", [invalidClaims]: "Given claims parameter must be a stringified JSON object.", [tokenRequestEmpty]: "Token request was empty and not found in cache.", [logoutRequestEmpty]: "The logout request was null or undefined.", [invalidCodeChallengeMethod]: 'code_challenge_method passed is invalid. Valid values are "plain" and "S256".', [pkceParamsMissing]: "Both params: code_challenge and code_challenge_method are to be passed if to be sent in the request", [invalidCloudDiscoveryMetadata]: "Invalid cloudDiscoveryMetadata provided. Must be a stringified JSON object containing tenant_discovery_endpoint and metadata fields", [invalidAuthorityMetadata]: "Invalid authorityMetadata provided. Must by a stringified JSON object containing authorization_endpoint, token_endpoint, issuer fields.", [untrustedAuthority]: "The provided authority is not a trusted authority. Please include this authority in the knownAuthorities config parameter.", [missingSshJwk]: "Missing sshJwk in SSH certificate request. A stringified JSON Web Key is required when using the SSH authentication scheme.", [missingSshKid]: "Missing sshKid in SSH certificate request. A string that uniquely identifies the public SSH key is required when using the SSH authentication scheme.", [missingNonceAuthenticationHeader]: "Unable to find an authentication header containing server nonce. Either the Authentication-Info or WWW-Authenticate headers must be present in order to obtain a server nonce.", [invalidAuthenticationHeader]: "Invalid authentication header provided", [cannotSetOIDCOptions]: "Cannot set OIDCOptions parameter. Please change the protocol mode to OIDC or use a non-Microsoft authority.", [cannotAllowPlatformBroker]: "Cannot set allowPlatformBroker parameter to true when not in AAD protocol mode.", [authorityMismatch]: "Authority mismatch error. Authority provided in login request or PublicClientApplication config does not match the environment of the provided account. Please use a matching account or make an interactive request to login to this authority.", }; /** * Error thrown when there is an error in configuration of the MSAL.js library. */ class ClientConfigurationError extends AuthError { constructor(errorCode) { super(errorCode, ClientConfigurationErrorMessages[errorCode]); this.name = "ClientConfigurationError"; Object.setPrototypeOf(this, ClientConfigurationError.prototype); } } function createClientConfigurationError(errorCode) { return new ClientConfigurationError(errorCode); } /*! @azure/msal-common v15.4.0 2025-03-25 */ const X_CLIENT_EXTRA_SKU = "x-client-xtra-sku"; /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Error thrown when there is an error with the server code, for example, unavailability. */ class ServerError extends AuthError { constructor(errorCode, errorMessage, subError, errorNo, status) { super(errorCode, errorMessage, subError); this.name = "ServerError"; this.errorNo = errorNo; this.status = status; Object.setPrototypeOf(this, ServerError.prototype); } } /*! @azure/msal-common v15.4.0 2025-03-25 */ /** * Error thrown when user interaction is required. */ class InteractionRequiredAuthError extends AuthError { constructor(errorCode, errorMessage, subError, timestamp, traceId, correlationId, claims, errorNo) { super(errorCode, errorMessage, subError); Object.setPrototypeOf(this, InteractionRequiredAuthError.prototype); this.timestamp = timestamp || Constants.EMPTY_STRING; this.traceId = traceId || Constants.EMPTY_STRING; this.correlationId = correlationId || Constants.EMPTY_STRING; this.claims = claims || Constants.EMPTY_STRING; this.name = "InteractionRequiredAuthError"; this.errorNo = errorNo; } } /*! @azure/msal-common v15.4.0 2025-03-25 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const skuGroupSeparator = ","; const skuValueSeparator = "|"; function makeExtraSkuString(params) { const { skus, libraryName, libraryVersion, extensionName, extensionVersion, } = params; const skuMap = new Map([ [0, [libraryName, libraryVersion]], [2, [extensionName, extensionVersion]], ]); let skuArr = []; if (skus?.length) { skuArr = skus.split(skuGroupSeparator); // Ignore invalid input sku param if (skuArr.length < 4) { return skus; } } else { skuArr = Array.from({ length: 4 }, () => skuValueSeparator); } skuMap.forEach((value, key) => { if (value.length === 2 && value[0]?.length && value[1]?.length) { setSku({ skuArr, index: key, skuName: value[0], skuVersion: value[1], }); } }); return skuArr.join(skuGroupSeparator); } function setSku(params) { const { skuArr, index, skuName, skuVersion } = params; if (index >= skuArr.length) { return; } skuArr[index] = [skuName, skuVersion].join(skuValueSeparator); } /** @internal */ class ServerTelemetryManager { constructor(telemetryRequest, cacheManager) { this.cacheOutcome = CacheOutcome.NOT_APPLICABLE; this.cacheManager = cacheManager; this.apiId = telemetryRequest.apiId; this.correlationId = telemetryRequest.correlationId; this.wrapperSKU = telemetryRequest.wrapperSKU || Constants.EMPTY_STRING; this.wrapperVer = telemetryRequest.wrapperVer || Constants.EMPTY_STRING; this.telemetryCacheKey = SERVER_TELEM_CONSTANTS.CACHE_KEY + Separators.CACHE_KEY_SEPARATOR + telemetryRequest.clientId; } /** * API to add MSER Telemetry to request */ generateCurrentRequestHeaderValue() { const request = `${this.apiId}${SERVER_TELEM_CONSTANTS.VALUE_SEPARATOR}${this.cacheOutcome}`; const platformFieldsArr = [this.wrapperSKU, this.wrapperVer]; const nativeBrokerErrorCode = this.getNativeBrokerErrorCode(); if (nativeBrokerErrorCode?.length) { platformFieldsArr.push(`broker_error=${nativeBrokerErrorCode}`); } const platformFields = platformFieldsArr.join(SERVER_TELEM_CONSTANTS.VALUE_SEPARATOR); const regionDiscoveryFields = this.getRegionDiscoveryFields(); const requestWithRegionDiscoveryFields = [ request, regionDiscoveryFields, ].join(SERVER_TELEM_CONSTANTS.VALUE_SEPARATOR); return [ SERVER_TELEM_CONSTANTS.SCHEMA_VERSION, requestWithRegionDiscoveryFields, platformFields, ].join(SERVER_TELEM_CONSTANTS.CATEGORY_SEPARATOR); } /** * API to add MSER Telemetry for the last failed request */ generateLastRequestHeaderValue() { const lastRequests = this.getLastRequests(); const maxErrors = ServerTelemetryManager.maxErrorsToSend(lastRequests); const failedRequests = lastRequests.failedRequests .slice(0, 2 * maxErrors) .join(SERVER_TELEM_CONSTANTS.VALUE_SEPARATOR); const errors = lastRequests.errors .slice(0, maxErrors) .join(SERVER_TELEM_CONSTANTS.VALUE_SEPARATOR); const errorCount = lastRequests.errors.length; // Indicate whether this header contains all data or partial data const overflow = maxErrors < errorCount ? SERVER_TELEM_CONSTANTS.OVERFLOW_TRUE : SERVER_TELEM_CONSTANTS.OVERFLOW_FALSE; const platformFields = [errorCount, overflow].join(SERVER_TELEM_CONSTANTS.VALUE_SEPARATOR); return [ SERVER_TELEM_CONSTANTS.SCHEMA_VERSION, lastRequests.cacheHits, failedRequests, errors, platformFields, ].join(SERVER_TELEM_CONSTANTS.CATEGORY_SEPARATOR); } /** * API to cache token failures for MSER data capture * @param error */ cacheFailedRequest(error) { const lastRequests = this.getLastRequests(); if (lastRequests.errors.length >= SERVER_TELEM_CONSTANTS.MAX_CACHED_ERRORS) { // Remove a cached error to make room, first in first out lastRequests.failedRequests.shift(); // apiId lastRequests.failedRequests.shift(); // correlationId lastRequests.errors.shift(); } lastRequests.failedRequests.push(this.apiId, this.correlationId); if (error instanceof Error && !!error && error.toString()) { if (error instanceof AuthError) { if (error.subError) { lastRequests.errors.push(error.subError); } else if (error.errorCode) { lastRequests.errors.push(error.errorCode); } else { lastRequests.errors.push(error.toString()); } } else { lastRequests.errors.push(error.toString()); } } else { lastRequests.errors.push(SERVER_TELEM_CONSTANTS.UNKNOWN_ERROR); } this.cacheManager.setServerTelemetry(this.telemetryCacheKey, lastRequests); return; } /** * Update server telemetry cache entry by incrementing cache hit counter */ incrementCacheHits() { const lastRequests = this.getLastRequests(); lastRequests.cacheHits += 1; this.cacheManager.setServerTelemetry(this.telemetryCacheKey, lastRequests); return lastRequests.cacheHits; } /** * Get the server telemetry entity from cache or initialize a new one */ getLastRequests() { const initialValue = { failedRequests: [], errors: [], cacheHits: 0, }; const lastRequests = this.cacheManager.getServerTelemetry(this.telemetryCacheKey); return lastRequests || initialValue; } /** * Remove server telemetry cache entry */ clearTelemetryCache() { const lastRequests = this.getLastRequests(); const numErrorsFlushed = ServerTelemetryManager.maxErrorsToSend(lastRequests); const errorCount = lastRequests.errors.length; if (numErrorsFlushed === errorCount) { // All errors were sent on last request, clear Telemetry cache this.cacheManager.removeItem(this.telemetryCacheKey); } else { // Partial data was flushed to server, construct a new telemetry cache item with errors that were not flushed const serverTelemEntity = { failedRequests: lastRequests.failedRequests.slice(numErrorsFlushed * 2), errors: lastRequests.errors.slice(numErrorsFlushed), cacheHits: 0, }; this.cacheManager.setServerTelemetry(this.telemetryCacheKey, serverTelemEntity); } } /** * Returns the maximum number of errors that can be flushed to the server in the next network request * @param serverTelemetryEntity */ static maxErrorsToSend(serverTelemetryEntity) { let i; let maxErrors = 0; let dataSize = 0; const errorCount = serverTelemetryEntity.errors.length; for (i = 0; i < errorCount; i++) { // failedRequests parameter contains pairs of apiId and correlationId, multiply index by 2 to preserve pairs const apiId = serverTelemetryEntity.failedRequests[2 * i] || Constants.EMPTY_STRING; const correlationId = serverTelemetryEntity.failedRequests[2 * i + 1] || Constants.EMPTY_STRING; const errorCode = serverTelemetryEntity.errors[i] || Constants.EMPTY_STRING; // Count number of characters that would be added to header, each character is 1 byte. Add 3 at the end to account for separators dataSize += apiId.toString().length + correlationId.toString().length + errorCode.length + 3; if (dataSize < SERVER_TELEM_CONSTANTS.MAX_LAST_HEADER_BYTES) { // Adding this entry to the header would still keep header size below the limit maxErrors += 1; } else { break; } } return maxErrors; } /** * Get the region discovery fields * * @returns string */ getRegionDiscoveryFields() { const regionDiscoveryFields = []; regionDiscoveryFields.push(this.regionUsed || Constants.EMPTY_STRING); regionDiscoveryFields.push(this.regionSource || Constants.EMPTY_STRING); regionDiscoveryFields.push(this.regionOutcome || Constants.EMPTY_STRING); return regionDiscoveryFields.join(","); } /** * Update the region discovery metadata * * @param regionDiscoveryMetadata * @returns void */ updateRegionDiscoveryMetadata(regionDiscoveryMetadata) { this.regionUsed = regionDiscoveryMetadata.region_used; this.regionSource = regionDiscoveryMetadata.region_source; this.regionOutcome = regionDiscoveryMetadata.region_outcome; } /** * Set cache outcome */ setCacheOutcome(cacheOutcome) { this.cacheOutcome = cacheOutcome; } setNativeBrokerErrorCode(errorCode) { const lastRequests = this.getLastRequests(); lastRequests.nativeBrokerErrorCode = errorCode; this.cacheManager.setServerTelemetry(this.telemetryCacheKey, lastRequests); } getNativeBrokerErrorCode() { return this.getLastRequests().nativeBrokerErrorCode; } clearNativeBrokerErrorCode() { const lastRequests = this.getLastRequests(); delete lastRequests.nativeBrokerErrorCode; this.cacheManager.setServerTelemetry(this.telemetryCacheKey, lastRequests); } static makeExtraSkuString(params) { return makeExtraSkuString(params); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class BasePersistence { async verifyPersistence() { // We are using a different location for the test to avoid overriding the functional cache const persistenceValidator = await this.createForPersistenceValidation(); try { await persistenceValidator.save(Constants$1.PERSISTENCE_TEST_DATA); const retrievedDummyData = await persistenceValidator.load(); if (!retrievedDummyData) { throw PersistenceError.createCachePersistenceError("Persistence check failed. Data was writt