/*! @azure/msal-node-extensions v1.5.17 2025-07-08 */ '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.8.1 2025-07-08 */ /* * 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: "-"}; const SERVER_TELEM_CONSTANTS = { SCHEMA_VERSION: 5, 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"}; /*! @azure/msal-common v15.8.1 2025-07-08 */ /** * 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.8.1 2025-07-08 */ /* * 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.8.1 2025-07-08 */ /* * 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.8.1 2025-07-08 */ /* * 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 defaultLoggerCallback = () => { return; }; const setLoggerOptions = loggerOptions || Logger.createDefaultLoggerOptions(); this.localCallback = setLoggerOptions.loggerCallback || defaultLoggerCallback; 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.8.1 2025-07-08 */ /** * 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.8.1 2025-07-08 */ /* * 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 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.8.1 2025-07-08 */ /* * 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.", [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.8.1 2025-07-08 */ const X_CLIENT_EXTRA_SKU = "x-client-xtra-sku"; /*! @azure/msal-common v15.8.1 2025-07-08 */ /* * 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.8.1 2025-07-08 */ /** * 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.8.1 2025-07-08 */ /* * 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, this.correlationId); 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, this.correlationId); 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, this.correlationId); } 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, this.correlationId); } } /** * 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, this.correlationId); } getNativeBrokerErrorCode() { return this.getLastRequests().nativeBrokerErrorCode; } clearNativeBrokerErrorCode() { const lastRequests = this.getLastRequests(); delete lastRequests.nativeBrokerErrorCode; this.cacheManager.setServerTelemetry(this.telemetryCacheKey, lastRequests, this.correlationId); } 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 written but it could not be read. " + "Possible cause: on Linux, LibSecret is installed but D-Bus isn't running \ because it cannot be started over SSH."); } if (retrievedDummyData !== Constants$1.PERSISTENCE_TEST_DATA) { throw PersistenceError.createCachePersistenceError(`Persistence check failed. Data written ${Constants$1.PERSISTENCE_TEST_DATA} is different \ from data read ${retrievedDummyData}`); } await persistenceValidator.delete(); return true; } catch (e) { throw PersistenceError.createCachePersistenceError(`Verifing persistence failed with the error: ${e}`); } } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Reads and writes data to file specified by file location. File contents are not * encrypted. * * If file or directory has not been created, it FilePersistence.create() will create * file and any directories in the path recursively. */ class FilePersistence extends BasePersistence { constructor(fileLocation, loggerOptions) { super(); this.logger = new Logger(loggerOptions || FilePersistence.createDefaultLoggerOptions()); this.filePath = fileLocation; } static async create(fileLocation, loggerOptions) { const filePersistence = new FilePersistence(fileLocation, loggerOptions); await filePersistence.createCacheFile(); return filePersistence; } async save(contents) { try { await fs.promises.writeFile(this.getFilePath(), contents, "utf-8"); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async saveBuffer(contents) { try { await fs.promises.writeFile(this.getFilePath(), contents); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async load() { try { return await fs.promises.readFile(this.getFilePath(), "utf-8"); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async loadBuffer() { try { return await fs.promises.readFile(this.getFilePath()); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async delete() { try { await fs.promises.unlink(this.getFilePath()); return true; } catch (err) { if (isNodeError(err)) { if (err.code === Constants$1.ENOENT_ERROR) { // file does not exist, so it was not deleted this.logger.warning("Cache file does not exist, so it could not be deleted"); return false; } throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } getFilePath() { return this.filePath; } async reloadNecessary(lastSync) { return lastSync < (await this.timeLastModified()); } getLogger() { return this.logger; } createForPersistenceValidation() { const testCacheFileLocation = `${path.dirname(this.filePath)}/test.cache`; return FilePersistence.create(testCacheFileLocation); } static createDefaultLoggerOptions() { return { loggerCallback: () => { // allow users to not set loggerCallback }, piiLoggingEnabled: false, logLevel: LogLevel.Info, }; } async timeLastModified() { try { const stats = await fs.promises.stat(this.filePath); return stats.mtime.getTime(); } catch (err) { if (isNodeError(err)) { if (err.code === Constants$1.ENOENT_ERROR) { // file does not exist, so it's never been modified this.logger.verbose("Cache file does not exist"); return 0; } throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } else { throw err; } } } async createCacheFile() { await this.createFileDirectory(); // File is created only if it does not exist const fileHandle = await fs.promises.open(this.filePath, "a"); await fileHandle.close(); this.logger.info(`File created at ${this.filePath}`); } async createFileDirectory() { try { await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true }); } catch (err) { if (isNodeError(err)) { if (err.code === Constants$1.EEXIST_ERROR) { this.logger.info(`Directory ${path.dirname(this.filePath)} already exists`); } else { throw PersistenceError.createFileSystemError(err.code || ErrorCodes.UNKNOWN, err.message); } } else { throw err; } } } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class UnavailableDpapi { constructor(errorMessage) { this.errorMessage = errorMessage; } protectData() { throw new Error(this.errorMessage); } unprotectData() { throw new Error(this.errorMessage); } } let Dpapi; if (process.platform !== "win32") { Dpapi = new UnavailableDpapi("Dpapi is not supported on this platform"); } else { // In .mjs files, require is not defined. We need to use createRequire to get a require function const safeRequire = typeof require !== "undefined" ? require : module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('msal-node-extensions.cjs', document.baseURI).href))); try { Dpapi = safeRequire(`../bin/${process.arch}/dpapi`); } catch (e) { Dpapi = new UnavailableDpapi("Dpapi bindings unavailable"); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Specifies the scope of the data protection - either the current user or the local * machine. * * You do not need a key to protect or unprotect the data. * If you set the Scope to CurrentUser, only applications running on your credentials can * unprotect the data; however, that means that any application running on your credentials * can access the protected data. If you set the Scope to LocalMachine, any full-trust * application on the computer can unprotect, access, and modify the data. * */ const DataProtectionScope = { CurrentUser: "CurrentUser", LocalMachine: "LocalMachine", }; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Uses CryptProtectData and CryptUnprotectData on Windows to encrypt and decrypt file contents. * * scope: Scope of the data protection. Either local user or the current machine * optionalEntropy: Password or other additional entropy used to encrypt the data */ class FilePersistenceWithDataProtection extends BasePersistence { constructor(filePersistence, scope, optionalEntropy) { super(); this.scope = scope; this.optionalEntropy = optionalEntropy ? Buffer.from(optionalEntropy, "utf-8") : null; this.filePersistence = filePersistence; } static async create(fileLocation, scope, optionalEntropy, loggerOptions) { const filePersistence = await FilePersistence.create(fileLocation, loggerOptions); const persistence = new FilePersistenceWithDataProtection(filePersistence, scope, optionalEntropy); return persistence; } async save(contents) { try { const encryptedContents = Dpapi.protectData(Buffer.from(contents, "utf-8"), this.optionalEntropy, this.scope.toString()); await this.filePersistence.saveBuffer(encryptedContents); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFilePersistenceWithDPAPIError(err.message); } else { throw err; } } } async load() { try { const encryptedContents = await this.filePersistence.loadBuffer(); if (typeof encryptedContents === "undefined" || !encryptedContents || 0 === encryptedContents.length) { this.filePersistence .getLogger() .info("Encrypted contents loaded from file were null or empty"); return null; } return Dpapi.unprotectData(encryptedContents, this.optionalEntropy, this.scope.toString()).toString(); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createFilePersistenceWithDPAPIError(err.message); } else { throw err; } } } async delete() { return this.filePersistence.delete(); } async reloadNecessary(lastSync) { return this.filePersistence.reloadNecessary(lastSync); } getFilePath() { return this.filePersistence.getFilePath(); } getLogger() { return this.filePersistence.getLogger(); } createForPersistenceValidation() { const testCacheFileLocation = `${path.dirname(this.filePersistence.getFilePath())}/test.cache`; return FilePersistenceWithDataProtection.create(testCacheFileLocation, DataProtectionScope.CurrentUser); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Uses reads and writes passwords to macOS keychain * * serviceName: Identifier used as key for whatever value is stored * accountName: Account under which password should be stored */ class KeychainPersistence extends BasePersistence { constructor(filePersistence, serviceName, accountName) { super(); this.filePersistence = filePersistence; this.serviceName = serviceName; this.accountName = accountName; } static async create(fileLocation, serviceName, accountName, loggerOptions) { const filePersistence = await FilePersistence.create(fileLocation, loggerOptions); const persistence = new KeychainPersistence(filePersistence, serviceName, accountName); return persistence; } async save(contents) { try { await keytar.setPassword(this.serviceName, this.accountName, contents); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createKeychainPersistenceError(err.message); } else { throw err; } } // Write dummy data to update file mtime await this.filePersistence.save("{}"); } async load() { try { return await keytar.getPassword(this.serviceName, this.accountName); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createKeychainPersistenceError(err.message); } else { throw err; } } } async delete() { try { await this.filePersistence.delete(); return await keytar.deletePassword(this.serviceName, this.accountName); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createKeychainPersistenceError(err.message); } else { throw err; } } } async reloadNecessary(lastSync) { return this.filePersistence.reloadNecessary(lastSync); } getFilePath() { return this.filePersistence.getFilePath(); } getLogger() { return this.filePersistence.getLogger(); } createForPersistenceValidation() { const testCacheFileLocation = `${path.dirname(this.filePersistence.getFilePath())}/test.cache`; return KeychainPersistence.create(testCacheFileLocation, "persistenceValidationServiceName", "persistencValidationAccountName"); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Uses reads and writes passwords to Secret Service API/libsecret. Requires libsecret * to be installed. * * serviceName: Identifier used as key for whatever value is stored * accountName: Account under which password should be stored */ class LibSecretPersistence extends BasePersistence { constructor(filePersistence, serviceName, accountName) { super(); this.filePersistence = filePersistence; this.serviceName = serviceName; this.accountName = accountName; } static async create(fileLocation, serviceName, accountName, loggerOptions) { const filePersistence = await FilePersistence.create(fileLocation, loggerOptions); const persistence = new LibSecretPersistence(filePersistence, serviceName, accountName); return persistence; } async save(contents) { try { await keytar.setPassword(this.serviceName, this.accountName, contents); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createLibSecretError(err.message); } else { throw err; } } // Write dummy data to update file mtime await this.filePersistence.save("{}"); } async load() { try { return await keytar.getPassword(this.serviceName, this.accountName); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createLibSecretError(err.message); } else { throw err; } } } async delete() { try { await this.filePersistence.delete(); return await keytar.deletePassword(this.serviceName, this.accountName); } catch (err) { if (isNodeError(err)) { throw PersistenceError.createLibSecretError(err.message); } else { throw err; } } } async reloadNecessary(lastSync) { return this.filePersistence.reloadNecessary(lastSync); } getFilePath() { return this.filePersistence.getFilePath(); } getLogger() { return this.filePersistence.getLogger(); } createForPersistenceValidation() { const testCacheFileLocation = `${path.dirname(this.filePersistence.getFilePath())}/test.cache`; return LibSecretPersistence.create(testCacheFileLocation, "persistenceValidationServiceName", "persistencValidationAccountName"); } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class Environment { static get homeEnvVar() { return this.getEnvironmentVariable(Constants$1.ENVIRONMENT.HOME); } static get lognameEnvVar() { return this.getEnvironmentVariable(Constants$1.ENVIRONMENT.LOGNAME); } static get userEnvVar() { return this.getEnvironmentVariable(Constants$1.ENVIRONMENT.USER); } static get lnameEnvVar() { return this.getEnvironmentVariable(Constants$1.ENVIRONMENT.LNAME); } static get usernameEnvVar() { return this.getEnvironmentVariable(Constants$1.ENVIRONMENT.USERNAME); } static getEnvironmentVariable(name) { return process.env[name] || ""; } static getEnvironmentPlatform() { return process.platform; } static isWindowsPlatform() { return this.getEnvironmentPlatform() === Platform.WINDOWS; } static isLinuxPlatform() { return this.getEnvironmentPlatform() === Platform.LINUX; } static isMacPlatform() { return this.getEnvironmentPlatform() === Platform.MACOS; } static isLinuxRootUser() { if (typeof process.getuid !== "function") { return false; } return process.getuid() === Constants$1.LINUX_ROOT_USER_GUID; } static getUserRootDirectory() { return !this.isWindowsPlatform() ? this.getUserHomeDirOnUnix() : this.getUserHomeDirOnWindows(); } static getUserHomeDirOnWindows() { return this.getEnvironmentVariable(Constants$1.ENVIRONMENT.LOCAL_APPLICATION_DATA); } static getUserHomeDirOnUnix() { if (this.isWindowsPlatform()) { throw PersistenceError.createNotSupportedError("Getting the user home directory for unix is not supported in windows"); } if (this.homeEnvVar) { return this.homeEnvVar; } let username = null; if (this.lognameEnvVar) { username = this.lognameEnvVar; } else if (this.userEnvVar) { username = this.userEnvVar; } else if (this.lnameEnvVar) { username = this.lnameEnvVar; } else if (this.usernameEnvVar) { username = this.usernameEnvVar; } if (this.isMacPlatform()) { return username ? path.join("/Users", username) : null; } else if (this.isLinuxPlatform()) { if (this.isLinuxRootUser()) { return "/root"; } else { return username ? path.join("/home", username) : null; } } else { throw PersistenceError.createNotSupportedError("Getting the user home directory for unix is not supported in windows"); } } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class PersistenceCreator { static async createPersistence(config) { let peristence; // On Windows, uses a DPAPI encrypted file if (Environment.isWindowsPlatform()) { if (!config.cachePath || !config.dataProtectionScope) { throw PersistenceError.createPersistenceNotValidatedError("Cache path and/or data protection scope not provided for the FilePersistenceWithDataProtection cache plugin"); } peristence = await FilePersistenceWithDataProtection.create(config.cachePath, DataProtectionScope.CurrentUser, undefined, config.loggerOptions); } // On Mac, uses keychain. else if (Environment.isMacPlatform()) { if (!config.cachePath || !config.serviceName || !config.accountName) { throw PersistenceError.createPersistenceNotValidatedError("Cache path, service name and/or account name not provided for the KeychainPersistence cache plugin"); } peristence = await KeychainPersistence.create(config.cachePath, config.serviceName, config.accountName, config.loggerOptions); } // On Linux, uses libsecret to store to secret service. Libsecret has to be installed. else if (Environment.isLinuxPlatform()) { if (!config.cachePath || !config.serviceName || !config.accountName) { throw PersistenceError.createPersistenceNotValidatedError("Cache path, service name and/or account name not provided for the LibSecretPersistence cache plugin"); } peristence = await LibSecretPersistence.create(config.cachePath, config.serviceName, config.accountName, config.loggerOptions); } else { throw PersistenceError.createNotSupportedError("The current environment is not supported by msal-node-extensions yet."); } await peristence.verifyPersistence().catch(async (e) => { if (Environment.isLinuxPlatform() && config.usePlaintextFileOnLinux) { if (!config.cachePath) { throw PersistenceError.createPersistenceNotValidatedError("Cache path not provided for the FilePersistence cache plugin"); } peristence = await FilePersistence.create(config.cachePath, config.loggerOptions); const isFilePersistenceVerified = await peristence.verifyPersistence(); if (isFilePersistenceVerified) { return peristence; } throw PersistenceError.createPersistenceNotVerifiedError("Persistence could not be verified"); } else { throw e; } }); return peristence; } } /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class NativeAuthError extends AuthError { constructor(errorStatus, errorContext, errorCode, errorTag) { super(errorStatus, errorContext); this.name = "NativeAuthError"; this.statusCode = errorCode; this.tag = errorTag; Object.setPrototypeOf(this, NativeAuthError.prototype); } } /* eslint-disable header/header */ const name = "@azure/msal-node-extensions"; const version = "1.5.17"; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class NativeBrokerPlugin { constructor() { const defaultLoggerOptions = { loggerCallback: () => { // Empty logger callback }, piiLoggingEnabled: false, }; this.logger = new Logger(defaultLoggerOptions, name, version); // Default logger this.isBrokerAvailable = msalNodeRuntime.msalNodeRuntime.StartupError ? false : true; } setLogger(loggerOptions) { this.logger = new Logger(loggerOptions, name, version); const logCallback = (message, logLevel, containsPii) => { switch (logLevel) { case msalNodeRuntime.LogLevel.Trace: if (containsPii) { this.logger.tracePii(message); } else { this.logger.trace(message); } break; case msalNodeRuntime.LogLevel.Debug: if (containsPii) { this.logger.tracePii(message); } else { this.logger.trace(message); } break; case msalNodeRuntime.LogLevel.Info: if (containsPii) { this.logger.infoPii(message); } else { this.logger.info(message); } break; case msalNodeRuntime.LogLevel.Warning: if (containsPii) { this.logger.warningPii(message); } else { this.logger.warning(message); } break; case msalNodeRuntime.LogLevel.Error: if (containsPii) { this.logger.errorPii(message); } else { this.logger.error(message); } break; case msalNodeRuntime.LogLevel.Fatal: if (containsPii) { this.logger.errorPii(message); } else { this.logger.error(message); } break; default: if (containsPii) { this.logger.infoPii(message); } else { this.logger.info(message); } break; } }; try { msalNodeRuntime.msalNodeRuntime.RegisterLogger(logCallback, loggerOptions.piiLoggingEnabled || false); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { throw wrappedError; } } } async getAccountById(accountId, correlationId) { this.logger.trace("NativeBrokerPlugin - getAccountById called", correlationId); const readAccountResult = await this.readAccountById(accountId, correlationId); return this.generateAccountInfo(readAccountResult.account); } async getAllAccounts(clientId, correlationId) { this.logger.trace("NativeBrokerPlugin - getAllAccounts called", correlationId); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } const accountInfoResult = []; result.accounts.forEach((account) => { accountInfoResult.push(this.generateAccountInfo(account)); }); resolve(accountInfoResult); }; try { msalNodeRuntime.msalNodeRuntime.DiscoverAccountsAsync(clientId, correlationId, resultCallback); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async acquireTokenSilent(request) { this.logger.trace("NativeBrokerPlugin - acquireTokenSilent called", request.correlationId); const authParams = this.generateRequestParameters(request); const account = await this.getAccount(request); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } const authenticationResult = this.getAuthenticationResult(request, result); resolve(authenticationResult); }; try { if (account) { msalNodeRuntime.msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, request.correlationId, account, resultCallback); } else { msalNodeRuntime.msalNodeRuntime.SignInSilentlyAsync(authParams, request.correlationId, resultCallback); } } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async acquireTokenInteractive(request, providedWindowHandle) { this.logger.trace("NativeBrokerPlugin - acquireTokenInteractive called", request.correlationId); const authParams = this.generateRequestParameters(request); const account = await this.getAccount(request); const windowHandle = providedWindowHandle || Buffer.from([0]); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } const authenticationResult = this.getAuthenticationResult(request, result); resolve(authenticationResult); }; try { switch (request.prompt) { case PromptValue.LOGIN: case PromptValue.SELECT_ACCOUNT: case PromptValue.CREATE: this.logger.info("Calling native interop SignInInteractively API", request.correlationId); const loginHint = request.loginHint || Constants.EMPTY_STRING; msalNodeRuntime.msalNodeRuntime.SignInInteractivelyAsync(windowHandle, authParams, request.correlationId, loginHint, resultCallback); break; case PromptValue.NONE: if (account) { this.logger.info("Calling native interop AcquireTokenSilently API", request.correlationId); msalNodeRuntime.msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, request.correlationId, account, resultCallback); } else { this.logger.info("Calling native interop SignInSilently API", request.correlationId); msalNodeRuntime.msalNodeRuntime.SignInSilentlyAsync(authParams, request.correlationId, resultCallback); } break; default: if (account) { this.logger.info("Calling native interop AcquireTokenInteractively API", request.correlationId); msalNodeRuntime.msalNodeRuntime.AcquireTokenInteractivelyAsync(windowHandle, authParams, request.correlationId, account, resultCallback); } else { this.logger.info("Calling native interop SignIn API", request.correlationId); const loginHint = request.loginHint || Constants.EMPTY_STRING; msalNodeRuntime.msalNodeRuntime.SignInAsync(windowHandle, authParams, request.correlationId, loginHint, resultCallback); } break; } } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async signOut(request) { this.logger.trace("NativeBrokerPlugin - signOut called", request.correlationId); const account = await this.getAccount(request); if (!account) { throw createClientAuthError(noAccountFound); } return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } resolve(); }; try { msalNodeRuntime.msalNodeRuntime.SignOutSilentlyAsync(request.clientId, request.correlationId, account, resultCallback); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async getAccount(request) { if (request.accountId) { const readAccountResult = await this.readAccountById(request.accountId, request.correlationId); return readAccountResult.account; } return null; } async readAccountById(accountId, correlationId) { this.logger.trace("NativeBrokerPlugin - readAccountById called", correlationId); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } resolve(result); }; try { msalNodeRuntime.msalNodeRuntime.ReadAccountByIdAsync(accountId, correlationId, resultCallback); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } generateRequestParameters(request) { this.logger.trace("NativeBrokerPlugin - generateRequestParameters called", request.correlationId); const authParams = new msalNodeRuntime.msalNodeRuntime.AuthParameters(); try { authParams.CreateAuthParameters(request.clientId, request.authority); authParams.SetRedirectUri(request.redirectUri); authParams.SetRequestedScopes(request.scopes.join(" ")); if (request.claims) { authParams.SetDecodedClaims(request.claims); } if (request.authenticationScheme === AuthenticationScheme.POP) { if (!request.resourceRequestMethod || !request.resourceRequestUri) { throw new Error("Authentication Scheme set to POP but one or more of the following parameters are missing: resourceRequestMethod, resourceRequestUri"); } const resourceUrl = new URL(request.resourceRequestUri); authParams.SetPopParams(request.resourceRequestMethod, resourceUrl.host, resourceUrl.pathname, request.shrNonce || ""); } if (request.extraParameters) { Object.entries(request.extraParameters).forEach(([key, value]) => { authParams.SetAdditionalParameter(key, value); }); } const skus = request.extraParameters && request.extraParameters[X_CLIENT_EXTRA_SKU] ?.length ? request.extraParameters[X_CLIENT_EXTRA_SKU] : ""; authParams.SetAdditionalParameter(X_CLIENT_EXTRA_SKU, ServerTelemetryManager.makeExtraSkuString({ skus, extensionName: "msal.node.ext", extensionVersion: version, })); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { throw wrappedError; } } return authParams; } getAuthenticationResult(request, authResult) { this.logger.trace("NativeBrokerPlugin - getAuthenticationResult called", request.correlationId); let fromCache = false; try { const telemetryJSON = JSON.parse(authResult.telemetryData); fromCache = !!telemetryJSON["is_cache"]; } catch (e) { this.logger.error("NativeBrokerPlugin: getAuthenticationResult - Error parsing telemetry data. Could not determine if response came from cache.", request.correlationId); } let idTokenClaims; try { idTokenClaims = JSON.parse(authResult.idToken); } catch (e) { throw new Error("Unable to parse idToken claims"); } const accountInfo = this.generateAccountInfo(authResult.account, idTokenClaims); let accessToken; let tokenType; if (authResult.isPopAuthorization) { // Header includes 'pop ' prefix accessToken = authResult.authorizationHeader.split(" ")[1]; tokenType = AuthenticationScheme.POP; } else { accessToken = authResult.accessToken; tokenType = AuthenticationScheme.BEARER; } const result = { authority: request.authority, uniqueId: idTokenClaims.oid || idTokenClaims.sub || "", tenantId: idTokenClaims.tid || "", scopes: authResult.grantedScopes.split(" "), account: accountInfo, idToken: authResult.rawIdToken, idTokenClaims: idTokenClaims, accessToken: accessToken, fromCache: fromCache, // MsalRuntime expiresOn returned in seconds, converting to Date for AuthenticationResult expiresOn: toDateFromSeconds(authResult.expiresOn), tokenType: tokenType, correlationId: request.correlationId, fromNativeBroker: true, }; return result; } generateAccountInfo(account, idTokenClaims) { this.logger.trace("NativeBrokerPlugin - generateAccountInfo called"); const accountInfo = { homeAccountId: account.homeAccountId, environment: account.environment, tenantId: account.realm, username: account.username, localAccountId: account.localAccountId, name: account.displayName, idTokenClaims: idTokenClaims, nativeAccountId: account.accountId, }; return accountInfo; } isMsalRuntimeError(result) { return (result.hasOwnProperty("errorCode") || result.hasOwnProperty("errorStatus") || result.hasOwnProperty("errorContext") || result.hasOwnProperty("errorTag")); } wrapError(error) { if (error && typeof error === "object" && this.isMsalRuntimeError(error)) { const { errorCode, errorStatus, errorContext, errorTag } = error; switch (errorStatus) { case msalNodeRuntime.ErrorStatus.InteractionRequired: case msalNodeRuntime.ErrorStatus.AccountUnusable: return new InteractionRequiredAuthError(ErrorCodes.INTERATION_REQUIRED_ERROR_CODE, errorContext); case msalNodeRuntime.ErrorStatus.NoNetwork: case msalNodeRuntime.ErrorStatus.NetworkTemporarilyUnavailable: return createClientAuthError(noNetworkConnectivity); case msalNodeRuntime.ErrorStatus.ServerTemporarilyUnavailable: return new ServerError(ErrorCodes.SERVER_UNAVAILABLE, errorContext); case msalNodeRuntime.ErrorStatus.UserCanceled: return createClientAuthError(userCanceled); case msalNodeRuntime.ErrorStatus.AuthorityUntrusted: return createClientConfigurationError(untrustedAuthority); case msalNodeRuntime.ErrorStatus.UserSwitched: // Not an error case, if there's customer demand we can surface this as a response property return null; case msalNodeRuntime.ErrorStatus.AccountNotFound: return createClientAuthError(noAccountFound); default: return new NativeAuthError(msalNodeRuntime.ErrorStatus[errorStatus], errorContext, errorCode, errorTag); } } throw error; } } exports.DataProtectionScope = DataProtectionScope; exports.Environment = Environment; exports.FilePersistence = FilePersistence; exports.FilePersistenceWithDataProtection = FilePersistenceWithDataProtection; exports.KeychainPersistence = KeychainPersistence; exports.LibSecretPersistence = LibSecretPersistence; exports.NativeBrokerPlugin = NativeBrokerPlugin; exports.PersistenceCachePlugin = PersistenceCachePlugin; exports.PersistenceCreator = PersistenceCreator; //# sourceMappingURL=msal-node-extensions.cjs.map