@azure/msal-node-extensions
Version:
 
1,192 lines (1,168 loc) • 92.2 kB
JavaScript
/*! @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