UNPKG

@azure/msal-browser

Version:
1,302 lines (1,278 loc) 914 kB
/*! @azure/msal-browser v4.21.0 2025-08-19 */ 'use strict'; 'use strict'; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const Constants = { LIBRARY_NAME: "MSAL.JS", SKU: "msal.js.common", // 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", }; const HttpStatus = { SUCCESS: 200, SUCCESS_RANGE_START: 200, SUCCESS_RANGE_END: 299, REDIRECT: 302, CLIENT_ERROR: 400, CLIENT_ERROR_RANGE_START: 400, BAD_REQUEST: 400, UNAUTHORIZED: 401, NOT_FOUND: 404, REQUEST_TIMEOUT: 408, GONE: 410, TOO_MANY_REQUESTS: 429, CLIENT_ERROR_RANGE_END: 499, SERVER_ERROR: 500, SERVER_ERROR_RANGE_START: 500, SERVICE_UNAVAILABLE: 503, GATEWAY_TIMEOUT: 504, SERVER_ERROR_RANGE_END: 599, MULTI_SIDED_ERROR: 600, }; const HttpMethod$1 = { GET: "GET", POST: "POST", }; const OIDC_DEFAULT_SCOPES = [ Constants.OPENID_SCOPE, Constants.PROFILE_SCOPE, Constants.OFFLINE_ACCESS_SCOPE, ]; const OIDC_SCOPES = [...OIDC_DEFAULT_SCOPES, Constants.EMAIL_SCOPE]; /** * Request header names */ const HeaderNames = { CONTENT_TYPE: "Content-Type", CONTENT_LENGTH: "Content-Length", RETRY_AFTER: "Retry-After", CCS_HEADER: "X-AnchorMailbox", WWWAuthenticate: "WWW-Authenticate", AuthenticationInfo: "Authentication-Info", X_MS_REQUEST_ID: "x-ms-request-id", X_MS_HTTP_VERSION: "x-ms-httpver", }; /** * Persistent cache keys MSAL which stay while user is logged in. */ const PersistentCacheKeys = { ACTIVE_ACCOUNT_FILTERS: "active-account-filters", // new cache entry for active_account for a more robust version for browser }; /** * String constants related to AAD Authority */ const AADAuthorityConstants = { COMMON: "common", ORGANIZATIONS: "organizations", CONSUMERS: "consumers", }; /** * Claims request keys */ const ClaimsRequestKeys = { ACCESS_TOKEN: "access_token", XMS_CC: "xms_cc", }; /** * 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", NO_SESSION: "no_session", }; /** * Allowed values for response_type */ const OAuthResponseType = { CODE: "code", IDTOKEN_TOKEN_REFRESHTOKEN: "id_token token refresh_token", }; /** * allowed values for server response type * @deprecated Use ResponseMode instead */ const ServerResponseType = { QUERY: "query", FRAGMENT: "fragment", }; /** * allowed values for response_mode */ const ResponseMode = { QUERY: "query"}; /** * allowed grant_type */ const GrantType$1 = { AUTHORIZATION_CODE_GRANT: "authorization_code", REFRESH_TOKEN_GRANT: "refresh_token"}; /** * Account types in Cache */ const CacheAccountType = { MSSTS_ACCOUNT_TYPE: "MSSTS", ADFS_ACCOUNT_TYPE: "ADFS", GENERIC_ACCOUNT_TYPE: "Generic", // NTLM, Kerberos, FBA, Basic etc }; /** * Separators used in cache */ const Separators = { CACHE_KEY_SEPARATOR: "-", CLIENT_INFO_SEPARATOR: ".", }; /** * Credential Type stored in the cache */ const CredentialType = { ID_TOKEN: "IdToken", ACCESS_TOKEN: "AccessToken", ACCESS_TOKEN_WITH_AUTH_SCHEME: "AccessToken_With_AuthScheme", REFRESH_TOKEN: "RefreshToken", }; /** * More Cache related constants */ const APP_METADATA = "appmetadata"; const CLIENT_INFO = "client_info"; const THE_FAMILY_ID = "1"; const AUTHORITY_METADATA_CONSTANTS = { CACHE_KEY: "authority-metadata", REFRESH_TIME_SECONDS: 3600 * 24, // 24 Hours }; const AuthorityMetadataSource = { CONFIG: "config", CACHE: "cache", NETWORK: "network", HARDCODED_VALUES: "hardcoded_values", }; 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", }; /** * Constants related to throttling */ const ThrottlingConstants = { // Default time to throttle RequestThumbprint in seconds DEFAULT_THROTTLE_TIME_SECONDS: 60, // Default maximum time to throttle in seconds, overrides what the server sends back DEFAULT_MAX_THROTTLE_TIME_SECONDS: 3600, // Prefix for storing throttling entries THROTTLING_PREFIX: "throttling", // Value assigned to the x-ms-lib-capability header to indicate to the server the library supports throttling X_MS_LIB_CAPABILITY_VALUE: "retry-after, h429", }; const Errors = { INVALID_GRANT_ERROR: "invalid_grant", CLIENT_MISMATCH_ERROR: "client_mismatch", }; /** * Region Discovery Sources */ const RegionDiscoverySources = { FAILED_AUTO_DETECTION: "1", INTERNAL_CACHE: "2", ENVIRONMENT_VARIABLE: "3", IMDS: "4", }; /** * Region Discovery Outcomes */ const RegionDiscoveryOutcomes = { CONFIGURED_NO_AUTO_DETECTION: "2", AUTO_DETECTION_REQUESTED_SUCCESSFUL: "4", AUTO_DETECTION_REQUESTED_FAILED: "5", }; /** * 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", }; const JsonWebTokenTypes = { Pop: "pop", }; // Token renewal offset default in seconds const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * AuthErrorMessage class containing string constants used by error codes and messages. */ const unexpectedError = "unexpected_error"; const postRequestFailed$1 = "post_request_failed"; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const AuthErrorMessages = { [unexpectedError]: "Unexpected error in authentication.", [postRequestFailed$1]: "Post request failed from the network, could be a 4xx/5xx or a network unavailability. Please check the exact error code for details.", }; /** * 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; } } function createAuthError(code, additionalMessage) { return new AuthError(code, additionalMessage ? `${AuthErrorMessages[code]} ${additionalMessage}` : AuthErrorMessages[code]); } /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * 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$1 = "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.12.0 2025-08-19 */ /* * 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$1]: "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.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const DEFAULT_CRYPTO_IMPLEMENTATION = { createNewGuid: () => { throw createClientAuthError(methodNotImplemented); }, base64Decode: () => { throw createClientAuthError(methodNotImplemented); }, base64Encode: () => { throw createClientAuthError(methodNotImplemented); }, base64UrlEncode: () => { throw createClientAuthError(methodNotImplemented); }, encodeKid: () => { throw createClientAuthError(methodNotImplemented); }, async getPublicKeyThumbprint() { throw createClientAuthError(methodNotImplemented); }, async removeTokenBindingKey() { throw createClientAuthError(methodNotImplemented); }, async clearKeystore() { throw createClientAuthError(methodNotImplemented); }, async signJwt() { throw createClientAuthError(methodNotImplemented); }, async hashString() { throw createClientAuthError(methodNotImplemented); }, }; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Log message level. */ exports.LogLevel = void 0; (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"; })(exports.LogLevel || (exports.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 = exports.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 : exports.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: exports.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} : ${exports.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: exports.LogLevel.Error, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs error messages with PII. */ errorPii(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Error, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs warning messages. */ warning(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Warning, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs warning messages with PII. */ warningPii(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Warning, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs info messages. */ info(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Info, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs info messages with PII. */ infoPii(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Info, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs verbose messages. */ verbose(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Verbose, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs verbose messages with PII. */ verbosePii(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Verbose, containsPii: true, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs trace messages. */ trace(message, correlationId) { this.logMessage(message, { logLevel: exports.LogLevel.Trace, containsPii: false, correlationId: correlationId || Constants.EMPTY_STRING, }); } /** * Logs trace messages with PII. */ tracePii(message, correlationId) { this.logMessage(message, { logLevel: exports.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.12.0 2025-08-19 */ /* eslint-disable header/header */ const name$1 = "@azure/msal-common"; const version$1 = "15.12.0"; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const AzureCloudInstance = { // AzureCloudInstance is not specified. None: "none"}; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * 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"; const invalidRequestMethodForEAR = "invalid_request_method_for_EAR"; const invalidAuthorizePostBodyParameters = "invalid_authorize_post_body_parameters"; /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * 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.", [invalidAuthorizePostBodyParameters]: "Invalid authorize post body parameters provided. If you are using authorizePostBodyParameters, the request method must be POST. Please check the request method and parameters.", [invalidRequestMethodForEAR]: "Invalid request method for EAR protocol mode. The request method cannot be GET when using EAR protocol mode. Please change the request method to POST.", }; /** * 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.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * @hidden */ class StringUtils { /** * Check if stringified object is empty * @param strObj */ static isEmptyObj(strObj) { if (strObj) { try { const obj = JSON.parse(strObj); return Object.keys(obj).length === 0; } catch (e) { } } return true; } static startsWith(str, search) { return str.indexOf(search) === 0; } static endsWith(str, search) { return (str.length >= search.length && str.lastIndexOf(search) === str.length - search.length); } /** * Parses string into an object. * * @param query */ static queryStringToObject(query) { const obj = {}; const params = query.split("&"); const decode = (s) => decodeURIComponent(s.replace(/\+/g, " ")); params.forEach((pair) => { if (pair.trim()) { const [key, value] = pair.split(/=(.+)/g, 2); // Split on the first occurence of the '=' character if (key && value) { obj[decode(key)] = decode(value); } } }); return obj; } /** * Trims entries in an array. * * @param arr */ static trimArrayEntries(arr) { return arr.map((entry) => entry.trim()); } /** * Removes empty strings from array * @param arr */ static removeEmptyStringsFromArray(arr) { return arr.filter((entry) => { return !!entry; }); } /** * Attempts to parse a string into JSON * @param str */ static jsonParseHelper(str) { try { return JSON.parse(str); } catch (e) { return null; } } /** * Tests if a given string matches a given pattern, with support for wildcards and queries. * @param pattern Wildcard pattern to string match. Supports "*" for wildcards and "?" for queries * @param input String to match against */ static matchPattern(pattern, input) { /** * Wildcard support: https://stackoverflow.com/a/3117248/4888559 * Queries: replaces "?" in string with escaped "\?" for regex test */ // eslint-disable-next-line security/detect-non-literal-regexp const regex = new RegExp(pattern .replace(/\\/g, "\\\\") .replace(/\*/g, "[^ ]*") .replace(/\?/g, "\\?")); return regex.test(input); } } /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * The ScopeSet class creates a set of scopes. Scopes are case-insensitive, unique values, so the Set object in JS makes * the most sense to implement for this class. All scopes are trimmed and converted to lower case strings in intersection and union functions * to ensure uniqueness of strings. */ class ScopeSet { constructor(inputScopes) { // Filter empty string and null/undefined array items const scopeArr = inputScopes ? StringUtils.trimArrayEntries([...inputScopes]) : []; const filteredInput = scopeArr ? StringUtils.removeEmptyStringsFromArray(scopeArr) : []; // Check if scopes array has at least one member if (!filteredInput || !filteredInput.length) { throw createClientConfigurationError(emptyInputScopesError); } this.scopes = new Set(); // Iterator in constructor not supported by IE11 filteredInput.forEach((scope) => this.scopes.add(scope)); } /** * Factory method to create ScopeSet from space-delimited string * @param inputScopeString * @param appClientId * @param scopesRequired */ static fromString(inputScopeString) { const scopeString = inputScopeString || Constants.EMPTY_STRING; const inputScopes = scopeString.split(" "); return new ScopeSet(inputScopes); } /** * Creates the set of scopes to search for in cache lookups * @param inputScopeString * @returns */ static createSearchScopes(inputScopeString) { // Handle empty scopes by using default OIDC scopes for cache lookup const scopesToUse = inputScopeString && inputScopeString.length > 0 ? inputScopeString : [...OIDC_DEFAULT_SCOPES]; const scopeSet = new ScopeSet(scopesToUse); if (!scopeSet.containsOnlyOIDCScopes()) { scopeSet.removeOIDCScopes(); } else { scopeSet.removeScope(Constants.OFFLINE_ACCESS_SCOPE); } return scopeSet; } /** * Check if a given scope is present in this set of scopes. * @param scope */ containsScope(scope) { const lowerCaseScopes = this.printScopesLowerCase().split(" "); const lowerCaseScopesSet = new ScopeSet(lowerCaseScopes); // compare lowercase scopes return scope ? lowerCaseScopesSet.scopes.has(scope.toLowerCase()) : false; } /** * Check if a set of scopes is present in this set of scopes. * @param scopeSet */ containsScopeSet(scopeSet) { if (!scopeSet || scopeSet.scopes.size <= 0) { return false; } return (this.scopes.size >= scopeSet.scopes.size && scopeSet.asArray().every((scope) => this.containsScope(scope))); } /** * Check if set of scopes contains only the defaults */ containsOnlyOIDCScopes() { let defaultScopeCount = 0; OIDC_SCOPES.forEach((defaultScope) => { if (this.containsScope(defaultScope)) { defaultScopeCount += 1; } }); return this.scopes.size === defaultScopeCount; } /** * Appends single scope if passed * @param newScope */ appendScope(newScope) { if (newScope) { this.scopes.add(newScope.trim()); } } /** * Appends multiple scopes if passed * @param newScopes */ appendScopes(newScopes) { try { newScopes.forEach((newScope) => this.appendScope(newScope)); } catch (e) { throw createClientAuthError(cannotAppendScopeSet); } } /** * Removes element from set of scopes. * @param scope */ removeScope(scope) { if (!scope) { throw createClientAuthError(cannotRemoveEmptyScope); } this.scopes.delete(scope.trim()); } /** * Removes default scopes from set of scopes * Primarily used to prevent cache misses if the default scopes are not returned from the server */ removeOIDCScopes() { OIDC_SCOPES.forEach((defaultScope) => { this.scopes.delete(defaultScope); }); } /** * Combines an array of scopes with the current set of scopes. * @param otherScopes */ unionScopeSets(otherScopes) { if (!otherScopes) { throw createClientAuthError(emptyInputScopeSet); } const unionScopes = new Set(); // Iterator in constructor not supported in IE11 otherScopes.scopes.forEach((scope) => unionScopes.add(scope.toLowerCase())); this.scopes.forEach((scope) => unionScopes.add(scope.toLowerCase())); return unionScopes; } /** * Check if scopes intersect between this set and another. * @param otherScopes */ intersectingScopeSets(otherScopes) { if (!otherScopes) { throw createClientAuthError(emptyInputScopeSet); } // Do not allow OIDC scopes to be the only intersecting scopes if (!otherScopes.containsOnlyOIDCScopes()) { otherScopes.removeOIDCScopes(); } const unionScopes = this.unionScopeSets(otherScopes); const sizeOtherScopes = otherScopes.getScopeCount(); const sizeThisScopes = this.getScopeCount(); const sizeUnionScopes = unionScopes.size; return sizeUnionScopes < sizeThisScopes + sizeOtherScopes; } /** * Returns size of set of scopes. */ getScopeCount() { return this.scopes.size; } /** * Returns the scopes as an array of string values */ asArray() { const array = []; this.scopes.forEach((val) => array.push(val)); return array; } /** * Prints scopes into a space-delimited string */ printScopes() { if (this.scopes) { const scopeArr = this.asArray(); return scopeArr.join(" "); } return Constants.EMPTY_STRING; } /** * Prints scopes into a space-delimited lower-case string (used for caching) */ printScopesLowerCase() { return this.printScopes().toLowerCase(); } } /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Returns true if tenantId matches the utid portion of homeAccountId * @param tenantId * @param homeAccountId * @returns */ function tenantIdMatchesHomeTenant(tenantId, homeAccountId) { return (!!tenantId && !!homeAccountId && tenantId === homeAccountId.split(".")[1]); } /** * Build tenant profile * @param homeAccountId - Home account identifier for this account object * @param localAccountId - Local account identifer for this account object * @param tenantId - Full tenant or organizational id that this account belongs to * @param idTokenClaims - Claims from the ID token * @returns */ function buildTenantProfile(homeAccountId, localAccountId, tenantId, idTokenClaims) { if (idTokenClaims) { const { oid, sub, tid, name, tfp, acr, preferred_username, upn, login_hint, } = idTokenClaims; /** * Since there is no way to determine if the authority is AAD or B2C, we exhaust all the possible claims that can serve as tenant ID with the following precedence: * tid - TenantID claim that identifies the tenant that issued the token in AAD. Expected in all AAD ID tokens, not present in B2C ID Tokens. * tfp - Trust Framework Policy claim that identifies the policy that was used to authenticate the user. Functions as tenant for B2C scenarios. * acr - Authentication Context Class Reference claim used only with older B2C policies. Fallback in case tfp is not present, but likely won't be present anyway. */ const tenantId = tid || tfp || acr || ""; return { tenantId: tenantId, localAccountId: oid || sub || "", name: name, username: preferred_username || upn || "", loginHint: login_hint, isHomeTenant: tenantIdMatchesHomeTenant(tenantId, homeAccountId), }; } else { return { tenantId, localAccountId, username: "", isHomeTenant: tenantIdMatchesHomeTenant(tenantId, homeAccountId), }; } } /** * Replaces account info that varies by tenant profile sourced from the ID token claims passed in with the tenant-specific account info * @param baseAccountInfo * @param idTokenClaims * @returns */ function updateAccountTenantProfileData(baseAccountInfo, tenantProfile, idTokenClaims, idTokenSecret) { let updatedAccountInfo = baseAccountInfo; // Tenant Profile overrides passed in account info if (tenantProfile) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { isHomeTenant, ...tenantProfileOverride } = tenantProfile; updatedAccountInfo = { ...baseAccountInfo, ...tenantProfileOverride }; } // ID token claims override passed in account info and tenant profile if (idTokenClaims) { // Ignore isHomeTenant, loginHint, and sid which are part of tenant profile but not base account info // eslint-disable-next-line @typescript-eslint/no-unused-vars const { isHomeTenant, ...claimsSourcedTenantProfile } = buildTenantProfile(baseAccountInfo.homeAccountId, baseAccountInfo.localAccountId, baseAccountInfo.tenantId, idTokenClaims); updatedAccountInfo = { ...updatedAccountInfo, ...claimsSourcedTenantProfile, idTokenClaims: idTokenClaims, idToken: idTokenSecret, }; return updatedAccountInfo; } return updatedAccountInfo; } /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Extract token by decoding the rawToken * * @param encodedToken */ function extractTokenClaims(encodedToken, base64Decode) { const jswPayload = getJWSPayload(encodedToken); // token will be decoded to get the username try { // base64Decode() should throw an error if there is an issue const base64Decoded = base64Decode(jswPayload); return JSON.parse(base64Decoded); } catch (err) { throw createClientAuthError(tokenParsingError); } } /** * decode a JWT * * @param authToken */ function getJWSPayload(authToken) { if (!authToken) { throw createClientAuthError(nullOrEmptyToken); } const tokenPartsRegex = /^([^\.\s]*)\.([^\.\s]+)\.([^\.\s]*)$/; const matches = tokenPartsRegex.exec(authToken); if (!matches || matches.length < 4) { throw createClientAuthError(tokenParsingError); } /** * const crackedToken = { * header: matches[1], * JWSPayload: matches[2], * JWSSig: matches[3], * }; */ return matches[2]; } /** * Determine if the token's max_age has transpired */ function checkMaxAge(authTime, maxAge) { /* * per https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest * To force an immediate re-authentication: If an app requires that a user re-authenticate prior to access, * provide a value of 0 for the max_age parameter and the AS will force a fresh login. */ const fiveMinuteSkew = 300000; // five minutes in milliseconds if (maxAge === 0 || Date.now() - fiveMinuteSkew > authTime + maxAge) { throw createClientAuthError(maxAgeTranspired); } } /*! @azure/msal-common v15.12.0 2025-08-19 */ /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * Canonicalizes a URL by making it lowercase and ensuring it ends with / * Inlined version of UrlString.canonicalizeUri to avoid circular dependency * @param url - URL to canonicalize * @returns Canonicalized URL */ function canonicalizeUrl(url) { if (!url) { return url; } let lowerCaseUrl = url.toLowerCase(); if (StringUtils.endsWith(lowerCaseUrl, "?")) { lowerCaseUrl = lowerCaseUrl.slice(0, -1); } else if (StringUtils.endsWith(lowerCaseUrl, "?/")) { lowerCaseUrl = lowerCaseUrl.slice(0, -2); } if (!StringUtils.endsWith(lowerCaseUrl, "/")) { lowerCaseUrl += "/"; } return lowerCaseUrl; } /** * Parses hash string from given string. Returns empty string if no hash symbol is found. * @param hashString */ function stripLeadingHashOrQuery(responseString) { if (responseString.startsWith("#/")) { return responseString.substring(2); } else if (responseString.startsWith("#") || responseString.startsWith("?")) { return responseString.substring(1); } return responseString; } /** * Returns URL hash as server auth code response object. */ function getDeserializedResponse(responseString) { // Check if given hash is empty if (!responseString || responseString.indexOf("=") < 0) { return null; } try { // Strip the # or ? symbol if present const normalizedResponse = stripLeadingHashOrQuery(responseString); // If # symbol was not present, above will return empty string, so give original hash value const deserializedHash = Object.fromEntries(new URLSearchParams(normalizedResponse)); // Check for known response properties if (deserializedHash.code || deserializedHash.ear_jwe || deserializedHash.error || deserializedHash.error_description || deserializedHash.state) { return deserializedHash; } } catch (e) { throw createClientAuthError(hashNotDeserialized); } return null; } /** * Utility to create a URL from the params map */ function mapToQueryString(parameters, encodeExtraParams = true, extraQueryParameters) { const queryParameterArray = new Array(); parameters.forEach((value, key) => { if (!encodeExtraParams && extraQueryParameters && key in extraQueryParameters) { queryParameterArray.push(`${key}=${value}`); } else { queryParameterArray.push(`${key}=${encodeURIComponent(value)}`); } }); return queryParameterArray.join("&"); } /** * Normalizes URLs for comparison by removing hash, canonicalizing, * and ensuring consistent URL encoding in query parameters. * This fixes redirect loops when URLs contain encoded characters like apostrophes (%27). * @param url - URL to normalize * @returns Normalized URL string for comparison */ function normalizeUrlForComparison(url) { if (!url) { return url; } // Remove hash first const urlWithoutHash = url.split("#")[0]; try { // Parse the URL to han