From 48c37cce591c0f0082ed6135549f51b013112c5d Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 5 Jun 2025 13:11:37 -0700 Subject: [PATCH 01/15] Add debug param to 1p-build --- .pipelines/1p-build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.pipelines/1p-build.yml b/.pipelines/1p-build.yml index 324ea883f2..b4eb0b819d 100644 --- a/.pipelines/1p-build.yml +++ b/.pipelines/1p-build.yml @@ -3,6 +3,10 @@ parameters: displayName: "NPM Install Timeout (Tests)" type: number default: 15 + - name: debug + displayName: "Debug" + type: boolean + default: false variables: CDP_DEFINITION_BUILD_COUNT: $[counter('', 0)] # needed for onebranch.pipeline.version task https://aka.ms/obpipelines/versioning LinuxContainerImage: "mcr.microsoft.com/onebranch/cbl-mariner/build:2.0" # Docker image which is used to build the project https://aka.ms/obpipelines/containers @@ -41,11 +45,13 @@ extends: libName: msal-common path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-browser path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-browser-1p @@ -55,18 +61,22 @@ extends: libName: msal-node path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-react path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-angular path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-node-extensions path: "extensions/" os: ["linux", "windows", "macOs"] + debug: ${{ parameters.debug }} From 99f93ea9a0983b85ea8a1f48e5c905dcfbfdd377 Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 5 Jun 2025 14:50:45 -0700 Subject: [PATCH 02/15] Add debug param to 1p-build (#7811) --- .pipelines/1p-build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.pipelines/1p-build.yml b/.pipelines/1p-build.yml index 324ea883f2..b4eb0b819d 100644 --- a/.pipelines/1p-build.yml +++ b/.pipelines/1p-build.yml @@ -3,6 +3,10 @@ parameters: displayName: "NPM Install Timeout (Tests)" type: number default: 15 + - name: debug + displayName: "Debug" + type: boolean + default: false variables: CDP_DEFINITION_BUILD_COUNT: $[counter('', 0)] # needed for onebranch.pipeline.version task https://aka.ms/obpipelines/versioning LinuxContainerImage: "mcr.microsoft.com/onebranch/cbl-mariner/build:2.0" # Docker image which is used to build the project https://aka.ms/obpipelines/containers @@ -41,11 +45,13 @@ extends: libName: msal-common path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-browser path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-browser-1p @@ -55,18 +61,22 @@ extends: libName: msal-node path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-react path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-angular path: "lib/" npmInstallTimeout: ${{ parameters.npmInstallTimeout }} + debug: ${{ parameters.debug }} - template: .pipelines/templates/ci-template.yml@1P parameters: libName: msal-node-extensions path: "extensions/" os: ["linux", "windows", "macOs"] + debug: ${{ parameters.debug }} From 75f62379475a5084ddc9dfe1efa436ad3a78024b Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 6 Jun 2025 13:06:31 -0400 Subject: [PATCH 03/15] Suppress false-positive CodeQL finding in NavigationClient (#7814) - Suppress false-positive CodeQL finding in NavigationClient --- ...-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json | 7 +++++++ lib/msal-browser/src/navigation/NavigationClient.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json diff --git a/change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json b/change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json new file mode 100644 index 0000000000..9cd8051beb --- /dev/null +++ b/change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "\u0016Suppress false-positive CodeQL finding in NavigationClient #7814", + "packageName": "@azure/msal-browser", + "email": "kshabelko@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/lib/msal-browser/src/navigation/NavigationClient.ts b/lib/msal-browser/src/navigation/NavigationClient.ts index 07af57fe7c..da78e700f5 100644 --- a/lib/msal-browser/src/navigation/NavigationClient.ts +++ b/lib/msal-browser/src/navigation/NavigationClient.ts @@ -41,9 +41,9 @@ export class NavigationClient implements INavigationClient { options: NavigationOptions ): Promise { if (options.noHistory) { - window.location.replace(url); + window.location.replace(url); // CodeQL [SM03712] Application owner controls the URL. User can't change it. } else { - window.location.assign(url); + window.location.assign(url); // CodeQL [SM03712] Application owner controls the URL. User can't change it. } return new Promise((resolve) => { From fa85c44c968dff980364382ed1fc7e8031fcdaaa Mon Sep 17 00:00:00 2001 From: Thomas Norling Date: Wed, 11 Jun 2025 16:34:32 -0700 Subject: [PATCH 04/15] [v4] Remove access tokens synchronously (#7822) Updates the remove access token cache logic to perform its steps synchronously. This is a pre-requisite for additional work to handle cache quota errors in the near future. This requires pushing the POP key removal (which is asynchronous) to the background, we will track failures around this in telemetry instead of throwing an error developers can't resolve anyway. --- ...-f0b6da2e-a220-4654-b1aa-6b4a0f4a921b.json | 7 ++ ...-042c27fe-92dc-4b48-b5e0-3d114168f8d4.json | 7 ++ ...-74ee63c8-51fb-422d-bbac-294ec6e9b0e5.json | 7 ++ .../src/cache/BrowserCacheManager.ts | 45 ++++---- .../controllers/NestedAppAuthController.ts | 4 +- .../src/controllers/StandardController.ts | 5 +- lib/msal-browser/src/crypto/CryptoOps.ts | 10 +- .../src/crypto/SignedHttpRequest.ts | 20 +++- .../BaseInteractionClient.ts | 7 +- .../PlatformAuthInteractionClient.ts | 9 +- .../src/interaction_client/PopupClient.ts | 5 +- .../src/interaction_client/RedirectClient.ts | 5 +- .../test/app/PublicClientApplication.spec.ts | 5 +- .../test/cache/BrowserCacheManager.spec.ts | 54 +++++---- .../test/cache/TestStorageManager.ts | 6 +- .../test/cache/TokenCache.spec.ts | 3 +- .../test/crypto/CryptoOps.spec.ts | 3 +- .../InteractionHandler.spec.ts | 7 +- lib/msal-common/apiReview/msal-common.api.md | 96 ++++++++-------- lib/msal-common/src/cache/CacheManager.ts | 83 +++++++------- .../src/cache/interface/ICacheManager.ts | 8 +- .../src/client/SilentFlowClient.ts | 4 +- .../src/config/ClientConfiguration.ts | 4 +- lib/msal-common/src/crypto/ICrypto.ts | 4 +- .../test/account/AuthToken.spec.ts | 73 +----------- .../test/account/ClientInfo.spec.ts | 67 +---------- .../test/authority/Authority.spec.ts | 4 +- .../test/cache/CacheManager.spec.ts | 80 +++----------- lib/msal-common/test/cache/MockCache.ts | 2 + .../test/cache/entities/AccountEntity.spec.ts | 96 +++++----------- .../test/client/ClientTestUtils.ts | 24 ++-- .../test/client/SilentFlowClient.spec.ts | 3 +- .../test/config/ClientConfiguration.spec.ts | 104 +----------------- .../test/crypto/PopTokenGenerator.spec.ts | 70 +----------- .../test/network/ThrottlingUtils.spec.ts | 21 +++- .../test/response/ResponseHandler.spec.ts | 74 ++----------- .../telemetry/ServerTelemetryManager.spec.ts | 4 +- .../test/utils/ProtocolUtils.spec.ts | 66 +---------- lib/msal-node/apiReview/msal-node.api.md | 4 +- lib/msal-node/src/cache/NodeStorage.ts | 9 +- lib/msal-node/src/cache/TokenCache.ts | 11 +- .../src/client/PublicClientApplication.ts | 5 +- lib/msal-node/src/crypto/CryptoProvider.ts | 2 +- lib/msal-node/test/client/ClientTestUtils.ts | 12 +- .../client/PublicClientApplication.spec.ts | 4 +- lib/msal-node/test/utils/TestConstants.ts | 2 +- 46 files changed, 371 insertions(+), 774 deletions(-) create mode 100644 change/@azure-msal-browser-f0b6da2e-a220-4654-b1aa-6b4a0f4a921b.json create mode 100644 change/@azure-msal-common-042c27fe-92dc-4b48-b5e0-3d114168f8d4.json create mode 100644 change/@azure-msal-node-74ee63c8-51fb-422d-bbac-294ec6e9b0e5.json diff --git a/change/@azure-msal-browser-f0b6da2e-a220-4654-b1aa-6b4a0f4a921b.json b/change/@azure-msal-browser-f0b6da2e-a220-4654-b1aa-6b4a0f4a921b.json new file mode 100644 index 0000000000..3b85e04ea4 --- /dev/null +++ b/change/@azure-msal-browser-f0b6da2e-a220-4654-b1aa-6b4a0f4a921b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "remove access tokens synchronously", + "packageName": "@azure/msal-browser", + "email": "thomas.norling@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-msal-common-042c27fe-92dc-4b48-b5e0-3d114168f8d4.json b/change/@azure-msal-common-042c27fe-92dc-4b48-b5e0-3d114168f8d4.json new file mode 100644 index 0000000000..689cfe1515 --- /dev/null +++ b/change/@azure-msal-common-042c27fe-92dc-4b48-b5e0-3d114168f8d4.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "remove access tokens synchronously", + "packageName": "@azure/msal-common", + "email": "thomas.norling@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@azure-msal-node-74ee63c8-51fb-422d-bbac-294ec6e9b0e5.json b/change/@azure-msal-node-74ee63c8-51fb-422d-bbac-294ec6e9b0e5.json new file mode 100644 index 0000000000..82ce195391 --- /dev/null +++ b/change/@azure-msal-node-74ee63c8-51fb-422d-bbac-294ec6e9b0e5.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "remove access tokens synchronously", + "packageName": "@azure/msal-node", + "email": "thomas.norling@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/lib/msal-browser/src/cache/BrowserCacheManager.ts b/lib/msal-browser/src/cache/BrowserCacheManager.ts index e5c02c9bdd..4098f85423 100644 --- a/lib/msal-browser/src/cache/BrowserCacheManager.ts +++ b/lib/msal-browser/src/cache/BrowserCacheManager.ts @@ -86,8 +86,6 @@ export class BrowserCacheManager extends CacheManager { protected cookieStorage: CookieStorage; // Logger instance protected logger: Logger; - // Telemetry perf client - protected performanceClient: IPerformanceClient; // Event Handler private eventHandler: EventHandler; @@ -100,7 +98,13 @@ export class BrowserCacheManager extends CacheManager { eventHandler: EventHandler, staticAuthorityOptions?: StaticAuthorityOptions ) { - super(clientId, cryptoImpl, logger, staticAuthorityOptions); + super( + clientId, + cryptoImpl, + logger, + performanceClient, + staticAuthorityOptions + ); this.cacheConfig = cacheConfig; this.logger = logger; this.internalStorage = new MemoryStorage(); @@ -118,7 +122,6 @@ export class BrowserCacheManager extends CacheManager { ); this.cookieStorage = new CookieStorage(); - this.performanceClient = performanceClient; this.eventHandler = eventHandler; } @@ -298,8 +301,8 @@ export class BrowserCacheManager extends CacheManager { * Extends inherited removeAccount function to include removal of the account key from the map * @param key */ - async removeAccount(key: string): Promise { - void super.removeAccount(key); + removeAccount(key: string, correlationId: string): void { + super.removeAccount(key, correlationId); this.removeAccountKeyFromMap(key); } @@ -307,8 +310,8 @@ export class BrowserCacheManager extends CacheManager { * Removes credentials associated with the provided account * @param account */ - async removeAccountContext(account: AccountEntity): Promise { - await super.removeAccountContext(account); + removeAccountContext(account: AccountEntity, correlationId: string): void { + super.removeAccountContext(account, correlationId); /** * @deprecated - Remove this in next major version in favor of more consistent LOGOUT event @@ -337,8 +340,8 @@ export class BrowserCacheManager extends CacheManager { * Removes given accessToken from the cache and from the key map * @param key */ - async removeAccessToken(key: string): Promise { - void super.removeAccessToken(key); + removeAccessToken(key: string, correlationId: string): void { + super.removeAccessToken(key, correlationId); this.removeTokenKey(key, CredentialType.ACCESS_TOKEN); } @@ -1012,9 +1015,9 @@ export class BrowserCacheManager extends CacheManager { /** * Clears all cache entries created by MSAL. */ - async clear(): Promise { + clear(correlationId: string): void { // Removes all accounts and their credentials - await this.removeAllAccounts(); + this.removeAllAccounts(correlationId); this.removeAppMetadata(); // Remove temp storage first to make sure any cookies are cleared @@ -1046,18 +1049,14 @@ export class BrowserCacheManager extends CacheManager { * @param correlationId {string} correlation id * @returns */ - async clearTokensAndKeysWithClaims( - performanceClient: IPerformanceClient, - correlationId: string - ): Promise { - performanceClient.addQueueMeasurement( + clearTokensAndKeysWithClaims(correlationId: string): void { + this.performanceClient.addQueueMeasurement( PerformanceEvents.ClearTokensAndKeysWithClaims, correlationId ); const tokenKeys = this.getTokenKeys(); - - const removedAccessTokens: Array> = []; + let removedAccessTokens = 0; tokenKeys.accessToken.forEach((key: string) => { // if the access token has claims in its key, remove the token key and the token const credential = this.getAccessTokenCredential(key); @@ -1065,15 +1064,15 @@ export class BrowserCacheManager extends CacheManager { credential?.requestedClaimsHash && key.includes(credential.requestedClaimsHash.toLowerCase()) ) { - removedAccessTokens.push(this.removeAccessToken(key)); + this.removeAccessToken(key, correlationId); + removedAccessTokens++; } }); - await Promise.all(removedAccessTokens); // warn if any access tokens are removed - if (removedAccessTokens.length > 0) { + if (removedAccessTokens > 0) { this.logger.warning( - `${removedAccessTokens.length} access tokens with claims in the cache keys have been removed from the cache.` + `${removedAccessTokens} access tokens with claims in the cache keys have been removed from the cache.` ); } } diff --git a/lib/msal-browser/src/controllers/NestedAppAuthController.ts b/lib/msal-browser/src/controllers/NestedAppAuthController.ts index 1a0d667d6c..9b6a6f52f8 100644 --- a/lib/msal-browser/src/controllers/NestedAppAuthController.ts +++ b/lib/msal-browser/src/controllers/NestedAppAuthController.ts @@ -505,9 +505,7 @@ export class NestedAppAuthController implements IController { currentAccount, authRequest, tokenKeys, - currentAccount.tenantId, - this.performanceClient, - authRequest.correlationId + currentAccount.tenantId ); // If there is no access token, log it and return null diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index d0802f2ada..40876014fa 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -23,6 +23,7 @@ import { getRequestThumbprint, AccountEntity, invokeAsync, + invoke, createClientAuthError, ClientAuthErrorCodes, AccountFilter, @@ -380,7 +381,7 @@ export class StandardController implements IController { "Claims-based caching is disabled. Clearing the previous cache with claims" ); - await invokeAsync( + invoke( this.browserStorage.clearTokensAndKeysWithClaims.bind( this.browserStorage ), @@ -388,7 +389,7 @@ export class StandardController implements IController { this.logger, this.performanceClient, initCorrelationId - )(this.performanceClient, initCorrelationId); + )(initCorrelationId); } this.config.system.asyncPopups && diff --git a/lib/msal-browser/src/crypto/CryptoOps.ts b/lib/msal-browser/src/crypto/CryptoOps.ts index fc30dfd56d..a0ebbbd7bf 100644 --- a/lib/msal-browser/src/crypto/CryptoOps.ts +++ b/lib/msal-browser/src/crypto/CryptoOps.ts @@ -4,6 +4,8 @@ */ import { + ClientAuthErrorCodes, + createClientAuthError, ICrypto, IPerformanceClient, JoseHeader, @@ -168,10 +170,14 @@ export class CryptoOps implements ICrypto { * Removes cryptographic keypair from key store matching the keyId passed in * @param kid */ - async removeTokenBindingKey(kid: string): Promise { + async removeTokenBindingKey(kid: string): Promise { await this.cache.removeItem(kid); const keyFound = await this.cache.containsKey(kid); - return !keyFound; + if (keyFound) { + throw createClientAuthError( + ClientAuthErrorCodes.bindingKeyNotRemoved + ); + } } /** diff --git a/lib/msal-browser/src/crypto/SignedHttpRequest.ts b/lib/msal-browser/src/crypto/SignedHttpRequest.ts index 0627ffdbe8..173abfe6fb 100644 --- a/lib/msal-browser/src/crypto/SignedHttpRequest.ts +++ b/lib/msal-browser/src/crypto/SignedHttpRequest.ts @@ -5,6 +5,8 @@ import { CryptoOps } from "./CryptoOps.js"; import { + ClientAuthError, + ClientAuthErrorCodes, Logger, LoggerOptions, PopTokenGenerator, @@ -71,6 +73,22 @@ export class SignedHttpRequest { * @returns If keys are properly deleted */ async removeKeys(publicKeyThumbprint: string): Promise { - return this.cryptoOps.removeTokenBindingKey(publicKeyThumbprint); + return this.cryptoOps + .removeTokenBindingKey(publicKeyThumbprint) + .then(() => true) + .catch((error) => { + /* + * @deprecated - To maintain public API signature, we return false if the error is due to the key still being present in indexedDB. + */ + if ( + error instanceof ClientAuthError && + error.errorCode === + ClientAuthErrorCodes.bindingKeyNotRemoved + ) { + return false; + } + + throw error; + }); } } diff --git a/lib/msal-browser/src/interaction_client/BaseInteractionClient.ts b/lib/msal-browser/src/interaction_client/BaseInteractionClient.ts index de519d660f..e477471bcb 100644 --- a/lib/msal-browser/src/interaction_client/BaseInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/BaseInteractionClient.ts @@ -102,8 +102,9 @@ export abstract class BaseInteractionClient { } // Clear given account. try { - await this.browserStorage.removeAccount( - AccountEntity.generateAccountCacheKey(account) + this.browserStorage.removeAccount( + AccountEntity.generateAccountCacheKey(account), + this.correlationId ); this.logger.verbose( "Cleared cache items belonging to the account provided in the logout request." @@ -120,7 +121,7 @@ export abstract class BaseInteractionClient { this.correlationId ); // Clear all accounts and tokens - await this.browserStorage.clear(); + this.browserStorage.clear(this.correlationId); // Clear any stray keys from IndexedDB await this.browserCrypto.clearKeystore(); } catch (e) { diff --git a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts index 7d3fd64537..ddcedd7532 100644 --- a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts @@ -714,11 +714,10 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { await this.browserStorage.setAccount(accountEntity, this.correlationId); // Remove any existing cached tokens for this account in browser storage - this.browserStorage.removeAccountContext(accountEntity).catch((e) => { - this.logger.error( - `Error occurred while removing account context from browser storage. ${e}` - ); - }); + this.browserStorage.removeAccountContext( + accountEntity, + this.correlationId + ); } /** diff --git a/lib/msal-browser/src/interaction_client/PopupClient.ts b/lib/msal-browser/src/interaction_client/PopupClient.ts index 45b025a54c..6c7448d8e2 100644 --- a/lib/msal-browser/src/interaction_client/PopupClient.ts +++ b/lib/msal-browser/src/interaction_client/PopupClient.ts @@ -503,8 +503,9 @@ export class PopupClient extends StandardInteractionClient { validRequest.postLogoutRedirectUri && authClient.authority.protocolMode === ProtocolMode.OIDC ) { - void this.browserStorage.removeAccount( - validRequest.account?.homeAccountId + this.browserStorage.removeAccount( + validRequest.account?.homeAccountId, + this.correlationId ); this.eventHandler.emitEvent( diff --git a/lib/msal-browser/src/interaction_client/RedirectClient.ts b/lib/msal-browser/src/interaction_client/RedirectClient.ts index c7eabc0c1f..262ab624f2 100644 --- a/lib/msal-browser/src/interaction_client/RedirectClient.ts +++ b/lib/msal-browser/src/interaction_client/RedirectClient.ts @@ -704,8 +704,9 @@ export class RedirectClient extends StandardInteractionClient { authClient.authority.endSessionEndpoint; } catch { if (validLogoutRequest.account?.homeAccountId) { - void this.browserStorage.removeAccount( - validLogoutRequest.account?.homeAccountId + this.browserStorage.removeAccount( + validLogoutRequest.account?.homeAccountId, + this.correlationId ); this.eventHandler.emitEvent( diff --git a/lib/msal-browser/test/app/PublicClientApplication.spec.ts b/lib/msal-browser/test/app/PublicClientApplication.spec.ts index 0568464263..22208a2d93 100644 --- a/lib/msal-browser/test/app/PublicClientApplication.spec.ts +++ b/lib/msal-browser/test/app/PublicClientApplication.spec.ts @@ -7779,7 +7779,10 @@ describe("PublicClientApplication.ts Class Unit Tests", () => { // Ensure account is present in the cache before removing it const cacheKey = AccountEntity.generateAccountCacheKey(accountInfo); - secondBrowserStorageInstance.removeAccount(cacheKey); + secondBrowserStorageInstance.removeAccount( + cacheKey, + RANDOM_TEST_GUID + ); }); }); diff --git a/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts b/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts index 6ba0daed97..e304bd8326 100644 --- a/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts +++ b/lib/msal-browser/test/cache/BrowserCacheManager.spec.ts @@ -269,8 +269,8 @@ describe("BrowserCacheManager tests", () => { }); afterEach(async () => { - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); }); it("setTemporaryCache", () => { @@ -334,11 +334,11 @@ describe("BrowserCacheManager tests", () => { ]); }); - it("clear()", async () => { + it("clear()", () => { browserSessionStorage.setTemporaryCache("cacheKey", cacheVal, true); browserLocalStorage.setTemporaryCache("cacheKey", cacheVal, true); - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); expect(browserSessionStorage.getKeys()).toHaveLength(0); expect(browserLocalStorage.getKeys()).toHaveLength(0); }); @@ -979,11 +979,9 @@ describe("BrowserCacheManager tests", () => { ).toEqual(testAT4); browserSessionStorage.clearTokensAndKeysWithClaims( - getDefaultPerformanceClient(), "test-correlation-id" ); browserLocalStorage.clearTokensAndKeysWithClaims( - getDefaultPerformanceClient(), "test-correlation-id" ); @@ -1366,8 +1364,8 @@ describe("BrowserCacheManager tests", () => { browserSessionStorage.getAuthorityMetadataKeys() ).toEqual(expect.arrayContaining([key])); - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); expect( browserSessionStorage.getAuthorityMetadata(key) ).toBeNull(); @@ -1686,8 +1684,8 @@ describe("BrowserCacheManager tests", () => { }); afterEach(async () => { - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); }); it("setTemporaryCache", () => { @@ -1732,11 +1730,11 @@ describe("BrowserCacheManager tests", () => { ).toBeNull(); }); - it("clear()", async () => { + it("clear()", () => { browserSessionStorage.setTemporaryCache("cacheKey", cacheVal, true); browserLocalStorage.setTemporaryCache("cacheKey", cacheVal, true); - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); expect(browserSessionStorage.getKeys()).toHaveLength(0); expect(browserLocalStorage.getKeys()).toHaveLength(0); }); @@ -2443,7 +2441,7 @@ describe("BrowserCacheManager tests", () => { ).toEqual(expect.arrayContaining([key])); }); - it("clear() removes AuthorityMetadataEntity from in-memory storage", async () => { + it("clear() removes AuthorityMetadataEntity from in-memory storage", () => { browserSessionStorage.setAuthorityMetadata(key, testObj); browserLocalStorage.setAuthorityMetadata(key, testObj); @@ -2460,8 +2458,8 @@ describe("BrowserCacheManager tests", () => { browserSessionStorage.getAuthorityMetadataKeys() ).toEqual(expect.arrayContaining([key])); - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); expect( browserSessionStorage.getAuthorityMetadata(key) ).toBeNull(); @@ -2592,9 +2590,9 @@ describe("BrowserCacheManager tests", () => { msalCacheKey = browserSessionStorage.generateCacheKey("cacheKey"); }); - afterEach(async () => { - await browserSessionStorage.clear(); - await browserLocalStorage.clear(); + afterEach(() => { + browserSessionStorage.clear(RANDOM_TEST_GUID); + browserLocalStorage.clear(RANDOM_TEST_GUID); }); it("setTempCache()", () => { @@ -2703,11 +2701,11 @@ describe("BrowserCacheManager tests", () => { expect(clearCookieSpy).toHaveBeenCalledTimes(3); }); - it("clear()", async () => { + it("clear()", () => { // sessionStorage browserSessionStorage.setTemporaryCache(msalCacheKey, cacheVal); expect(document.cookie).toContain(`${msalCacheKey}=${cacheVal}`); - await browserSessionStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); expect(browserSessionStorage.getKeys()).toHaveLength(0); expect(document.cookie).not.toContain( `${msalCacheKey}=${cacheVal}` @@ -2715,7 +2713,7 @@ describe("BrowserCacheManager tests", () => { // localStorage browserLocalStorage.setTemporaryCache(msalCacheKey, cacheVal); expect(document.cookie).toContain(`${msalCacheKey}=${cacheVal}`); - await browserLocalStorage.clear(); + browserLocalStorage.clear(RANDOM_TEST_GUID); expect(browserLocalStorage.getKeys()).toHaveLength(0); expect(document.cookie).not.toContain( `${msalCacheKey}=${cacheVal}` @@ -2723,7 +2721,7 @@ describe("BrowserCacheManager tests", () => { // browser memory browserMemoryStorage.setTemporaryCache(msalCacheKey, cacheVal); expect(document.cookie).toContain(`${msalCacheKey}=${cacheVal}`); - await browserMemoryStorage.clear(); + browserMemoryStorage.clear(RANDOM_TEST_GUID); expect(browserMemoryStorage.getKeys()).toHaveLength(0); expect(document.cookie).not.toContain( `${msalCacheKey}=${cacheVal}` @@ -2825,12 +2823,12 @@ describe("BrowserCacheManager tests", () => { expect(clearCookieSpy).toHaveBeenCalledTimes(3); }); - it("clear() with item that contains ==", async () => { + it("clear() with item that contains ==", () => { msalCacheKey = `${Constants.CACHE_PREFIX}.${TEST_STATE_VALUES.ENCODED_LIB_STATE}`; // sessionStorage browserSessionStorage.setTemporaryCache(msalCacheKey, cacheVal); expect(document.cookie).toContain(`${msalCacheKey}=${cacheVal}`); - await browserSessionStorage.clear(); + browserSessionStorage.clear(RANDOM_TEST_GUID); expect(browserSessionStorage.getKeys()).toHaveLength(0); expect(document.cookie).not.toContain( `${msalCacheKey}=${cacheVal}` @@ -2838,7 +2836,7 @@ describe("BrowserCacheManager tests", () => { // localStorage browserLocalStorage.setTemporaryCache(msalCacheKey, cacheVal); expect(document.cookie).toContain(`${msalCacheKey}=${cacheVal}`); - await browserLocalStorage.clear(); + browserLocalStorage.clear(RANDOM_TEST_GUID); expect(browserLocalStorage.getKeys()).toHaveLength(0); expect(document.cookie).not.toContain( `${msalCacheKey}=${cacheVal}` @@ -2846,7 +2844,7 @@ describe("BrowserCacheManager tests", () => { // browser memory browserMemoryStorage.setTemporaryCache(msalCacheKey, cacheVal); expect(document.cookie).toContain(`${msalCacheKey}=${cacheVal}`); - await browserMemoryStorage.clear(); + browserMemoryStorage.clear(RANDOM_TEST_GUID); expect(browserMemoryStorage.getKeys()).toHaveLength(0); expect(document.cookie).not.toContain( `${msalCacheKey}=${cacheVal}` diff --git a/lib/msal-browser/test/cache/TestStorageManager.ts b/lib/msal-browser/test/cache/TestStorageManager.ts index f3fceedaf3..b9e7c33588 100644 --- a/lib/msal-browser/test/cache/TestStorageManager.ts +++ b/lib/msal-browser/test/cache/TestStorageManager.ts @@ -13,10 +13,10 @@ import { ServerTelemetryEntity, ThrottlingEntity, AuthorityMetadataEntity, - ValidCredentialType, TokenKeys, CacheHelpers, } from "@azure/msal-common"; +import { RANDOM_TEST_GUID } from "../utils/StringConstants.js"; const ACCOUNT_KEYS = "ACCOUNT_KEYS"; const TOKEN_KEYS = "TOKEN_KEYS"; @@ -53,8 +53,8 @@ export class TestStorageManager extends CacheManager { } } - async removeAccount(key: string): Promise { - await super.removeAccount(key); + removeAccount(key: string): void { + super.removeAccount(key, RANDOM_TEST_GUID); this.removeAccountKeyFromMap(key); } diff --git a/lib/msal-browser/test/cache/TokenCache.spec.ts b/lib/msal-browser/test/cache/TokenCache.spec.ts index 7ebbbbaed8..d9c83d98fa 100644 --- a/lib/msal-browser/test/cache/TokenCache.spec.ts +++ b/lib/msal-browser/test/cache/TokenCache.spec.ts @@ -29,6 +29,7 @@ import { import { BrowserCacheLocation } from "../../src/utils/BrowserConstants.js"; import { ID_TOKEN_CLAIMS, + RANDOM_TEST_GUID, TEST_CONFIG, TEST_DATA_CLIENT_INFO, TEST_TOKENS, @@ -175,7 +176,7 @@ describe("TokenCache tests", () => { }); afterEach(() => { - browserStorage.clear(); + browserStorage.clear(RANDOM_TEST_GUID); }); it("loads id token with a request account", async () => { diff --git a/lib/msal-browser/test/crypto/CryptoOps.spec.ts b/lib/msal-browser/test/crypto/CryptoOps.spec.ts index e9a19117de..6f400ed6b5 100644 --- a/lib/msal-browser/test/crypto/CryptoOps.spec.ts +++ b/lib/msal-browser/test/crypto/CryptoOps.spec.ts @@ -301,10 +301,9 @@ describe("CryptoOps.ts Unit Tests", () => { resourceRequestUri: TEST_URIS.TEST_AUTH_ENDPT_WITH_PARAMS, } as BaseAuthRequest); const key = mockDatabase["TestDB.keys"][pkThumbprint]; - const keyDeleted = await cryptoObj.removeTokenBindingKey(pkThumbprint); + await cryptoObj.removeTokenBindingKey(pkThumbprint); expect(key).not.toBe(undefined); expect(mockDatabase["TestDB.keys"][pkThumbprint]).toBe(undefined); - expect(keyDeleted).toBe(true); }, 30000); it("signJwt() throws signingKeyNotFoundInStorage error if signing keypair is not found in storage", async () => { diff --git a/lib/msal-browser/test/interaction_handler/InteractionHandler.spec.ts b/lib/msal-browser/test/interaction_handler/InteractionHandler.spec.ts index 19ef024a7c..c46fca2a05 100644 --- a/lib/msal-browser/test/interaction_handler/InteractionHandler.spec.ts +++ b/lib/msal-browser/test/interaction_handler/InteractionHandler.spec.ts @@ -139,8 +139,8 @@ const cryptoInterface = { signJwt: async (): Promise => { return "signedJwt"; }, - removeTokenBindingKey: async (): Promise => { - return Promise.resolve(true); + removeTokenBindingKey: async (): Promise => { + return Promise.resolve(); }, clearKeystore: async (): Promise => { return Promise.resolve(true); @@ -222,7 +222,8 @@ describe("InteractionHandler.ts Unit Tests", () => { storageInterface: new TestStorageManager( TEST_CONFIG.MSAL_CLIENT_ID, cryptoInterface, - logger + logger, + new StubPerformanceClient() ), networkInterface: { sendGetRequestAsync: async ( diff --git a/lib/msal-common/apiReview/msal-common.api.md b/lib/msal-common/apiReview/msal-common.api.md index 36cdb1bea2..68a7bd456d 100644 --- a/lib/msal-common/apiReview/msal-common.api.md +++ b/lib/msal-common/apiReview/msal-common.api.md @@ -1045,7 +1045,7 @@ export { CacheHelpers } // // @internal export abstract class CacheManager implements ICacheManager { - constructor(clientId: string, cryptoImpl: ICrypto, logger: Logger, staticAuthorityOptions?: StaticAuthorityOptions); + constructor(clientId: string, cryptoImpl: ICrypto, logger: Logger, performanceClient: IPerformanceClient, staticAuthorityOptions?: StaticAuthorityOptions); // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -1066,9 +1066,7 @@ export abstract class CacheManager implements ICacheManager { // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' - getAccessToken(account: AccountInfo, request: BaseAuthRequest, tokenKeys?: TokenKeys, targetRealm?: string, performanceClient?: IPerformanceClient, correlationId?: string): AccessTokenEntity | null; + getAccessToken(account: AccountInfo, request: BaseAuthRequest, tokenKeys?: TokenKeys, targetRealm?: string): AccessTokenEntity | null; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen abstract getAccessTokenCredential(accessTokenKey: string): AccessTokenEntity | null; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -1140,6 +1138,8 @@ export abstract class CacheManager implements ICacheManager { protected isAuthorityMetadata(key: string): boolean; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen isCredentialKey(key: string): boolean; + // (undocumented) + protected performanceClient: IPerformanceClient; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen readAccountFromCache(account: AccountInfo): AccountEntity | null; readAppMetadataFromCache(environment: string): AppMetadataEntity | null; @@ -1147,12 +1147,12 @@ export abstract class CacheManager implements ICacheManager { // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen refreshTokenKeyMatchesFilter(inputKey: string, filter: CredentialFilter): boolean; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - removeAccessToken(key: string): Promise; + removeAccessToken(key: string, correlationId: string): void; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - removeAccount(accountKey: string): Promise; + removeAccount(accountKey: string, correlationId: string): void; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - removeAccountContext(account: AccountEntity): Promise; - removeAllAccounts(): Promise; + removeAccountContext(account: AccountEntity, correlationId: string): void; + removeAllAccounts(correlationId: string): void; removeAppMetadata(): boolean; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen removeIdToken(key: string): void; @@ -2476,7 +2476,7 @@ export interface ICrypto { // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen hashString(plainText: string): Promise; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - removeTokenBindingKey(kid: string): Promise; + removeTokenBindingKey(kid: string): Promise; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen signJwt(payload: SignedHttpRequest, kid: string, shrOptions?: ShrOptions, correlationId?: string): Promise; } @@ -4579,42 +4579,42 @@ const X_MS_LIB_CAPABILITY = "x-ms-lib-capability"; // src/authority/Authority.ts:818:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/authority/Authority.ts:1009:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/authority/AuthorityOptions.ts:26:5 - (ae-forgotten-export) The symbol "CloudInstanceDiscoveryResponse" needs to be exported by the entry point index.d.ts -// src/cache/CacheManager.ts:289:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:290:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:572:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1559:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1560:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1574:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1575:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1595:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1596:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1605:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1606:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1622:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1623:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1637:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1638:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1671:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1672:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1686:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1687:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1698:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1699:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1710:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1711:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1722:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1723:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1740:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1741:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1765:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1766:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1785:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1786:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1805:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1806:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1817:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1818:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/cache/CacheManager.ts:1826:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:292:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:293:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:575:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1564:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1565:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1579:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1580:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1600:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1601:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1610:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1611:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1627:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1628:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1642:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1643:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1676:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1677:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1691:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1692:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1703:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1704:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1715:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1716:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1727:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1728:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1745:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1746:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1770:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1771:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1790:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1791:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1810:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1811:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1822:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1823:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/cache/CacheManager.ts:1831:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/cache/utils/CacheTypes.ts:94:53 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag // src/cache/utils/CacheTypes.ts:94:43 - (tsdoc-malformed-html-name) Invalid HTML element: An HTML name must be an ASCII letter followed by zero or more letters, digits, or hyphens // src/client/AuthorizationCodeClient.ts:158:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -4625,9 +4625,9 @@ const X_MS_LIB_CAPABILITY = "x-ms-lib-capability"; // src/client/RefreshTokenClient.ts:287:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/client/RefreshTokenClient.ts:288:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/client/RefreshTokenClient.ts:339:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/client/SilentFlowClient.ts:172:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/config/ClientConfiguration.ts:50:5 - (ae-forgotten-export) The symbol "ClientCredentials" needs to be exported by the entry point index.d.ts -// src/config/ClientConfiguration.ts:52:5 - (ae-forgotten-export) The symbol "TelemetryOptions" needs to be exported by the entry point index.d.ts +// src/client/SilentFlowClient.ts:170:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/config/ClientConfiguration.ts:51:5 - (ae-forgotten-export) The symbol "ClientCredentials" needs to be exported by the entry point index.d.ts +// src/config/ClientConfiguration.ts:53:5 - (ae-forgotten-export) The symbol "TelemetryOptions" needs to be exported by the entry point index.d.ts // src/index.ts:8:12 - (tsdoc-characters-after-block-tag) The token "@azure" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" // src/index.ts:8:4 - (tsdoc-undefined-tag) The TSDoc tag "@module" is not defined in this configuration // src/request/AuthenticationHeaderParser.ts:74:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen diff --git a/lib/msal-common/src/cache/CacheManager.ts b/lib/msal-common/src/cache/CacheManager.ts index 2b95feaf49..87c13b5122 100644 --- a/lib/msal-common/src/cache/CacheManager.ts +++ b/lib/msal-common/src/cache/CacheManager.ts @@ -64,17 +64,20 @@ export abstract class CacheManager implements ICacheManager { // Instance of logger for functions defined in the msal-common layer private commonLogger: Logger; private staticAuthorityOptions?: StaticAuthorityOptions; + protected performanceClient: IPerformanceClient; constructor( clientId: string, cryptoImpl: ICrypto, logger: Logger, + performanceClient: IPerformanceClient, staticAuthorityOptions?: StaticAuthorityOptions ) { this.clientId = clientId; this.cryptoImpl = cryptoImpl; this.commonLogger = logger.clone(name, version); this.staticAuthorityOptions = staticAuthorityOptions; + this.performanceClient = performanceClient; } /** @@ -588,7 +591,6 @@ export abstract class CacheManager implements ICacheManager { const tokenKeys = this.getTokenKeys(); const currentScopes = ScopeSet.fromString(credential.target); - const removedAccessTokens: Array> = []; tokenKeys.accessToken.forEach((key) => { if ( !this.accessTokenKeyMatchesFilter(key, accessTokenFilter, false) @@ -604,11 +606,10 @@ export abstract class CacheManager implements ICacheManager { ) { const tokenScopeSet = ScopeSet.fromString(tokenEntity.target); if (tokenScopeSet.intersectingScopeSets(currentScopes)) { - removedAccessTokens.push(this.removeAccessToken(key)); + this.removeAccessToken(key, correlationId); } } }); - await Promise.all(removedAccessTokens); await this.setAccessTokenCredential(credential, correlationId); } @@ -959,27 +960,24 @@ export abstract class CacheManager implements ICacheManager { /** * Removes all accounts and related tokens from cache. */ - async removeAllAccounts(): Promise { + removeAllAccounts(correlationId: string): void { const allAccountKeys = this.getAccountKeys(); - const removedAccounts: Array> = []; allAccountKeys.forEach((cacheKey) => { - removedAccounts.push(this.removeAccount(cacheKey)); + this.removeAccount(cacheKey, correlationId); }); - - await Promise.all(removedAccounts); } /** * Removes the account and related tokens for a given account key * @param account */ - async removeAccount(accountKey: string): Promise { + removeAccount(accountKey: string, correlationId: string): void { const account = this.getAccount(accountKey, this.commonLogger); if (!account) { return; } - await this.removeAccountContext(account); + this.removeAccountContext(account, correlationId); this.removeItem(accountKey); } @@ -987,10 +985,9 @@ export abstract class CacheManager implements ICacheManager { * Removes credentials associated with the provided account * @param account */ - async removeAccountContext(account: AccountEntity): Promise { + removeAccountContext(account: AccountEntity, correlationId: string): void { const allTokenKeys = this.getTokenKeys(); const accountId = account.generateAccountId(); - const removedCredentials: Array> = []; allTokenKeys.idToken.forEach((key) => { if (key.indexOf(accountId) === 0) { @@ -1000,7 +997,7 @@ export abstract class CacheManager implements ICacheManager { allTokenKeys.accessToken.forEach((key) => { if (key.indexOf(accountId) === 0) { - removedCredentials.push(this.removeAccessToken(key)); + this.removeAccessToken(key, correlationId); } }); @@ -1009,20 +1006,20 @@ export abstract class CacheManager implements ICacheManager { this.removeRefreshToken(key); } }); - - await Promise.all(removedCredentials); } /** * returns a boolean if the given credential is removed * @param credential */ - async removeAccessToken(key: string): Promise { + removeAccessToken(key: string, correlationId: string): void { const credential = this.getAccessTokenCredential(key); if (!credential) { return; } + this.removeItem(key); + // Remove Token Binding Key from key store for PoP Tokens Credentials if ( credential.credentialType.toLowerCase() === @@ -1034,18 +1031,21 @@ export abstract class CacheManager implements ICacheManager { const kid = accessTokenWithAuthSchemeEntity.keyId; if (kid) { - try { - await this.cryptoImpl.removeTokenBindingKey(kid); - } catch (error) { - throw createClientAuthError( - ClientAuthErrorCodes.bindingKeyNotRemoved - ); - } + void this.cryptoImpl + .removeTokenBindingKey(kid) + .catch(() => { + this.commonLogger.error( + `Failed to remove token binding key ${kid}`, + correlationId + ); + this.performanceClient?.incrementFields( + { removeTokenBindingKeyFailure: 1 }, + correlationId + ); + }); } } } - - return this.removeItem(key); } /** @@ -1239,17 +1239,18 @@ export abstract class CacheManager implements ICacheManager { * @param request {BaseAuthRequest} * @param tokenKeys {?TokenKeys} * @param performanceClient {?IPerformanceClient} - * @param correlationId {?string} */ getAccessToken( account: AccountInfo, request: BaseAuthRequest, tokenKeys?: TokenKeys, - targetRealm?: string, - performanceClient?: IPerformanceClient, - correlationId?: string + targetRealm?: string ): AccessTokenEntity | null { - this.commonLogger.trace("CacheManager - getAccessToken called"); + const correlationId = request.correlationId; + this.commonLogger.trace( + "CacheManager - getAccessToken called", + correlationId + ); const scopes = ScopeSet.createSearchScopes(request.scopes); const authScheme = request.authenticationScheme || AuthenticationScheme.BEARER; @@ -1301,27 +1302,31 @@ export abstract class CacheManager implements ICacheManager { const numAccessTokens = accessTokens.length; if (numAccessTokens < 1) { this.commonLogger.info( - "CacheManager:getAccessToken - No token found" + "CacheManager:getAccessToken - No token found", + correlationId ); return null; } else if (numAccessTokens > 1) { this.commonLogger.info( - "CacheManager:getAccessToken - Multiple access tokens found, clearing them" + "CacheManager:getAccessToken - Multiple access tokens found, clearing them", + correlationId ); accessTokens.forEach((accessToken) => { - void this.removeAccessToken(generateCredentialKey(accessToken)); - }); - if (performanceClient && correlationId) { - performanceClient.addFields( - { multiMatchedAT: accessTokens.length }, + this.removeAccessToken( + generateCredentialKey(accessToken), correlationId ); - } + }); + this.performanceClient.addFields( + { multiMatchedAT: accessTokens.length }, + correlationId + ); return null; } this.commonLogger.info( - "CacheManager:getAccessToken - Returning access token" + "CacheManager:getAccessToken - Returning access token", + correlationId ); return accessTokens[0]; } diff --git a/lib/msal-common/src/cache/interface/ICacheManager.ts b/lib/msal-common/src/cache/interface/ICacheManager.ts index 2d1c879d8d..555da7be36 100644 --- a/lib/msal-common/src/cache/interface/ICacheManager.ts +++ b/lib/msal-common/src/cache/interface/ICacheManager.ts @@ -197,19 +197,19 @@ export interface ICacheManager { /** * Removes all accounts and related tokens from cache. */ - removeAllAccounts(): Promise; + removeAllAccounts(correlationId: string): void; /** * returns a boolean if the given account is removed * @param account */ - removeAccount(accountKey: string): Promise; + removeAccount(accountKey: string, correlationId: string): void; /** * returns a boolean if the given account is removed * @param account */ - removeAccountContext(account: AccountEntity): Promise; + removeAccountContext(account: AccountEntity, correlationId: string): void; /** * @param key @@ -219,7 +219,7 @@ export interface ICacheManager { /** * @param key */ - removeAccessToken(key: string): Promise; + removeAccessToken(key: string, correlationId: string): void; /** * @param key diff --git a/lib/msal-common/src/client/SilentFlowClient.ts b/lib/msal-common/src/client/SilentFlowClient.ts index 0801c88ee0..439ca759d9 100644 --- a/lib/msal-common/src/client/SilentFlowClient.ts +++ b/lib/msal-common/src/client/SilentFlowClient.ts @@ -75,9 +75,7 @@ export class SilentFlowClient extends BaseClient { request.account, request, tokenKeys, - requestTenantId, - this.performanceClient, - request.correlationId + requestTenantId ); if (!cachedAccessToken) { diff --git a/lib/msal-common/src/config/ClientConfiguration.ts b/lib/msal-common/src/config/ClientConfiguration.ts index 2bee204e4d..06be508c2a 100644 --- a/lib/msal-common/src/config/ClientConfiguration.ts +++ b/lib/msal-common/src/config/ClientConfiguration.ts @@ -23,6 +23,7 @@ import { ClientAuthErrorCodes, createClientAuthError, } from "../error/ClientAuthError.js"; +import { StubPerformanceClient } from "../telemetry/performance/StubPerformanceClient.js"; /** * Use the configuration object to configure MSAL Modules and initialize the base interfaces for MSAL. @@ -260,7 +261,8 @@ export function buildClientConfiguration({ new DefaultStorageClass( userAuthOptions.clientId, DEFAULT_CRYPTO_IMPLEMENTATION, - new Logger(loggerOptions) + new Logger(loggerOptions), + new StubPerformanceClient() ), networkInterface: networkImplementation || DEFAULT_NETWORK_IMPLEMENTATION, diff --git a/lib/msal-common/src/crypto/ICrypto.ts b/lib/msal-common/src/crypto/ICrypto.ts index 10425a536a..4f9f98e1cd 100644 --- a/lib/msal-common/src/crypto/ICrypto.ts +++ b/lib/msal-common/src/crypto/ICrypto.ts @@ -70,7 +70,7 @@ export interface ICrypto { * Removes cryptographic keypair from key store matching the keyId passed in * @param kid */ - removeTokenBindingKey(kid: string): Promise; + removeTokenBindingKey(kid: string): Promise; /** * Removes all cryptographic keys from IndexedDB storage */ @@ -111,7 +111,7 @@ export const DEFAULT_CRYPTO_IMPLEMENTATION: ICrypto = { async getPublicKeyThumbprint(): Promise { throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented); }, - async removeTokenBindingKey(): Promise { + async removeTokenBindingKey(): Promise { throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented); }, async clearKeystore(): Promise { diff --git a/lib/msal-common/test/account/AuthToken.spec.ts b/lib/msal-common/test/account/AuthToken.spec.ts index ccd778618e..8e46f20bfd 100644 --- a/lib/msal-common/test/account/AuthToken.spec.ts +++ b/lib/msal-common/test/account/AuthToken.spec.ts @@ -1,84 +1,17 @@ import * as AuthToken from "../../src/account/AuthToken"; -import { - TEST_DATA_CLIENT_INFO, - RANDOM_TEST_GUID, - TEST_POP_VALUES, - TEST_CRYPTO_VALUES, - TEST_TOKENS, - ID_TOKEN_CLAIMS, -} from "../test_kit/StringConstants"; +import { TEST_TOKENS, ID_TOKEN_CLAIMS } from "../test_kit/StringConstants"; import { ICrypto } from "../../src/crypto/ICrypto"; import { ClientAuthErrorMessage, ClientAuthError, } from "../../src/error/ClientAuthError"; import { AuthError } from "../../src/error/AuthError"; - -const TEST_ID_TOKEN_PAYLOAD = TEST_TOKENS.IDTOKEN_V2.split(".")[1]; +import { mockCrypto } from "../client/ClientTestUtils.js"; describe("AuthToken.ts Class Unit Tests", () => { let cryptoInterface: ICrypto; beforeEach(() => { - cryptoInterface = { - createNewGuid(): string { - return RANDOM_TEST_GUID; - }, - base64Decode(input: string): string { - switch (input) { - case TEST_POP_VALUES.ENCODED_REQ_CNF: - return TEST_POP_VALUES.DECODED_REQ_CNF; - case TEST_DATA_CLIENT_INFO.TEST_RAW_CLIENT_INFO: - return TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO; - case TEST_ID_TOKEN_PAYLOAD: - return JSON.stringify(ID_TOKEN_CLAIMS); - default: - return input; - } - }, - base64Encode(input: string): string { - switch (input) { - case "123-test-uid": - return "MTIzLXRlc3QtdWlk"; - case "456-test-uid": - return "NDU2LXRlc3QtdWlk"; - case TEST_POP_VALUES.DECODED_REQ_CNF: - return TEST_POP_VALUES.ENCODED_REQ_CNF; - default: - return input; - } - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return ""; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, - }; + cryptoInterface = mockCrypto; }); afterEach(() => { diff --git a/lib/msal-common/test/account/ClientInfo.spec.ts b/lib/msal-common/test/account/ClientInfo.spec.ts index bc3111ff29..835637dfb6 100644 --- a/lib/msal-common/test/account/ClientInfo.spec.ts +++ b/lib/msal-common/test/account/ClientInfo.spec.ts @@ -3,12 +3,7 @@ import { buildClientInfoFromHomeAccountId, ClientInfo, } from "../../src/account/ClientInfo"; -import { - TEST_DATA_CLIENT_INFO, - RANDOM_TEST_GUID, - TEST_POP_VALUES, - TEST_CRYPTO_VALUES, -} from "../test_kit/StringConstants"; +import { TEST_DATA_CLIENT_INFO } from "../test_kit/StringConstants"; import { ICrypto } from "../../src/crypto/ICrypto"; import { ClientAuthError, @@ -17,69 +12,13 @@ import { createClientAuthError, } from "../../src/error/ClientAuthError"; import { Constants } from "../../src"; +import { mockCrypto } from "../client/ClientTestUtils.js"; describe("ClientInfo.ts Class Unit Tests", () => { describe("buildClientInfo()", () => { let cryptoInterface: ICrypto; beforeEach(() => { - cryptoInterface = { - createNewGuid(): string { - return RANDOM_TEST_GUID; - }, - base64Decode(input: string): string { - switch (input) { - case TEST_POP_VALUES.ENCODED_REQ_CNF: - return TEST_POP_VALUES.DECODED_REQ_CNF; - case TEST_DATA_CLIENT_INFO.TEST_RAW_CLIENT_INFO: - return TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO; - default: - return input; - } - }, - base64Encode(input: string): string { - switch (input) { - case "123-test-uid": - return "MTIzLXRlc3QtdWlk"; - case "456-test-uid": - return "NDU2LXRlc3QtdWlk"; - case TEST_POP_VALUES.DECODED_REQ_CNF: - return TEST_POP_VALUES.ENCODED_REQ_CNF; - default: - return input; - } - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return ""; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, - }; + cryptoInterface = mockCrypto; }); afterEach(() => { diff --git a/lib/msal-common/test/authority/Authority.spec.ts b/lib/msal-common/test/authority/Authority.spec.ts index f8fbcffaf4..73c7671427 100644 --- a/lib/msal-common/test/authority/Authority.spec.ts +++ b/lib/msal-common/test/authority/Authority.spec.ts @@ -46,6 +46,7 @@ import { import { RegionDiscovery } from "../../src/authority/RegionDiscovery"; import { InstanceDiscoveryMetadata } from "../../src/authority/AuthorityMetadata"; import * as authorityMetadata from "../../src/authority/AuthorityMetadata"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; let mockStorage: MockStorageClass; @@ -87,7 +88,8 @@ describe("Authority.ts Class Unit Tests", () => { mockStorage = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - logger + logger, + new StubPerformanceClient() ); }); afterEach(() => { diff --git a/lib/msal-common/test/cache/CacheManager.spec.ts b/lib/msal-common/test/cache/CacheManager.spec.ts index 06f1966e8c..a7ee725587 100644 --- a/lib/msal-common/test/cache/CacheManager.spec.ts +++ b/lib/msal-common/test/cache/CacheManager.spec.ts @@ -23,11 +23,8 @@ import { TEST_TOKEN_LIFETIMES, ID_TOKEN_ALT_CLAIMS, GUEST_ID_TOKEN_CLAIMS, + RANDOM_TEST_GUID, } from "../test_kit/StringConstants.js"; -import { - ClientAuthErrorCodes, - createClientAuthError, -} from "../../src/error/ClientAuthError.js"; import { AccountInfo } from "../../src/account/AccountInfo.js"; import { MockCache } from "./MockCache.js"; import { buildAccountFromIdTokenClaims, buildIdToken } from "msal-test-utils"; @@ -1561,22 +1558,22 @@ describe("CacheManager.ts test cases", () => { ).toBeUndefined(); }); - it("removeAllAccounts", async () => { + it("removeAllAccounts", () => { const accountsBeforeRemove = mockCache.cacheManager.getAllAccounts(); - await mockCache.cacheManager.removeAllAccounts(); + mockCache.cacheManager.removeAllAccounts(RANDOM_TEST_GUID); const accountsAfterRemove = mockCache.cacheManager.getAllAccounts(); expect(accountsBeforeRemove).toHaveLength(3); expect(accountsAfterRemove).toHaveLength(0); }); - it("removeAccount", async () => { + it("removeAccount", () => { const accountToRemove = buildAccountFromIdTokenClaims(ID_TOKEN_CLAIMS); const accountToRemoveKey = accountToRemove.generateAccountKey(); expect( mockCache.cacheManager.getAccount(accountToRemoveKey) ).not.toBeNull(); - await mockCache.cacheManager.removeAccount(accountToRemoveKey); + mockCache.cacheManager.removeAccount(accountToRemoveKey); expect( mockCache.cacheManager.getAccount(accountToRemoveKey) ).toBeNull(); @@ -1596,8 +1593,9 @@ describe("CacheManager.ts test cases", () => { extendedExpiresOn: "4600", }; - await mockCache.cacheManager.removeAccessToken( - CacheHelpers.generateCredentialKey(at) + mockCache.cacheManager.removeAccessToken( + CacheHelpers.generateCredentialKey(at), + RANDOM_TEST_GUID ); const atKey = CacheHelpers.generateCredentialKey(at); expect(mockCache.cacheManager.getAccount(atKey)).toBeNull(); @@ -1624,8 +1622,9 @@ describe("CacheManager.ts test cases", () => { "removeTokenBindingKey" ); - await mockCache.cacheManager.removeAccessToken( - CacheHelpers.generateCredentialKey(atWithAuthScheme) + mockCache.cacheManager.removeAccessToken( + CacheHelpers.generateCredentialKey(atWithAuthScheme), + RANDOM_TEST_GUID ); const atKey = CacheHelpers.generateCredentialKey(atWithAuthScheme); expect(mockCache.cacheManager.getAccount(atKey)).toBeNull(); @@ -1655,45 +1654,15 @@ describe("CacheManager.ts test cases", () => { "removeTokenBindingKey" ); - await mockCache.cacheManager.removeAccessToken( - CacheHelpers.generateCredentialKey(atWithAuthScheme) + mockCache.cacheManager.removeAccessToken( + CacheHelpers.generateCredentialKey(atWithAuthScheme), + RANDOM_TEST_GUID ); const atKey = CacheHelpers.generateCredentialKey(atWithAuthScheme); expect(mockCache.cacheManager.getAccount(atKey)).toBeNull(); expect(removeTokenBindingKeySpy).toHaveBeenCalledTimes(0); }); - it("throws bindingKeyNotRemoved error when key is not deleted from storage", async () => { - const atWithAuthScheme = { - environment: "login.microsoftonline.com", - credentialType: CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME, - secret: "an access token", - realm: "microsoft", - target: "scope1 scope2 scope3", - clientId: "mock_client_id", - cachedAt: "1000", - homeAccountId: "uid.utid", - extendedExpiresOn: "4600", - expiresOn: "4600", - keyId: "V6N_HMPagNpYS_wxM14X73q3eWzbTr9Z31RyHkIcN0Y", - tokenType: AuthenticationScheme.POP, - }; - - jest.spyOn(mockCrypto, "removeTokenBindingKey").mockImplementation( - (keyId: string): Promise => { - return Promise.reject(); - } - ); - - return await expect( - mockCache.cacheManager.removeAccessToken( - CacheHelpers.generateCredentialKey(atWithAuthScheme) - ) - ).rejects.toThrow( - createClientAuthError(ClientAuthErrorCodes.bindingKeyNotRemoved) - ); - }); - it("getAccessToken matches multiple tokens, removes them and returns null", (done) => { mockCache.cacheManager.clear().then(async () => { const mockedAtEntity: AccessTokenEntity = @@ -1770,35 +1739,18 @@ describe("CacheManager.ts test cases", () => { forceRefresh: false, }; - const mockPerfClient = new MockPerformanceClient(); - const correlationId = "test-correlation-id"; - - mockPerfClient.addPerformanceCallback((events) => { - expect(events.length).toBe(1); - expect(events[0].multiMatchedAT).toEqual(2); - done(); - }); - - const measurement = mockPerfClient.startMeasurement( - PerformanceEvents.AcquireTokenSilent, - correlationId - ); - expect( mockCache.cacheManager.getAccessToken( mockedAccountInfo, silentFlowRequest, undefined, - undefined, - mockPerfClient, - correlationId + undefined ) ).toBeNull(); expect( mockCache.cacheManager.getTokenKeys().accessToken.length ).toEqual(0); - - measurement.end(); + done(); }); }); diff --git a/lib/msal-common/test/cache/MockCache.ts b/lib/msal-common/test/cache/MockCache.ts index 961cfb4dbf..ee07de9f7f 100644 --- a/lib/msal-common/test/cache/MockCache.ts +++ b/lib/msal-common/test/cache/MockCache.ts @@ -7,6 +7,7 @@ import { StaticAuthorityOptions } from "../../src/authority/AuthorityOptions.js" import { RefreshTokenEntity } from "../../src/cache/entities/RefreshTokenEntity.js"; import { ICrypto } from "../../src/crypto/ICrypto.js"; import { Logger } from "../../src/logger/Logger.js"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; import { AuthenticationScheme, CredentialType, @@ -33,6 +34,7 @@ export class MockCache { clientId, cryptoImpl, new Logger({}), + new StubPerformanceClient(), staticAuthorityOptions ); } diff --git a/lib/msal-common/test/cache/entities/AccountEntity.spec.ts b/lib/msal-common/test/cache/entities/AccountEntity.spec.ts index fe03c29e18..df6750ed31 100644 --- a/lib/msal-common/test/cache/entities/AccountEntity.spec.ts +++ b/lib/msal-common/test/cache/entities/AccountEntity.spec.ts @@ -8,13 +8,10 @@ import { } from "../../../src/network/INetworkModule.js"; import { ICrypto } from "../../../src/crypto/ICrypto.js"; import { - RANDOM_TEST_GUID, TEST_DATA_CLIENT_INFO, TEST_TOKENS, TEST_URIS, - TEST_POP_VALUES, PREFERRED_CACHE_ALIAS, - TEST_CRYPTO_VALUES, ID_TOKEN_CLAIMS, GUEST_ID_TOKEN_CLAIMS, TEST_CONFIG, @@ -31,67 +28,9 @@ import { Authority } from "../../../src/authority/Authority.js"; import { AuthorityType } from "../../../src/authority/AuthorityType.js"; import { TokenClaims } from "../../../src/index.js"; import { buildAccountFromIdTokenClaims } from "msal-test-utils"; +import { StubPerformanceClient } from "../../../src/telemetry/performance/StubPerformanceClient.js"; -const cryptoInterface: ICrypto = { - createNewGuid(): string { - return RANDOM_TEST_GUID; - }, - base64Decode(input: string): string { - switch (input) { - case TEST_POP_VALUES.ENCODED_REQ_CNF: - return TEST_POP_VALUES.DECODED_REQ_CNF; - case TEST_DATA_CLIENT_INFO.TEST_CACHE_RAW_CLIENT_INFO: - return TEST_DATA_CLIENT_INFO.TEST_CACHE_DECODED_CLIENT_INFO; - case TEST_DATA_CLIENT_INFO.TEST_RAW_CLIENT_INFO_GUIDS: - return TEST_DATA_CLIENT_INFO.TEST_CACHE_DECODED_CLIENT_INFO_GUIDS; - default: - return input; - } - }, - base64Encode(input: string): string { - switch (input) { - case TEST_POP_VALUES.DECODED_REQ_CNF: - return TEST_POP_VALUES.ENCODED_REQ_CNF; - case "uid": - return "dWlk"; - case "utid": - return "dXRpZA=="; - default: - return input; - } - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return ""; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, -}; +const cryptoInterface: ICrypto = mockCrypto; const networkInterface: INetworkModule = { sendGetRequestAsync(url: string, options?: NetworkRequestOptions): T { @@ -121,11 +60,12 @@ const loggerOptions = { logLevel: LogLevel.Verbose, }; const logger = new Logger(loggerOptions); +const performanceClient = new StubPerformanceClient(); const authority = new Authority( Constants.DEFAULT_AUTHORITY, networkInterface, - new MockStorageClass("client-id", mockCrypto, logger), + new MockStorageClass("client-id", mockCrypto, logger, performanceClient), authorityOptions, logger, TEST_CONFIG.CORRELATION_ID @@ -276,7 +216,12 @@ describe("AccountEntity.ts Unit Tests", () => { const authority = new Authority( Constants.DEFAULT_AUTHORITY, networkInterface, - new MockStorageClass("client-id", mockCrypto, logger), + new MockStorageClass( + "client-id", + mockCrypto, + logger, + performanceClient + ), authorityOptions, logger, TEST_CONFIG.CORRELATION_ID @@ -324,7 +269,12 @@ describe("AccountEntity.ts Unit Tests", () => { const authority = new Authority( Constants.DEFAULT_AUTHORITY, networkInterface, - new MockStorageClass("client-id", mockCrypto, logger), + new MockStorageClass( + "client-id", + mockCrypto, + logger, + performanceClient + ), { protocolMode: ProtocolMode.OIDC, knownAuthorities: [Constants.DEFAULT_AUTHORITY], @@ -657,7 +607,12 @@ describe("AccountEntity.ts Unit Tests for ADFS", () => { const authority = new Authority( "https://myadfs.com/adfs", networkInterface, - new MockStorageClass("client-id", mockCrypto, logger), + new MockStorageClass( + "client-id", + mockCrypto, + logger, + performanceClient + ), authorityOptions, logger, TEST_CONFIG.CORRELATION_ID @@ -713,7 +668,12 @@ describe("AccountEntity.ts Unit Tests for ADFS", () => { const authority = new Authority( "https://myadfs.com/adfs", networkInterface, - new MockStorageClass("client-id", mockCrypto, logger), + new MockStorageClass( + "client-id", + mockCrypto, + logger, + performanceClient + ), authorityOptions, logger, TEST_CONFIG.CORRELATION_ID diff --git a/lib/msal-common/test/client/ClientTestUtils.ts b/lib/msal-common/test/client/ClientTestUtils.ts index ba12425886..750843f6ea 100644 --- a/lib/msal-common/test/client/ClientTestUtils.ts +++ b/lib/msal-common/test/client/ClientTestUtils.ts @@ -8,6 +8,7 @@ import { TEST_CONFIG, TEST_CRYPTO_VALUES, TEST_POP_VALUES, + TEST_TOKENS, } from "../test_kit/StringConstants.js"; import { CacheManager } from "../../src/cache/CacheManager.js"; @@ -32,6 +33,7 @@ import { ServerTelemetryManager } from "../../src/telemetry/server/ServerTelemet import { Constants, EncodingTypes } from "../../src/utils/Constants.js"; import { AuthorityOptions } from "../../src/authority/AuthorityOptions.js"; import { TokenKeys } from "../../src/cache/utils/CacheTypes.js"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; const ACCOUNT_KEYS = "ACCOUNT_KEYS"; const TOKEN_KEYS = "TOKEN_KEYS"; @@ -59,8 +61,8 @@ export class MockStorageClass extends CacheManager { } } - async removeAccount(key: string): Promise { - await super.removeAccount(key); + removeAccount(key: string): void { + super.removeAccount(key, RANDOM_TEST_GUID); const currentAccounts = this.getAccountKeys(); const removalIndex = currentAccounts.indexOf(key); if (removalIndex > -1) { @@ -70,17 +72,17 @@ export class MockStorageClass extends CacheManager { } getAccountKeys(): string[] { - return this.store[ACCOUNT_KEYS] || []; + return [...(this.store[ACCOUNT_KEYS] || [])]; } getTokenKeys(): TokenKeys { - return ( - this.store[TOKEN_KEYS] || { + return { + ...(this.store[TOKEN_KEYS] || { idToken: [], accessToken: [], refreshToken: [], - } - ); + }), + } as TokenKeys; } // Credentials (idtokens) @@ -215,11 +217,11 @@ export const mockCrypto = { async getPublicKeyThumbprint(): Promise { return TEST_POP_VALUES.KID; }, - async removeTokenBindingKey(keyId: string): Promise { - return Promise.resolve(true); + async removeTokenBindingKey(keyId: string): Promise { + return Promise.resolve(); }, async signJwt(): Promise { - return ""; + return TEST_TOKENS.POP_TOKEN; }, async clearKeystore(): Promise { return Promise.resolve(true); @@ -238,6 +240,7 @@ export class ClientTestUtils { TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, new Logger({}), + new StubPerformanceClient(), { canonicalAuthority: TEST_CONFIG.validAuthority, } @@ -316,6 +319,7 @@ export async function getDiscoveredAuthority( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, new Logger({}), + new StubPerformanceClient(), { canonicalAuthority: TEST_CONFIG.validAuthority, } diff --git a/lib/msal-common/test/client/SilentFlowClient.spec.ts b/lib/msal-common/test/client/SilentFlowClient.spec.ts index b62fa17c51..1922e3642a 100644 --- a/lib/msal-common/test/client/SilentFlowClient.spec.ts +++ b/lib/msal-common/test/client/SilentFlowClient.spec.ts @@ -783,7 +783,8 @@ describe("SilentFlowClient unit tests", () => { new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - logger + logger, + new StubPerformanceClient() ) ); client = new SilentFlowClient(config, stubPerformanceClient); diff --git a/lib/msal-common/test/config/ClientConfiguration.spec.ts b/lib/msal-common/test/config/ClientConfiguration.spec.ts index 9a49c658f1..fc4ea2fc34 100644 --- a/lib/msal-common/test/config/ClientConfiguration.spec.ts +++ b/lib/msal-common/test/config/ClientConfiguration.spec.ts @@ -6,16 +6,13 @@ import { AuthError } from "../../src/error/AuthError.js"; import { NetworkRequestOptions } from "../../src/network/INetworkModule.js"; import { Logger, LogLevel } from "../../src/logger/Logger.js"; import { version } from "../../src/packageMetadata.js"; -import { - TEST_CONFIG, - TEST_CRYPTO_VALUES, - TEST_POP_VALUES, -} from "../test_kit/StringConstants.js"; +import { TEST_CONFIG } from "../test_kit/StringConstants.js"; import { MockStorageClass, mockCrypto } from "../client/ClientTestUtils.js"; import { MockCache } from "../cache/entities/cacheConstants.js"; import { Constants } from "../../src/utils/Constants.js"; import * as ClientAuthErrorCodes from "../../src/error/ClientAuthErrorCodes.js"; import { createClientAuthError } from "../../src/error/ClientAuthError.js"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; describe("ClientConfiguration.ts Class Unit Tests", () => { it("buildConfiguration assigns default functions", async () => { @@ -124,7 +121,8 @@ describe("ClientConfiguration.ts Class Unit Tests", () => { const cacheStorageMock = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + new StubPerformanceClient() ); const testNetworkResult = { @@ -137,48 +135,7 @@ describe("ClientConfiguration.ts Class Unit Tests", () => { authOptions: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, }, - cryptoInterface: { - createNewGuid: (): string => { - return "newGuid"; - }, - base64Decode: (input: string): string => { - return "testDecodedString"; - }, - base64Encode: (input: string): string => { - return "testEncodedString"; - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return "signedJwt"; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, - }, + cryptoInterface: mockCrypto, storageInterface: cacheStorageMock, networkInterface: { sendGetRequestAsync: async ( @@ -219,62 +176,11 @@ describe("ClientConfiguration.ts Class Unit Tests", () => { }, }); await cacheStorageMock.setAccount(MockCache.acc); - // Crypto interface tests expect(newConfig.cryptoInterface).not.toBeNull(); - expect(newConfig.cryptoInterface.base64Decode).not.toBeNull(); - expect(newConfig.cryptoInterface.base64Decode("testString")).toBe( - "testDecodedString" - ); - expect(newConfig.cryptoInterface.base64Encode).not.toBeNull(); - expect(newConfig.cryptoInterface.base64Encode("testString")).toBe( - "testEncodedString" - ); - expect(newConfig.cryptoInterface.removeTokenBindingKey).not.toBeNull(); - expect( - newConfig.cryptoInterface.removeTokenBindingKey("testString") - ).resolves.toBe(true); - // Storage interface tests expect(newConfig.storageInterface).not.toBeNull(); - expect(newConfig.storageInterface.getAccount).not.toBeNull(); - expect( - newConfig.storageInterface.getAccount( - MockCache.acc.generateAccountKey() - ) - ).toBe(MockCache.acc); - expect(newConfig.storageInterface.getKeys).not.toBeNull(); - expect(newConfig.storageInterface.getKeys()).toEqual([ - MockCache.acc.generateAccountKey(), - "ACCOUNT_KEYS", - ]); - expect(newConfig.storageInterface.removeItem).not.toBeNull(); - expect(newConfig.storageInterface.removeItem).toBe( - cacheStorageMock.removeItem - ); - expect(newConfig.storageInterface.setAccount).not.toBeNull(); - expect(newConfig.storageInterface.setAccount).toBe( - cacheStorageMock.setAccount - ); - // Network interface tests expect(newConfig.networkInterface).not.toBeNull(); - expect(newConfig.networkInterface.sendGetRequestAsync).not.toBeNull(); - - expect( - //@ts-ignore - newConfig.networkInterface.sendGetRequestAsync("", null) - ).resolves.toBe(testNetworkResult); - expect(newConfig.networkInterface.sendPostRequestAsync).not.toBeNull(); - - expect( - //@ts-ignore - newConfig.networkInterface.sendPostRequestAsync("", null) - ).resolves.toBe(testNetworkResult); - // Logger option tests expect(newConfig.loggerOptions).not.toBeNull(); - expect(newConfig.loggerOptions.loggerCallback).not.toBeNull(); - expect(newConfig.loggerOptions.piiLoggingEnabled).toBe(true); - // Cache options tests expect(newConfig.cacheOptions).not.toBeNull(); - expect(newConfig.cacheOptions.claimsBasedCachingEnabled).toBe(true); // Client info tests expect(newConfig.libraryInfo.sku).toBe(TEST_CONFIG.TEST_SKU); expect(newConfig.libraryInfo.version).toBe(TEST_CONFIG.TEST_VERSION); diff --git a/lib/msal-common/test/crypto/PopTokenGenerator.spec.ts b/lib/msal-common/test/crypto/PopTokenGenerator.spec.ts index c7906b2b10..c70231ee96 100644 --- a/lib/msal-common/test/crypto/PopTokenGenerator.spec.ts +++ b/lib/msal-common/test/crypto/PopTokenGenerator.spec.ts @@ -1,10 +1,8 @@ import { RANDOM_TEST_GUID, TEST_POP_VALUES, - TEST_DATA_CLIENT_INFO, TEST_CONFIG, TEST_URIS, - TEST_CRYPTO_VALUES, } from "../test_kit/StringConstants.js"; import { PopTokenGenerator } from "../../src/crypto/PopTokenGenerator.js"; import { ICrypto } from "../../src/crypto/ICrypto.js"; @@ -14,78 +12,14 @@ import { UrlString } from "../../src/url/UrlString.js"; import { AuthenticationScheme } from "../../src/utils/Constants.js"; import { SignedHttpRequest } from "../../src/crypto/SignedHttpRequest.js"; import { Logger } from "../../src/logger/Logger.js"; +import { mockCrypto } from "../client/ClientTestUtils.js"; describe("PopTokenGenerator Unit Tests", () => { afterEach(() => { jest.restoreAllMocks(); }); - const cryptoInterface: ICrypto = { - createNewGuid(): string { - return RANDOM_TEST_GUID; - }, - base64Decode(input: string): string { - switch (input) { - case TEST_POP_VALUES.ENCODED_REQ_CNF: - return TEST_POP_VALUES.DECODED_REQ_CNF; - case TEST_DATA_CLIENT_INFO.TEST_RAW_CLIENT_INFO: - return TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO; - case TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_ENCODED: - return TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_DECODED; - default: - return input; - } - }, - base64Encode(input: string): string { - switch (input) { - case "123-test-uid": - return "MTIzLXRlc3QtdWlk"; - case "456-test-uid": - return "NDU2LXRlc3QtdWlk"; - case TEST_POP_VALUES.DECODED_REQ_CNF: - return TEST_POP_VALUES.ENCODED_REQ_CNF; - case TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_DECODED: - return TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_ENCODED; - default: - return input; - } - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "e2tpZDogIlhuc3VBdnR0VFBwMG5uMUtfWU1MZVBMRGJwN3N5Q0toTkh0N0hqWUhKWWMifQ"; - case '{"kid":"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs","xms_ksl":"sw"}': - return "eyJraWQiOiJOemJMc1hoOHVEQ2NkLTZNTndYRjRXXzdub1dYRlpBZkhreFpzUkdDOVhzIiwieG1zX2tzbCI6InN3In0"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - case "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs": - return "eyJraWQiOiJOemJMc1hoOHVEQ2NkLTZNTndYRjRXXzdub1dYRlpBZkhreFpzUkdDOVhzIiwieG1zX2tzbCI6InN3In0"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return ""; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, - }; + const cryptoInterface: ICrypto = mockCrypto; describe("generateCnf", () => { const testRequest = { diff --git a/lib/msal-common/test/network/ThrottlingUtils.spec.ts b/lib/msal-common/test/network/ThrottlingUtils.spec.ts index 01f01ef712..d2d5070cde 100644 --- a/lib/msal-common/test/network/ThrottlingUtils.spec.ts +++ b/lib/msal-common/test/network/ThrottlingUtils.spec.ts @@ -16,6 +16,9 @@ import { } from "../test_kit/StringConstants.js"; import { ServerError } from "../../src/error/ServerError.js"; import { BaseAuthRequest, Logger } from "../../src/index.js"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; + +const performanceClient = new StubPerformanceClient(); describe("ThrottlingUtils", () => { afterAll(() => { @@ -40,7 +43,8 @@ describe("ThrottlingUtils", () => { const cache = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + performanceClient ); const removeItemStub = jest .spyOn(cache, "removeItem") @@ -66,7 +70,8 @@ describe("ThrottlingUtils", () => { const cache = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + performanceClient ); const removeItemStub = jest .spyOn(cache, "removeItem") @@ -89,7 +94,8 @@ describe("ThrottlingUtils", () => { const cache = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + performanceClient ); const removeItemStub = jest .spyOn(cache, "removeItem") @@ -116,7 +122,8 @@ describe("ThrottlingUtils", () => { const cache = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + performanceClient ); const setItemStub = jest .spyOn(cache, "setThrottlingCache") @@ -136,7 +143,8 @@ describe("ThrottlingUtils", () => { const cache = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + performanceClient ); const setItemStub = jest .spyOn(cache, "setThrottlingCache") @@ -269,7 +277,8 @@ describe("ThrottlingUtils", () => { const cache = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + performanceClient ); const clientId = TEST_CONFIG.MSAL_CLIENT_ID; const removeItemStub = jest.spyOn(cache, "removeItem"); diff --git a/lib/msal-common/test/response/ResponseHandler.spec.ts b/lib/msal-common/test/response/ResponseHandler.spec.ts index 6aa4b3550c..7522cc25af 100644 --- a/lib/msal-common/test/response/ResponseHandler.spec.ts +++ b/lib/msal-common/test/response/ResponseHandler.spec.ts @@ -4,9 +4,7 @@ import { AUTHENTICATION_RESULT, ID_TOKEN_CLAIMS, POP_AUTHENTICATION_RESULT, - RANDOM_TEST_GUID, TEST_CONFIG, - TEST_CRYPTO_VALUES, TEST_DATA_CLIENT_INFO, TEST_POP_VALUES, TEST_TOKEN_LIFETIMES, @@ -19,7 +17,7 @@ import { NetworkRequestOptions, } from "../../src/network/INetworkModule.js"; import { ICrypto } from "../../src/crypto/ICrypto.js"; -import { MockStorageClass } from "../client/ClientTestUtils.js"; +import { mockCrypto, MockStorageClass } from "../client/ClientTestUtils.js"; import { TokenClaims } from "../../src/account/TokenClaims.js"; import { AccountInfo } from "../../src/account/AccountInfo.js"; import { AuthenticationResult } from "../../src/response/AuthenticationResult.js"; @@ -46,6 +44,7 @@ import { import { CacheManager } from "../../src/cache/CacheManager.js"; import { cacheQuotaExceededErrorCode } from "../../src/error/CacheErrorCodes.js"; import { TestTimeUtils } from "msal-test-utils"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; const networkInterface: INetworkModule = { sendGetRequestAsync(url: string, options?: NetworkRequestOptions): T { @@ -55,68 +54,8 @@ const networkInterface: INetworkModule = { return {} as T; }, }; -const signedJwt = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJjbmYiOnsia2lkIjoiTnpiTHNYaDh1RENjZC02TU53WEY0V183bm9XWEZaQWZIa3hac1JHQzlYcyJ9fQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; -const cryptoInterface: ICrypto = { - createNewGuid(): string { - return RANDOM_TEST_GUID; - }, - base64Decode(input: string): string { - switch (input) { - case TEST_POP_VALUES.ENCODED_REQ_CNF: - return TEST_POP_VALUES.DECODED_REQ_CNF; - case TEST_DATA_CLIENT_INFO.TEST_RAW_CLIENT_INFO: - return TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO; - case TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_ENCODED: - return TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_DECODED; - default: - return input; - } - }, - base64Encode(input: string): string { - switch (input) { - case TEST_POP_VALUES.DECODED_REQ_CNF: - return TEST_POP_VALUES.ENCODED_REQ_CNF; - case TEST_DATA_CLIENT_INFO.TEST_DECODED_CLIENT_INFO: - return TEST_DATA_CLIENT_INFO.TEST_RAW_CLIENT_INFO; - case TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_DECODED: - return TEST_POP_VALUES.SAMPLE_POP_AT_PAYLOAD_ENCODED; - default: - return input; - } - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return signedJwt; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, -}; + +const cryptoInterface: ICrypto = mockCrypto; const testServerTokenResponse = { headers: null, @@ -174,7 +113,8 @@ const logger = new Logger(loggerOptions); const testCacheManager = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, cryptoInterface, - logger + logger, + new StubPerformanceClient() ); const testAuthority = new Authority( @@ -642,7 +582,7 @@ describe("ResponseHandler.ts", () => { ); expect(result.tokenType).toBe(AuthenticationScheme.POP); - expect(result.accessToken).toBe(signedJwt); + expect(result.accessToken).toBe(TEST_TOKENS.POP_TOKEN); }); it("Does not sign access token when PoP kid is set and PoP scheme enabled", async () => { diff --git a/lib/msal-common/test/telemetry/ServerTelemetryManager.spec.ts b/lib/msal-common/test/telemetry/ServerTelemetryManager.spec.ts index 78b5084a36..80e259dec7 100644 --- a/lib/msal-common/test/telemetry/ServerTelemetryManager.spec.ts +++ b/lib/msal-common/test/telemetry/ServerTelemetryManager.spec.ts @@ -11,11 +11,13 @@ import { AuthError } from "../../src/error/AuthError.js"; import { ServerTelemetryEntity } from "../../src/cache/entities/ServerTelemetryEntity.js"; import { CacheOutcome } from "../../src/utils/Constants.js"; import { Logger } from "../../src/logger/Logger.js"; +import { StubPerformanceClient } from "../../src/telemetry/performance/StubPerformanceClient.js"; const testCacheManager = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + new StubPerformanceClient() ); const testApiCode = 9999999; const testError = "interaction_required"; diff --git a/lib/msal-common/test/utils/ProtocolUtils.spec.ts b/lib/msal-common/test/utils/ProtocolUtils.spec.ts index ff6c8c8ca3..14021b019c 100644 --- a/lib/msal-common/test/utils/ProtocolUtils.spec.ts +++ b/lib/msal-common/test/utils/ProtocolUtils.spec.ts @@ -1,15 +1,12 @@ import { ProtocolUtils } from "../../src/utils/ProtocolUtils.js"; -import { - RANDOM_TEST_GUID, - TEST_CRYPTO_VALUES, - TEST_POP_VALUES, -} from "../test_kit/StringConstants.js"; +import { RANDOM_TEST_GUID } from "../test_kit/StringConstants.js"; import { ICrypto } from "../../src/crypto/ICrypto.js"; import { Constants } from "../../src/utils/Constants.js"; import { ClientAuthError, ClientAuthErrorMessage, } from "../../src/error/ClientAuthError.js"; +import { mockCrypto } from "../client/ClientTestUtils.js"; describe("ProtocolUtils.ts Class Unit Tests", () => { const userState = "userState"; @@ -19,62 +16,7 @@ describe("ProtocolUtils.ts Class Unit Tests", () => { let cryptoInterface: ICrypto; beforeEach(() => { - cryptoInterface = { - createNewGuid(): string { - return RANDOM_TEST_GUID; - }, - base64Decode(input: string): string { - switch (input) { - case TEST_POP_VALUES.ENCODED_REQ_CNF: - return TEST_POP_VALUES.DECODED_REQ_CNF; - case encodedLibState: - return decodedLibState; - default: - return input; - } - }, - base64Encode(input: string): string { - switch (input) { - case TEST_POP_VALUES.DECODED_REQ_CNF: - return TEST_POP_VALUES.ENCODED_REQ_CNF; - case `${decodedLibState}`: - return encodedLibState; - default: - return input; - } - }, - base64UrlEncode(input: string): string { - switch (input) { - case '{"kid": "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc"}': - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - encodeKid(input: string): string { - switch (input) { - case "XnsuAvttTPp0nn1K_YMLePLDbp7syCKhNHt7HjYHJYc": - return "eyJraWQiOiAiWG5zdUF2dHRUUHAwbm4xS19ZTUxlUExEYnA3c3lDS2hOSHQ3SGpZSEpZYyJ9"; - default: - return input; - } - }, - async getPublicKeyThumbprint(): Promise { - return TEST_POP_VALUES.KID; - }, - async signJwt(): Promise { - return ""; - }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); - }, - async clearKeystore(): Promise { - return Promise.resolve(true); - }, - async hashString(): Promise { - return Promise.resolve(TEST_CRYPTO_VALUES.TEST_SHA256_HASH); - }, - }; + cryptoInterface = mockCrypto; }); afterEach(() => { @@ -127,7 +69,7 @@ describe("ProtocolUtils.ts Class Unit Tests", () => { it("parseRequestState() returns empty userRequestState if no resource delimiter found in state string", () => { const requestState = ProtocolUtils.parseRequestState( cryptoInterface, - decodedLibState + cryptoInterface.base64Encode(decodedLibState) ); expect(requestState.userRequestState).toHaveLength(0); }); diff --git a/lib/msal-node/apiReview/msal-node.api.md b/lib/msal-node/apiReview/msal-node.api.md index ea5a4d9ba9..5fa0dc6173 100644 --- a/lib/msal-node/apiReview/msal-node.api.md +++ b/lib/msal-node/apiReview/msal-node.api.md @@ -230,7 +230,7 @@ export class CryptoProvider implements ICrypto { generatePkceCodes(): Promise; getPublicKeyThumbprint(): Promise; hashString(plainText: string): Promise; - removeTokenBindingKey(): Promise; + removeTokenBindingKey(): Promise; signJwt(): Promise; } @@ -607,7 +607,7 @@ export class TokenCache implements ISerializableTokenCache, ITokenCache { overwriteCache(): Promise; // (undocumented) readonly persistence: ICachePlugin; - removeAccount(account: AccountInfo): Promise; + removeAccount(account: AccountInfo, correlationId?: string): Promise; serialize(): string; } diff --git a/lib/msal-node/src/cache/NodeStorage.ts b/lib/msal-node/src/cache/NodeStorage.ts index d1c0c36582..5a904a0bba 100644 --- a/lib/msal-node/src/cache/NodeStorage.ts +++ b/lib/msal-node/src/cache/NodeStorage.ts @@ -29,6 +29,7 @@ import { JsonCache, CacheKVStore, } from "./serializer/SerializerTypes.js"; +import { StubPerformanceClient } from "@azure/msal-common"; /** * This class implements Storage for node, reading cache from user specified storage location or an extension library @@ -46,7 +47,13 @@ export class NodeStorage extends CacheManager { cryptoImpl: ICrypto, staticAuthorityOptions?: StaticAuthorityOptions ) { - super(clientId, cryptoImpl, logger, staticAuthorityOptions); + super( + clientId, + cryptoImpl, + logger, + new StubPerformanceClient(), + staticAuthorityOptions + ); this.logger = logger; } diff --git a/lib/msal-node/src/cache/TokenCache.ts b/lib/msal-node/src/cache/TokenCache.ts index bdf95577e7..267a2947a6 100644 --- a/lib/msal-node/src/cache/TokenCache.ts +++ b/lib/msal-node/src/cache/TokenCache.ts @@ -25,6 +25,7 @@ import { import { Deserializer } from "./serializer/Deserializer.js"; import { Serializer } from "./serializer/Serializer.js"; import { ITokenCache } from "./ITokenCache.js"; +import { GuidGenerator } from "../crypto/GuidGenerator.js"; const defaultSerializedCache: JsonCache = { Account: {}, @@ -191,7 +192,10 @@ export class TokenCache implements ISerializableTokenCache, ITokenCache { * API to remove a specific account and the relevant data from cache * @param account - AccountInfo passed by the user */ - async removeAccount(account: AccountInfo): Promise { + async removeAccount( + account: AccountInfo, + correlationId?: string + ): Promise { this.logger.trace("removeAccount called"); let cacheContext; try { @@ -199,8 +203,9 @@ export class TokenCache implements ISerializableTokenCache, ITokenCache { cacheContext = new TokenCacheContext(this, true); await this.persistence.beforeCacheAccess(cacheContext); } - await this.storage.removeAccount( - AccountEntity.generateAccountCacheKey(account) + this.storage.removeAccount( + AccountEntity.generateAccountCacheKey(account), + correlationId || new GuidGenerator().generateGuid() ); } finally { if (this.persistence && cacheContext) { diff --git a/lib/msal-node/src/client/PublicClientApplication.ts b/lib/msal-node/src/client/PublicClientApplication.ts index 2fa0a0b09f..e2c7864d25 100644 --- a/lib/msal-node/src/client/PublicClientApplication.ts +++ b/lib/msal-node/src/client/PublicClientApplication.ts @@ -291,7 +291,10 @@ export class PublicClientApplication await this.nativeBrokerPlugin.signOut(signoutRequest); } - await this.getTokenCache().removeAccount(request.account); + await this.getTokenCache().removeAccount( + request.account, + request.correlationId + ); } /** diff --git a/lib/msal-node/src/crypto/CryptoProvider.ts b/lib/msal-node/src/crypto/CryptoProvider.ts index 4e08f32ce8..d6e06d99e1 100644 --- a/lib/msal-node/src/crypto/CryptoProvider.ts +++ b/lib/msal-node/src/crypto/CryptoProvider.ts @@ -83,7 +83,7 @@ export class CryptoProvider implements ICrypto { * Removes cryptographic keypair from key store matching the keyId passed in * @param kid - public key id */ - removeTokenBindingKey(): Promise { + removeTokenBindingKey(): Promise { throw new Error("Method not implemented."); } diff --git a/lib/msal-node/test/client/ClientTestUtils.ts b/lib/msal-node/test/client/ClientTestUtils.ts index 157a27f06f..d508e8db7a 100644 --- a/lib/msal-node/test/client/ClientTestUtils.ts +++ b/lib/msal-node/test/client/ClientTestUtils.ts @@ -32,6 +32,7 @@ import { ClientAssertionConfig, PasswordGrantConstants, OAuthResponseType, + StubPerformanceClient, } from "@azure/msal-common"; import { AUTHENTICATION_RESULT, @@ -77,8 +78,8 @@ export class MockStorageClass extends CacheManager { } } - async removeAccount(key: string): Promise { - await super.removeAccount(key); + removeAccount(key: string): void { + super.removeAccount(key, RANDOM_TEST_GUID); const currentAccounts = this.getAccountKeys(); const removalIndex = currentAccounts.indexOf(key); if (removalIndex > -1) { @@ -250,8 +251,8 @@ export const mockCrypto = { async getPublicKeyThumbprint(): Promise { return TEST_POP_VALUES.KID; }, - async removeTokenBindingKey(): Promise { - return Promise.resolve(true); + async removeTokenBindingKey(): Promise { + return Promise.resolve(); }, async signJwt(): Promise { return ""; @@ -272,7 +273,8 @@ export class ClientTestUtils { const mockStorage = new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, mockCrypto, - new Logger({}) + new Logger({}), + new StubPerformanceClient() ); const testLoggerCallback = (): void => { diff --git a/lib/msal-node/test/client/PublicClientApplication.spec.ts b/lib/msal-node/test/client/PublicClientApplication.spec.ts index e03dc265f2..c9668907d5 100644 --- a/lib/msal-node/test/client/PublicClientApplication.spec.ts +++ b/lib/msal-node/test/client/PublicClientApplication.spec.ts @@ -81,6 +81,7 @@ import { NodeStorage } from "../../src/cache/NodeStorage.js"; import { TokenCache } from "../../src/index.js"; import { buildAccountFromIdTokenClaims } from "msal-test-utils"; import * as AuthorizeProtocol from "../../src/protocol/Authorize.js"; +import { StubPerformanceClient } from "@azure/msal-common"; const msalCommon: MSALCommonModule = jest.requireActual( "@azure/msal-common/node" @@ -1051,7 +1052,8 @@ describe("PublicClientApplication", () => { new MockStorageClass( TEST_CONFIG.MSAL_CLIENT_ID, cryptoProvider, - new Logger({}) + new Logger({}), + new StubPerformanceClient() ), { protocolMode: ProtocolMode.AAD, diff --git a/lib/msal-node/test/utils/TestConstants.ts b/lib/msal-node/test/utils/TestConstants.ts index 5331fcbc4d..cc59752386 100644 --- a/lib/msal-node/test/utils/TestConstants.ts +++ b/lib/msal-node/test/utils/TestConstants.ts @@ -111,7 +111,7 @@ export const DEFAULT_CRYPTO_IMPLEMENTATION: ICrypto = { async getPublicKeyThumbprint(): Promise { throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented); }, - async removeTokenBindingKey(): Promise { + async removeTokenBindingKey(): Promise { throw createClientAuthError(ClientAuthErrorCodes.methodNotImplemented); }, async clearKeystore(): Promise { From 1eb9ee50f210cdaf6c34e8a8a249ff196868f58b Mon Sep 17 00:00:00 2001 From: "msal-js-release-automation[bot]" <152663010+msal-js-release-automation[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 16:50:16 -0700 Subject: [PATCH 05/15] Release PR: official (#7829) This PR contains the changelogs and version bumps for the MSAL.js 3P releases. Co-authored-by: MSAL.js Release Automation Co-authored-by: Sameera Gajjarapu --- ...-26d01131-1bee-477b-8553-8240aed2a24d.json | 7 ---- lib/msal-browser/CHANGELOG.json | 33 +++++++++++++++++++ lib/msal-browser/CHANGELOG.md | 13 +++++++- .../apiReview/msal-browser.api.md | 2 +- lib/msal-browser/package.json | 2 +- lib/msal-browser/src/packageMetadata.ts | 2 +- package-lock.json | 6 ++-- 7 files changed, 51 insertions(+), 14 deletions(-) delete mode 100644 change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json diff --git a/change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json b/change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json deleted file mode 100644 index 9cd8051beb..0000000000 --- a/change/@azure-msal-browser-26d01131-1bee-477b-8553-8240aed2a24d.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "\u0016Suppress false-positive CodeQL finding in NavigationClient #7814", - "packageName": "@azure/msal-browser", - "email": "kshabelko@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/lib/msal-browser/CHANGELOG.json b/lib/msal-browser/CHANGELOG.json index 4355ff6f40..d7cbe1b713 100644 --- a/lib/msal-browser/CHANGELOG.json +++ b/lib/msal-browser/CHANGELOG.json @@ -1,6 +1,39 @@ { "name": "@azure/msal-browser", "entries": [ + { + "date": "Tue, 10 Jun 2025 20:38:38 GMT", + "version": "4.13.1", + "tag": "@azure/msal-browser_v4.13.1", + "comments": { + "patch": [ + { + "author": "kshabelko@microsoft.com", + "package": "@azure/msal-browser", + "commit": "75f62379475a5084ddc9dfe1efa436ad3a78024b", + "comment": "\u0016Suppress false-positive CodeQL finding in NavigationClient #7814" + }, + { + "author": "beachball", + "package": "@azure/msal-browser", + "comment": "Bump eslint-config-msal to v0.0.0", + "commit": "not available" + }, + { + "author": "beachball", + "package": "@azure/msal-browser", + "comment": "Bump msal-test-utils to v0.0.1", + "commit": "not available" + }, + { + "author": "beachball", + "package": "@azure/msal-browser", + "comment": "Bump rollup-msal to v0.0.0", + "commit": "not available" + } + ] + } + }, { "date": "Fri, 30 May 2025 22:36:44 GMT", "version": "4.13.0", diff --git a/lib/msal-browser/CHANGELOG.md b/lib/msal-browser/CHANGELOG.md index a37a734107..fb8b6e5cf1 100644 --- a/lib/msal-browser/CHANGELOG.md +++ b/lib/msal-browser/CHANGELOG.md @@ -1,9 +1,20 @@ # Change Log - @azure/msal-browser - + +## 4.13.1 + +Tue, 10 Jun 2025 20:38:38 GMT + +### Patches + +- Suppress false-positive CodeQL finding in NavigationClient #7814 (kshabelko@microsoft.com) +- Bump eslint-config-msal to v0.0.0 +- Bump msal-test-utils to v0.0.1 +- Bump rollup-msal to v0.0.0 + ## 4.13.0 Fri, 30 May 2025 22:36:44 GMT diff --git a/lib/msal-browser/apiReview/msal-browser.api.md b/lib/msal-browser/apiReview/msal-browser.api.md index 97a7f9a6fb..3559439a74 100644 --- a/lib/msal-browser/apiReview/msal-browser.api.md +++ b/lib/msal-browser/apiReview/msal-browser.api.md @@ -1773,7 +1773,7 @@ const userCancelled = "user_cancelled"; // Warning: (ae-missing-release-tag) "version" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const version = "4.13.0"; +export const version = "4.13.1"; // Warning: (ae-missing-release-tag) "WrapperSKU" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // Warning: (ae-missing-release-tag) "WrapperSKU" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/lib/msal-browser/package.json b/lib/msal-browser/package.json index 3bf7712d22..e454775158 100644 --- a/lib/msal-browser/package.json +++ b/lib/msal-browser/package.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/AzureAD/microsoft-authentication-library-for-js.git" }, - "version": "4.13.0", + "version": "4.13.1", "description": "Microsoft Authentication Library for js", "keywords": [ "implicit", diff --git a/lib/msal-browser/src/packageMetadata.ts b/lib/msal-browser/src/packageMetadata.ts index b301a3ec34..c4bc727bda 100644 --- a/lib/msal-browser/src/packageMetadata.ts +++ b/lib/msal-browser/src/packageMetadata.ts @@ -1,3 +1,3 @@ /* eslint-disable header/header */ export const name = "@azure/msal-browser"; -export const version = "4.13.0"; +export const version = "4.13.1"; diff --git a/package-lock.json b/package-lock.json index 7f8ef3bffe..c31fe34816 100644 --- a/package-lock.json +++ b/package-lock.json @@ -351,7 +351,7 @@ }, "lib/msal-browser": { "name": "@azure/msal-browser", - "version": "4.13.0", + "version": "4.13.1", "license": "MIT", "dependencies": { "@azure/msal-common": "15.7.0" @@ -45176,8 +45176,8 @@ }, "node_modules/vite": { "version": "4.5.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", - "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "resolved": "https://identitydivision.pkgs.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_packaging/dd15892d-fc68-4d1c-93a5-090f3b303f31/npm/registry/vite/-/vite-4.5.14.tgz", + "integrity": "sha1-LmUrwdiYJl2YfWVDzoZuzWX6QIY=", "dev": true, "license": "MIT", "peer": true, From 97ea591adf91631435b8c1eec6c64209927f05de Mon Sep 17 00:00:00 2001 From: Robbie-Microsoft <87724641+Robbie-Microsoft@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:09:43 -0400 Subject: [PATCH 06/15] Bumped axios to latest version in all msal-node samples (#7831) Bumped to new minor version --- package-lock.json | 24 +++++++++---------- samples/e2eTestUtils/package.json | 2 +- .../ElectronSystemBrowserTestApp/package.json | 2 +- .../ElectronTestApp/package.json | 2 +- .../auth-code-cli-app/package.json | 2 +- .../auth-code-distributed-cache/package.json | 2 +- .../b2c-user-flows/package.json | 2 +- .../package.json | 4 ++-- .../package.json | 4 ++-- .../silent-flow/package.json | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index c31fe34816..70030d4e09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17634,9 +17634,9 @@ } }, "node_modules/axios": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", - "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -47561,7 +47561,7 @@ "license": "MIT", "dependencies": { "@azure/identity": "^4.5.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "dotenv": "^8.2.0", "find-process": "^1.4.4" }, @@ -60880,7 +60880,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "open": "^8.4.0" }, "devDependencies": { @@ -60907,7 +60907,7 @@ "license": "ISC", "dependencies": { "@azure/msal-node": "^1.15.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "connect-redis": "^7.0.0", "express": "^4.20.0", "express-session": "^1.17.3", @@ -61240,7 +61240,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-session": "^1.17.2" @@ -61287,7 +61287,7 @@ "license": "ISC", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "open": "^8.4.2", "redis": "^4.6.5", "yargs": "^17.7.1" @@ -61511,7 +61511,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "bootstrap": "^5.0.0", "electron-squirrel-startup": "^1.0.0", "react": "^19.1.0", @@ -61698,7 +61698,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2" + "axios": "^1.9.0" }, "devDependencies": { "babel": "^6.23.0", @@ -61731,7 +61731,7 @@ "license": "ISC", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "express": "^4.20.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", @@ -61922,7 +61922,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-promise-router": "^4.0.1" diff --git a/samples/e2eTestUtils/package.json b/samples/e2eTestUtils/package.json index ceb3bd3ca0..7ee0ea35d0 100644 --- a/samples/e2eTestUtils/package.json +++ b/samples/e2eTestUtils/package.json @@ -6,7 +6,7 @@ "main": "src/index.ts", "dependencies": { "@azure/identity": "^4.5.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "dotenv": "^8.2.0", "find-process": "^1.4.4" }, diff --git a/samples/msal-node-samples/ElectronSystemBrowserTestApp/package.json b/samples/msal-node-samples/ElectronSystemBrowserTestApp/package.json index 9b65da45d0..8e5746b6da 100644 --- a/samples/msal-node-samples/ElectronSystemBrowserTestApp/package.json +++ b/samples/msal-node-samples/ElectronSystemBrowserTestApp/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "bootstrap": "^5.0.0", "electron-squirrel-startup": "^1.0.0", "react": "^19.1.0", diff --git a/samples/msal-node-samples/ElectronTestApp/package.json b/samples/msal-node-samples/ElectronTestApp/package.json index 5591682502..a9e6ef2f86 100644 --- a/samples/msal-node-samples/ElectronTestApp/package.json +++ b/samples/msal-node-samples/ElectronTestApp/package.json @@ -24,6 +24,6 @@ }, "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2" + "axios": "^1.9.0" } } diff --git a/samples/msal-node-samples/auth-code-cli-app/package.json b/samples/msal-node-samples/auth-code-cli-app/package.json index e3df108597..6a7c21dc33 100644 --- a/samples/msal-node-samples/auth-code-cli-app/package.json +++ b/samples/msal-node-samples/auth-code-cli-app/package.json @@ -14,7 +14,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "open": "^8.4.0" }, "devDependencies": { diff --git a/samples/msal-node-samples/auth-code-distributed-cache/package.json b/samples/msal-node-samples/auth-code-distributed-cache/package.json index fe8af2e90d..c7eaed98c6 100644 --- a/samples/msal-node-samples/auth-code-distributed-cache/package.json +++ b/samples/msal-node-samples/auth-code-distributed-cache/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@azure/msal-node": "^1.15.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "connect-redis": "^7.0.0", "express": "^4.20.0", "express-session": "^1.17.3", diff --git a/samples/msal-node-samples/b2c-user-flows/package.json b/samples/msal-node-samples/b2c-user-flows/package.json index 135d8643a9..172290a8ac 100644 --- a/samples/msal-node-samples/b2c-user-flows/package.json +++ b/samples/msal-node-samples/b2c-user-flows/package.json @@ -14,7 +14,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-session": "^1.17.2" diff --git a/samples/msal-node-samples/client-credentials-distributed-cache/package.json b/samples/msal-node-samples/client-credentials-distributed-cache/package.json index 09952c9589..a4801f5f80 100644 --- a/samples/msal-node-samples/client-credentials-distributed-cache/package.json +++ b/samples/msal-node-samples/client-credentials-distributed-cache/package.json @@ -22,9 +22,9 @@ }, "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "open": "^8.4.2", "redis": "^4.6.5", "yargs": "^17.7.1" } -} \ No newline at end of file +} diff --git a/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json b/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json index 8b6f58da48..33f143e998 100644 --- a/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json +++ b/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "express": "^4.20.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", "redis": "^4.6.5" } -} \ No newline at end of file +} diff --git a/samples/msal-node-samples/silent-flow/package.json b/samples/msal-node-samples/silent-flow/package.json index 5f66590763..584bd515dc 100644 --- a/samples/msal-node-samples/silent-flow/package.json +++ b/samples/msal-node-samples/silent-flow/package.json @@ -14,7 +14,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "axios": "^1.8.2", + "axios": "^1.9.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-promise-router": "^4.0.1" From 4b956d00ace3c700076d55378428c1f1a3be1380 Mon Sep 17 00:00:00 2001 From: Robbie-Microsoft <87724641+Robbie-Microsoft@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:24:13 -0400 Subject: [PATCH 07/15] Bumped the dotenv package in all msal-node samples (#7823) 1. Updated all msal-node samples to use dotenv 16.5.0 2. Moved dotenv from devDependencies to dependencies in several samples. This change reflects that dotenv is a runtime requirement for these samples since they need to load environment variables during actual execution, not just during development. 3. Run `npm i` in each directory to update package-lock.json files 4. Merged in child: #7824 --- package-lock.json | 133 +++++++++++------- .../Managed-Identity/FIC/.npmrc | 1 + .../Managed-Identity/FIC/package.json | 2 +- .../Managed-Identity/Imds/.npmrc | 1 + .../Managed-Identity/Imds/package.json | 2 +- .../auth-code-distributed-cache/package.json | 2 +- .../auth-code-key-vault/package.json | 3 +- .../auth-code-pkce/package.json | 2 +- .../auth-code-with-certs/package.json | 3 +- .../msal-node-samples/auth-code/package.json | 2 +- .../b2c-user-flows/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../on-behalf-of/package.json | 2 +- .../refresh-token/package.json | 4 +- .../silent-flow/package.json | 2 +- .../username-password-cca/package.json | 4 +- 17 files changed, 99 insertions(+), 70 deletions(-) create mode 100644 samples/msal-node-samples/Managed-Identity/FIC/.npmrc create mode 100644 samples/msal-node-samples/Managed-Identity/Imds/.npmrc diff --git a/package-lock.json b/package-lock.json index 70030d4e09..f03e840f73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60862,12 +60862,12 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.2" }, "devDependencies": { "@types/jest": "^29.5.0", - "dotenv": "^16.3.1", "e2e-test-utils": "^0.0.1", "jest": "^29.5.0", "jest-junit": "^16.0.0", @@ -60909,6 +60909,7 @@ "@azure/msal-node": "^1.15.0", "axios": "^1.9.0", "connect-redis": "^7.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.3", "hbs": "^4.2.0", @@ -60918,7 +60919,6 @@ "@types/express": "^4.17.17", "@types/express-session": "^1.17.6", "@types/node": "^18.14.1", - "dotenv": "^16.0.3", "nodemon": "^2.0.20", "rimraf": "^4.1.2", "ts-node": "^10.9.1", @@ -60968,10 +60968,10 @@ } }, "samples/msal-node-samples/auth-code-distributed-cache/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61088,6 +61088,7 @@ "@azure/keyvault-certificates": "^4.1.0", "@azure/keyvault-secrets": "^4.1.0", "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0" } }, @@ -61158,6 +61159,18 @@ "node": ">=0.8.0" } }, + "samples/msal-node-samples/auth-code-key-vault/node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "samples/msal-node-samples/auth-code-key-vault/node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -61183,6 +61196,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.2" }, @@ -61190,7 +61204,6 @@ "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", "@types/node": "^16.10.1", - "dotenv": "^16.3.1", "typescript": "^4.4.3" } }, @@ -61201,10 +61214,10 @@ "dev": true }, "samples/msal-node-samples/auth-code-pkce/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61218,15 +61231,28 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "local": "^0.3.3" } }, + "samples/msal-node-samples/auth-code-with-certs/node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "samples/msal-node-samples/auth-code/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61241,13 +61267,13 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-session": "^1.17.2" }, "devDependencies": { "@types/jest": "^29.5.0", - "dotenv": "^16.3.1", "e2e-test-utils": "^0.0.1", "jest": "^29.5.0", "jest-junit": "^16.0.0", @@ -61255,10 +61281,10 @@ } }, "samples/msal-node-samples/b2c-user-flows/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61288,13 +61314,13 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "open": "^8.4.2", "redis": "^4.6.5", "yargs": "^17.7.1" }, "devDependencies": { "@types/node": "^18.14.1", - "dotenv": "^16.0.3", "nodemon": "^2.0.20", "rimraf": "^4.1.2", "ts-node": "^10.9.1", @@ -61320,10 +61346,10 @@ } }, "samples/msal-node-samples/client-credentials-distributed-cache/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61712,7 +61738,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "dotenv": "^16.0.3", + "dotenv": "^16.5.0", "express": "^4.20.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", @@ -61732,6 +61758,7 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", @@ -61741,7 +61768,6 @@ "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.1", "@types/node": "^18.14.1", - "dotenv": "^16.0.3", "nodemon": "^2.0.20", "rimraf": "^4.1.2", "ts-node": "^10.9.1", @@ -61767,10 +61793,10 @@ } }, "samples/msal-node-samples/on-behalf-of-distributed-cache/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61879,9 +61905,10 @@ "dev": true }, "samples/msal-node-samples/on-behalf-of/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61896,19 +61923,18 @@ "@azure/msal-node": "^3.0.0", "adal-node": "^0.2.3", "cookie-parser": "^1.4.6", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.2", "underscore": "^1.13.3" }, - "devDependencies": { - "dotenv": "^16.3.1" - } + "devDependencies": {} }, "samples/msal-node-samples/refresh-token/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61923,13 +61949,13 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-promise-router": "^4.0.1" }, "devDependencies": { "@types/jest": "^29.5.0", - "dotenv": "^16.3.1", "e2e-test-utils": "^0.0.1", "jest": "^29.5.0", "jest-junit": "^16.0.0", @@ -61937,10 +61963,10 @@ } }, "samples/msal-node-samples/silent-flow/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -61961,17 +61987,16 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@azure/msal-node": "^3.0.0" + "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0" }, - "devDependencies": { - "dotenv": "^16.3.1" - } + "devDependencies": {} }, "samples/msal-node-samples/username-password-cca/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, diff --git a/samples/msal-node-samples/Managed-Identity/FIC/.npmrc b/samples/msal-node-samples/Managed-Identity/FIC/.npmrc new file mode 100644 index 0000000000..43c97e719a --- /dev/null +++ b/samples/msal-node-samples/Managed-Identity/FIC/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/samples/msal-node-samples/Managed-Identity/FIC/package.json b/samples/msal-node-samples/Managed-Identity/FIC/package.json index f8666bee56..2d1fc68501 100644 --- a/samples/msal-node-samples/Managed-Identity/FIC/package.json +++ b/samples/msal-node-samples/Managed-Identity/FIC/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@azure/msal-node": "^3.0.0", - "dotenv": "^16.4.5" + "dotenv": "^16.5.0" }, "devDependencies": { "typescript": "^5.3.3" diff --git a/samples/msal-node-samples/Managed-Identity/Imds/.npmrc b/samples/msal-node-samples/Managed-Identity/Imds/.npmrc new file mode 100644 index 0000000000..43c97e719a --- /dev/null +++ b/samples/msal-node-samples/Managed-Identity/Imds/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/samples/msal-node-samples/Managed-Identity/Imds/package.json b/samples/msal-node-samples/Managed-Identity/Imds/package.json index 7b9281b523..66fd3c1c04 100644 --- a/samples/msal-node-samples/Managed-Identity/Imds/package.json +++ b/samples/msal-node-samples/Managed-Identity/Imds/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@azure/msal-node": "^3.0.0", - "dotenv": "^16.4.5" + "dotenv": "^16.5.0" }, "devDependencies": { "typescript": "^5.3.3" diff --git a/samples/msal-node-samples/auth-code-distributed-cache/package.json b/samples/msal-node-samples/auth-code-distributed-cache/package.json index c7eaed98c6..2e133df571 100644 --- a/samples/msal-node-samples/auth-code-distributed-cache/package.json +++ b/samples/msal-node-samples/auth-code-distributed-cache/package.json @@ -16,7 +16,6 @@ "@types/express": "^4.17.17", "@types/express-session": "^1.17.6", "@types/node": "^18.14.1", - "dotenv": "^16.0.3", "nodemon": "^2.0.20", "rimraf": "^4.1.2", "ts-node": "^10.9.1", @@ -26,6 +25,7 @@ "@azure/msal-node": "^1.15.0", "axios": "^1.9.0", "connect-redis": "^7.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.3", "hbs": "^4.2.0", diff --git a/samples/msal-node-samples/auth-code-key-vault/package.json b/samples/msal-node-samples/auth-code-key-vault/package.json index 0dc66ac577..0e481dd404 100644 --- a/samples/msal-node-samples/auth-code-key-vault/package.json +++ b/samples/msal-node-samples/auth-code-key-vault/package.json @@ -16,6 +16,7 @@ "@azure/identity": "^3.4.2", "@azure/keyvault-certificates": "^4.1.0", "@azure/keyvault-secrets": "^4.1.0", + "dotenv": "^16.5.0", "express": "^4.20.0" } -} \ No newline at end of file +} diff --git a/samples/msal-node-samples/auth-code-pkce/package.json b/samples/msal-node-samples/auth-code-pkce/package.json index adb061a968..1b8a6cb03d 100644 --- a/samples/msal-node-samples/auth-code-pkce/package.json +++ b/samples/msal-node-samples/auth-code-pkce/package.json @@ -15,6 +15,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.2" }, @@ -22,7 +23,6 @@ "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", "@types/node": "^16.10.1", - "dotenv": "^16.3.1", "typescript": "^4.4.3" } } diff --git a/samples/msal-node-samples/auth-code-with-certs/package.json b/samples/msal-node-samples/auth-code-with-certs/package.json index bced00afb5..0018ff4eb5 100644 --- a/samples/msal-node-samples/auth-code-with-certs/package.json +++ b/samples/msal-node-samples/auth-code-with-certs/package.json @@ -13,7 +13,8 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "local": "^0.3.3" } -} \ No newline at end of file +} diff --git a/samples/msal-node-samples/auth-code/package.json b/samples/msal-node-samples/auth-code/package.json index 237b917d47..6b525c8815 100644 --- a/samples/msal-node-samples/auth-code/package.json +++ b/samples/msal-node-samples/auth-code/package.json @@ -15,12 +15,12 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.2" }, "devDependencies": { "@types/jest": "^29.5.0", - "dotenv": "^16.3.1", "e2e-test-utils": "^0.0.1", "jest": "^29.5.0", "jest-junit": "^16.0.0", diff --git a/samples/msal-node-samples/b2c-user-flows/package.json b/samples/msal-node-samples/b2c-user-flows/package.json index 172290a8ac..dc02dfd7a3 100644 --- a/samples/msal-node-samples/b2c-user-flows/package.json +++ b/samples/msal-node-samples/b2c-user-flows/package.json @@ -15,13 +15,13 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-session": "^1.17.2" }, "devDependencies": { "@types/jest": "^29.5.0", - "dotenv": "^16.3.1", "e2e-test-utils": "^0.0.1", "jest": "^29.5.0", "jest-junit": "^16.0.0", diff --git a/samples/msal-node-samples/client-credentials-distributed-cache/package.json b/samples/msal-node-samples/client-credentials-distributed-cache/package.json index a4801f5f80..27bc97311e 100644 --- a/samples/msal-node-samples/client-credentials-distributed-cache/package.json +++ b/samples/msal-node-samples/client-credentials-distributed-cache/package.json @@ -14,7 +14,6 @@ "license": "ISC", "devDependencies": { "@types/node": "^18.14.1", - "dotenv": "^16.0.3", "nodemon": "^2.0.20", "rimraf": "^4.1.2", "ts-node": "^10.9.1", @@ -23,6 +22,7 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "open": "^8.4.2", "redis": "^4.6.5", "yargs": "^17.7.1" diff --git a/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json b/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json index 33f143e998..3d4e663d05 100644 --- a/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json +++ b/samples/msal-node-samples/on-behalf-of-distributed-cache/package.json @@ -16,7 +16,6 @@ "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.1", "@types/node": "^18.14.1", - "dotenv": "^16.0.3", "nodemon": "^2.0.20", "rimraf": "^4.1.2", "ts-node": "^10.9.1", @@ -25,6 +24,7 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", diff --git a/samples/msal-node-samples/on-behalf-of/package.json b/samples/msal-node-samples/on-behalf-of/package.json index 424deab420..d93eced0ea 100644 --- a/samples/msal-node-samples/on-behalf-of/package.json +++ b/samples/msal-node-samples/on-behalf-of/package.json @@ -13,7 +13,7 @@ "license": "MIT", "dependencies": { "@azure/msal-node": "^3.0.0", - "dotenv": "^16.0.3", + "dotenv": "^16.5.0", "express": "^4.20.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", diff --git a/samples/msal-node-samples/refresh-token/package.json b/samples/msal-node-samples/refresh-token/package.json index 0944533fc2..a55745d36e 100644 --- a/samples/msal-node-samples/refresh-token/package.json +++ b/samples/msal-node-samples/refresh-token/package.json @@ -14,11 +14,11 @@ "@azure/msal-node": "^3.0.0", "adal-node": "^0.2.3", "cookie-parser": "^1.4.6", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-session": "^1.17.2", "underscore": "^1.13.3" }, "devDependencies": { - "dotenv": "^16.3.1" } -} \ No newline at end of file +} diff --git a/samples/msal-node-samples/silent-flow/package.json b/samples/msal-node-samples/silent-flow/package.json index 584bd515dc..872e9a40e8 100644 --- a/samples/msal-node-samples/silent-flow/package.json +++ b/samples/msal-node-samples/silent-flow/package.json @@ -15,13 +15,13 @@ "dependencies": { "@azure/msal-node": "^3.0.0", "axios": "^1.9.0", + "dotenv": "^16.5.0", "express": "^4.20.0", "express-handlebars": "^5.3.5", "express-promise-router": "^4.0.1" }, "devDependencies": { "@types/jest": "^29.5.0", - "dotenv": "^16.3.1", "e2e-test-utils": "^0.0.1", "jest": "^29.5.0", "jest-junit": "^16.0.0", diff --git a/samples/msal-node-samples/username-password-cca/package.json b/samples/msal-node-samples/username-password-cca/package.json index bc06b25edd..bf263f7e94 100644 --- a/samples/msal-node-samples/username-password-cca/package.json +++ b/samples/msal-node-samples/username-password-cca/package.json @@ -12,9 +12,9 @@ "author": "Microsoft", "license": "MIT", "dependencies": { - "@azure/msal-node": "^3.0.0" + "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0" }, "devDependencies": { - "dotenv": "^16.3.1" } } From aedd7625319a460fec2aaa5521b8d0282441ceea Mon Sep 17 00:00:00 2001 From: Robbie-Microsoft <87724641+Robbie-Microsoft@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:21:21 -0400 Subject: [PATCH 08/15] Client Credential Sample: Moved client secret from code to .env file (#7820) Updated code and README --- package-lock.json | 15 ++++++++++++++- .../client-credentials/README.md | 8 +++++++- .../msal-node-samples/client-credentials/index.js | 8 +++++++- .../client-credentials/package.json | 3 ++- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f03e840f73..7215e4180a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61297,7 +61297,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@azure/msal-node": "^3.0.0" + "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0" }, "devDependencies": { "@types/jest": "^29.5.0", @@ -61485,6 +61486,18 @@ "url": "https://dotenvx.com" } }, + "samples/msal-node-samples/client-credentials/node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "samples/msal-node-samples/custom-INetworkModule-and-network-tracing": { "name": "custom-inetworkmodule-and-network-tracing", "version": "1.0.0", diff --git a/samples/msal-node-samples/client-credentials/README.md b/samples/msal-node-samples/client-credentials/README.md index effbb4884a..c2e87752cd 100644 --- a/samples/msal-node-samples/client-credentials/README.md +++ b/samples/msal-node-samples/client-credentials/README.md @@ -43,11 +43,17 @@ const config = { auth: { clientId: "ENTER_CLIENT_ID", authority: "https://login.microsoftonline.com/ENTER_TENANT_INFO", - clientSecret: "ENTER_CLIENT_SECRET", + process.env.CLIENT_SECRET, }, }; ``` +You will also need to add the client secret to your **.env file**: + +``` +CLIENT_SECRET= +``` + ## Run the app In the same folder, type: diff --git a/samples/msal-node-samples/client-credentials/index.js b/samples/msal-node-samples/client-credentials/index.js index 19239033d3..cb3e237fc6 100644 --- a/samples/msal-node-samples/client-credentials/index.js +++ b/samples/msal-node-samples/client-credentials/index.js @@ -16,6 +16,8 @@ const argv = require("../cliArgs"); const cacheLocation = argv.c || "./data/cache.json"; const cachePlugin = require('../cachePlugin')(cacheLocation); +require('dotenv').config(); + /** * The scenario string is the name of a .json file which contains the MSAL client configuration * For an example of what a configuration file should look like, check out the customConfig.json file in the @@ -55,7 +57,11 @@ if(argv.$0 === "index.js") { // Build MSAL ClientApplication Configuration object const clientConfig = { - auth: config.authOptions, + auth: { + clientId: "ENTER_CLIENT_ID", + authority: "https://login.microsoftonline.com/ENTER_TENANT_INFO", + clientSecret: process.env.CLIENT_SECRET, + }, cache: { cachePlugin }, diff --git a/samples/msal-node-samples/client-credentials/package.json b/samples/msal-node-samples/client-credentials/package.json index 920da5d9ee..0f1ee8a13a 100644 --- a/samples/msal-node-samples/client-credentials/package.json +++ b/samples/msal-node-samples/client-credentials/package.json @@ -14,7 +14,8 @@ "author": "", "license": "MIT", "dependencies": { - "@azure/msal-node": "^3.0.0" + "@azure/msal-node": "^3.0.0", + "dotenv": "^16.5.0" }, "devDependencies": { "@types/jest": "^29.5.0", From 14fe6196f12d190f3f186389bf9ec0daa74cf93f Mon Sep 17 00:00:00 2001 From: Robbie-Microsoft <87724641+Robbie-Microsoft@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:42:58 -0400 Subject: [PATCH 09/15] Added support for DEFAULT_IDENTITY_CLIENT_ID environment variable in Machine Learning Managed Identity (#7616) Fixes https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/7609 If the Machine Learning System Assigned Managed Identity is being used, the "clientid" request parameter will be set to the DEFAULT_IDENTITY_CLIENT_ID environment variable instead of being omitted in the network request. I manually tested this on two different AML clusters - one with SAMI enabled and the other with UAMI enabled. --- ...-8d574304-a0bd-4fc1-988d-d574e0883fa3.json | 7 ++ .../ManagedIdentitySources/MachineLearning.ts | 23 +++- lib/msal-node/src/utils/Constants.ts | 1 + .../ManagedIdentitySources/Imds.spec.ts | 10 +- .../MachineLearning.spec.ts | 107 ++++++++++++------ .../test/test_kit/ManagedIdentityTestUtils.ts | 9 ++ 6 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 change/@azure-msal-node-8d574304-a0bd-4fc1-988d-d574e0883fa3.json diff --git a/change/@azure-msal-node-8d574304-a0bd-4fc1-988d-d574e0883fa3.json b/change/@azure-msal-node-8d574304-a0bd-4fc1-988d-d574e0883fa3.json new file mode 100644 index 0000000000..677ed87a02 --- /dev/null +++ b/change/@azure-msal-node-8d574304-a0bd-4fc1-988d-d574e0883fa3.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Added support for DEFAULT_IDENTITY_CLIENT_ID environment variable in Machine Learning Managed Identity #7616", + "packageName": "@azure/msal-node", + "email": "rginsburg@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/lib/msal-node/src/client/ManagedIdentitySources/MachineLearning.ts b/lib/msal-node/src/client/ManagedIdentitySources/MachineLearning.ts index c41f5796e0..1e5f0a52cd 100644 --- a/lib/msal-node/src/client/ManagedIdentitySources/MachineLearning.ts +++ b/lib/msal-node/src/client/ManagedIdentitySources/MachineLearning.ts @@ -4,7 +4,10 @@ */ import { INetworkModule, Logger } from "@azure/msal-common/node"; -import { BaseManagedIdentitySource } from "./BaseManagedIdentitySource.js"; +import { + BaseManagedIdentitySource, + ManagedIdentityUserAssignedIdQueryParameterNames, +} from "./BaseManagedIdentitySource.js"; import { HttpMethod, ManagedIdentityEnvironmentVariableNames, @@ -20,6 +23,8 @@ import { NodeStorage } from "../../cache/NodeStorage.js"; const MACHINE_LEARNING_MSI_API_VERSION: string = "2017-09-01"; +export const MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR = `Only client id is supported for user-assigned managed identity in ${ManagedIdentitySourceNames.MACHINE_LEARNING}.`; // referenced in unit test + export class MachineLearning extends BaseManagedIdentitySource { private msiEndpoint: string; private secret: string; @@ -115,7 +120,17 @@ export class MachineLearning extends BaseManagedIdentitySource { resource; if ( - managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED + managedIdentityId.idType === ManagedIdentityIdType.SYSTEM_ASSIGNED + ) { + request.queryParameters[ + ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 + ] = process.env[ + ManagedIdentityEnvironmentVariableNames + .DEFAULT_IDENTITY_CLIENT_ID + ] as string; // this environment variable is always set in an Azure Machine Learning source + } else if ( + managedIdentityId.idType === + ManagedIdentityIdType.USER_ASSIGNED_CLIENT_ID ) { request.queryParameters[ this.getManagedIdentityUserAssignedIdQueryParameterKey( @@ -124,6 +139,10 @@ export class MachineLearning extends BaseManagedIdentitySource { true // uses2017API ) ] = managedIdentityId.id; + } else { + throw new Error( + MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR + ); } // bodyParameters calculated in BaseManagedIdentity.acquireTokenWithManagedIdentity diff --git a/lib/msal-node/src/utils/Constants.ts b/lib/msal-node/src/utils/Constants.ts index 0571b1bc03..c539567fe6 100644 --- a/lib/msal-node/src/utils/Constants.ts +++ b/lib/msal-node/src/utils/Constants.ts @@ -41,6 +41,7 @@ export type ManagedIdentityQueryParameters = */ export const ManagedIdentityEnvironmentVariableNames = { AZURE_POD_IDENTITY_AUTHORITY_HOST: "AZURE_POD_IDENTITY_AUTHORITY_HOST", + DEFAULT_IDENTITY_CLIENT_ID: "DEFAULT_IDENTITY_CLIENT_ID", IDENTITY_ENDPOINT: "IDENTITY_ENDPOINT", IDENTITY_HEADER: "IDENTITY_HEADER", IDENTITY_SERVER_THUMBPRINT: "IDENTITY_SERVER_THUMBPRINT", diff --git a/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts index 53afa37006..48a1ff8e4f 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts @@ -32,6 +32,7 @@ import { managedIdentityRequestParams, systemAssignedConfig, userAssignedResourceIdConfig, + userAssignedObjectIdConfig, } from "../../test_kit/ManagedIdentityTestUtils.js"; import { DEFAULT_MANAGED_IDENTITY_ID, @@ -81,15 +82,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { HttpStatus.BAD_REQUEST ); - const userAssignedObjectIdConfig: ManagedIdentityConfiguration = { - system: { - networkClient, - }, - managedIdentityIdParams: { - userAssignedObjectId: MANAGED_IDENTITY_RESOURCE_ID, - }, - }; - describe("User Assigned", () => { test("acquires a User Assigned Client Id token", async () => { const sendGetRequestAsyncSpy: jest.SpyInstance = jest.spyOn( diff --git a/lib/msal-node/test/client/ManagedIdentitySources/MachineLearning.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/MachineLearning.spec.ts index 5f5e41bed3..7317ffbbf3 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/MachineLearning.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/MachineLearning.spec.ts @@ -9,7 +9,6 @@ import { DEFAULT_USER_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT, MANAGED_IDENTITY_MACHINE_LEARNING_NETWORK_REQUEST_400_ERROR, MANAGED_IDENTITY_RESOURCE, - MANAGED_IDENTITY_RESOURCE_ID, } from "../../test_kit/StringConstants.js"; import { @@ -19,6 +18,7 @@ import { networkClient, ManagedIdentityNetworkErrorClient, userAssignedResourceIdConfig, + userAssignedObjectIdConfig, } from "../../test_kit/ManagedIdentityTestUtils.js"; import { AuthenticationResult, @@ -31,9 +31,13 @@ import { ManagedIdentitySourceNames, } from "../../../src/utils/Constants.js"; import { ManagedIdentityUserAssignedIdQueryParameterNames } from "../../../src/client/ManagedIdentitySources/BaseManagedIdentitySource.js"; +import { MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR } from "../../../src/client/ManagedIdentitySources/MachineLearning.js"; describe("Acquires a token successfully via an Machine Learning Managed Identity", () => { beforeAll(() => { + process.env[ + ManagedIdentityEnvironmentVariableNames.DEFAULT_IDENTITY_CLIENT_ID + ] = "fake_DEFAULT_IDENTITY_CLIENT_ID"; process.env[ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT] = "fake_MSI_ENDPOINT"; process.env[ManagedIdentityEnvironmentVariableNames.MSI_SECRET] = @@ -41,6 +45,9 @@ describe("Acquires a token successfully via an Machine Learning Managed Identity }); afterAll(() => { + delete process.env[ + ManagedIdentityEnvironmentVariableNames.DEFAULT_IDENTITY_CLIENT_ID + ]; delete process.env[ ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT ]; @@ -80,52 +87,24 @@ describe("Acquires a token successfully via an Machine Learning Managed Identity const url: URLSearchParams = new URLSearchParams( sendGetRequestAsyncSpy.mock.lastCall[0] ); - expect( - url.has( - ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 - ) - ).toBe(true); expect( url.has( ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID ) ).toBe(false); - }); - - test("acquires a User Assigned Resource Id token", async () => { - const sendGetRequestAsyncSpy: jest.SpyInstance = jest.spyOn( - networkClient, - "sendGetRequestAsync" - ); - - const managedIdentityApplication: ManagedIdentityApplication = - new ManagedIdentityApplication(userAssignedResourceIdConfig); - expect(managedIdentityApplication.getManagedIdentitySource()).toBe( - ManagedIdentitySourceNames.MACHINE_LEARNING - ); - - const networkManagedIdentityResult: AuthenticationResult = - await managedIdentityApplication.acquireToken( - managedIdentityRequestParams - ); - - expect(networkManagedIdentityResult.accessToken).toEqual( - DEFAULT_USER_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT.accessToken - ); - - const url: URLSearchParams = new URLSearchParams( - sendGetRequestAsyncSpy.mock.lastCall[0] - ); expect( url.has( - ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_RESOURCE_ID_NON_IMDS + ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 ) ).toBe(true); expect( url.get( - ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_RESOURCE_ID_NON_IMDS + ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 ) - ).toEqual(MANAGED_IDENTITY_RESOURCE_ID); + ).toEqual( + userAssignedClientIdConfig.managedIdentityIdParams + ?.userAssignedClientId + ); }); test("ensures that App Service is selected as the Managed Identity source when all App Service and Machine Learning environment variables are present", async () => { @@ -163,6 +142,11 @@ describe("Acquires a token successfully via an Machine Learning Managed Identity }); test("acquires a token", async () => { + const sendGetRequestAsyncSpy: jest.SpyInstance = jest.spyOn( + networkClient, + "sendGetRequestAsync" + ); + const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( managedIdentityRequestParams @@ -172,6 +156,30 @@ describe("Acquires a token successfully via an Machine Learning Managed Identity expect(networkManagedIdentityResult.accessToken).toEqual( DEFAULT_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT.accessToken ); + + const url: URLSearchParams = new URLSearchParams( + sendGetRequestAsyncSpy.mock.lastCall[0] + ); + expect( + url.has( + ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID + ) + ).toBe(false); + expect( + url.has( + ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 + ) + ).toBe(true); + expect( + url.get( + ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 + ) + ).toEqual( + process.env[ + ManagedIdentityEnvironmentVariableNames + .DEFAULT_IDENTITY_CLIENT_ID + ] + ); }); test("returns an already acquired token from the cache", async () => { @@ -237,5 +245,34 @@ describe("Acquires a token successfully via an Machine Learning Managed Identity ) ).toBe(true); }); + + test.each([ + ["a resource", userAssignedResourceIdConfig], + ["an object", userAssignedObjectIdConfig], + ])( + "ensures that providing %s id will throw an error", + async (_description, userAssignedIdConfig) => { + const managedIdentityApplication: ManagedIdentityApplication = + new ManagedIdentityApplication(userAssignedIdConfig); + expect( + managedIdentityApplication.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.MACHINE_LEARNING); + + let error: Error = new Error(); + try { + await managedIdentityApplication.acquireToken( + managedIdentityRequestParams + ); + } catch (e) { + error = e as Error; + } + + expect( + error.message.includes( + MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR + ) + ).toBe(true); + } + ); }); }); diff --git a/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts b/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts index 0cd263fb4c..0640e7930d 100644 --- a/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts +++ b/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts @@ -135,6 +135,15 @@ export const userAssignedResourceIdConfig: ManagedIdentityConfiguration = { }, }; +export const userAssignedObjectIdConfig: ManagedIdentityConfiguration = { + system: { + networkClient, + }, + managedIdentityIdParams: { + userAssignedObjectId: MANAGED_IDENTITY_RESOURCE_ID, + }, +}; + export const systemAssignedConfig: ManagedIdentityConfiguration = { system: { networkClient, From d16f8cbfa1ad47e6a6ee1b0e56ff3a51e182e6fe Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 19 Jun 2025 13:53:47 -0700 Subject: [PATCH 10/15] Move navigateToLoginRequestUrl to request config --- lib/msal-browser/src/config/Configuration.ts | 6 +---- .../src/controllers/StandardController.ts | 21 ++++++++++++---- .../PlatformAuthInteractionClient.ts | 4 +++- .../src/interaction_client/RedirectClient.ts | 12 +++++++--- .../src/request/RedirectRequest.ts | 2 ++ .../test/config/Configuration.spec.ts | 3 --- .../interaction_client/RedirectClient.spec.ts | 24 ++++++++++++------- 7 files changed, 48 insertions(+), 24 deletions(-) diff --git a/lib/msal-browser/src/config/Configuration.ts b/lib/msal-browser/src/config/Configuration.ts index f88cd771fe..8b138a5a26 100644 --- a/lib/msal-browser/src/config/Configuration.ts +++ b/lib/msal-browser/src/config/Configuration.ts @@ -69,10 +69,7 @@ export type BrowserAuthOptions = { * The redirect URI where the window navigates after a successful logout. */ postLogoutRedirectUri?: string | null; - /** - * Boolean indicating whether to navigate to the original request URL after the auth server navigates to the redirect URL. - */ - navigateToLoginRequestUrl?: boolean; + /** * Array of capabilities which will be added to the claims.access_token.xms_cc request property on every network request. */ @@ -241,7 +238,6 @@ export function buildConfiguration( redirectUri: typeof window !== "undefined" ? BrowserUtils.getCurrentUri() : "", postLogoutRedirectUri: "", - navigateToLoginRequestUrl: true, clientCapabilities: [], OIDCOptions: { responseMode: Constants.ResponseMode.FRAGMENT, diff --git a/lib/msal-browser/src/controllers/StandardController.ts b/lib/msal-browser/src/controllers/StandardController.ts index 85bfd17742..74df5c1760 100644 --- a/lib/msal-browser/src/controllers/StandardController.ts +++ b/lib/msal-browser/src/controllers/StandardController.ts @@ -392,10 +392,14 @@ export class StandardController implements IController { * has loaded during redirect flows. This should be invoked on all page loads involved in redirect * auth flows. * @param hash Hash to process. Defaults to the current value of window.location.hash. Only needs to be provided explicitly if the response to be handled is not contained in the current value. + * @param options Object containing optional configuration for redirect promise handling. * @returns Token response or null. If the return value is null, then no auth redirect was detected. */ async handleRedirectPromise( - hash?: string + hash?: string, + options?: { + navigateToLoginRequestUrl?: boolean; + } ): Promise { this.logger.verbose("handleRedirectPromise called"); // Block token acquisition before initialize has been called @@ -410,7 +414,7 @@ export class StandardController implements IController { const redirectResponseKey = hash || ""; let response = this.redirectResponse.get(redirectResponseKey); if (typeof response === "undefined") { - response = this.handleRedirectPromiseInternal(hash); + response = this.handleRedirectPromiseInternal(hash, options); this.redirectResponse.set(redirectResponseKey, response); this.logger.verbose( "handleRedirectPromise has been called for the first time, storing the promise" @@ -435,7 +439,10 @@ export class StandardController implements IController { * @returns */ private async handleRedirectPromiseInternal( - hash?: string + hash?: string, + options?: { + navigateToLoginRequestUrl?: boolean; + } ): Promise { if (!this.browserStorage.isInteractionInProgress(true)) { this.logger.info( @@ -518,7 +525,13 @@ export class StandardController implements IController { this.logger, this.performanceClient, rootMeasurement.event.correlationId - )(hash, standardRequest, codeVerifier, rootMeasurement); + )( + hash, + standardRequest, + codeVerifier, + rootMeasurement, + options + ); } } catch (e) { this.browserStorage.resetRequestCache(); diff --git a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts index a401585163..13cbfe5b3f 100644 --- a/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts +++ b/lib/msal-browser/src/interaction_client/PlatformAuthInteractionClient.ts @@ -311,6 +311,8 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { ); const nativeRequest = await this.initializeNativeRequest(request); + const navigateToLoginRequestUrl = + request.navigateToLoginRequestUrl ?? true; try { await this.platformAuthProvider.sendMessage(nativeRequest); @@ -336,7 +338,7 @@ export class PlatformAuthInteractionClient extends BaseInteractionClient { timeout: this.config.system.redirectNavigationTimeout, noHistory: false, }; - const redirectUri = this.config.auth.navigateToLoginRequestUrl + const redirectUri = navigateToLoginRequestUrl ? window.location.href : this.getRedirectUri(request.redirectUri); rootMeasurement.end({ success: true }); diff --git a/lib/msal-browser/src/interaction_client/RedirectClient.ts b/lib/msal-browser/src/interaction_client/RedirectClient.ts index 7a88256d56..2ee4b10a7b 100644 --- a/lib/msal-browser/src/interaction_client/RedirectClient.ts +++ b/lib/msal-browser/src/interaction_client/RedirectClient.ts @@ -288,12 +288,18 @@ export class RedirectClient extends StandardInteractionClient { hash: string = "", request: CommonAuthorizationUrlRequest, pkceVerifier: string, - parentMeasurement: InProgressPerformanceEvent + parentMeasurement: InProgressPerformanceEvent, + options?: { + navigateToLoginRequestUrl?: boolean; + } ): Promise { const serverTelemetryManager = this.initializeServerTelemetryManager( ApiId.handleRedirectPromise ); + const navigateToLoginRequestUrl = + options?.navigateToLoginRequestUrl ?? true; + try { const [serverParams, responseString] = this.getRedirectResponse( hash || "" @@ -330,7 +336,7 @@ export class RedirectClient extends StandardInteractionClient { if ( loginRequestUrlNormalized === currentUrlNormalized && - this.config.auth.navigateToLoginRequestUrl + navigateToLoginRequestUrl ) { // We are on the page we need to navigate to - handle hash this.logger.verbose( @@ -350,7 +356,7 @@ export class RedirectClient extends StandardInteractionClient { ); return handleHashResult; - } else if (!this.config.auth.navigateToLoginRequestUrl) { + } else if (!navigateToLoginRequestUrl) { this.logger.verbose( "NavigateToLoginRequestUrl set to false, handling response" ); diff --git a/lib/msal-browser/src/request/RedirectRequest.ts b/lib/msal-browser/src/request/RedirectRequest.ts index 4d4dda4ca4..6f5039e4eb 100644 --- a/lib/msal-browser/src/request/RedirectRequest.ts +++ b/lib/msal-browser/src/request/RedirectRequest.ts @@ -31,6 +31,7 @@ import { CommonAuthorizationUrlRequest } from "@azure/msal-common/browser"; * - claims - In cases where Azure AD tenant admin has enabled conditional access policies, and the policy has not been met, exceptions will contain claims that need to be consented to. * - nonce - A value included in the request that is returned in the id token. A randomly generated unique value is typically used to mitigate replay attacks. * - redirectStartPage - The page that should be returned to after loginRedirect or acquireTokenRedirect. This should only be used if this is different from the redirectUri and will default to the page that initiates the request. When the navigateToLoginRequestUrl config option is set to false this parameter will be ignored. + * - navigateToLoginRequestUrl - If true, the browser will navigate to the login request URL after the redirect response is received. If false, the response will be returned to the calling code and the browser will not navigate to the login request URL. */ export type RedirectRequest = Partial< Omit< @@ -46,4 +47,5 @@ export type RedirectRequest = Partial< > & { scopes: Array; redirectStartPage?: string; + navigateToLoginRequestUrl?: boolean; }; diff --git a/lib/msal-browser/test/config/Configuration.spec.ts b/lib/msal-browser/test/config/Configuration.spec.ts index bd3a8d75d2..8b76c62ddf 100644 --- a/lib/msal-browser/test/config/Configuration.spec.ts +++ b/lib/msal-browser/test/config/Configuration.spec.ts @@ -42,7 +42,6 @@ describe("Configuration.ts Class Unit Tests", () => { ); expect(emptyConfig.auth.redirectUri).toBeDefined(); expect(emptyConfig.auth.postLogoutRedirectUri).toBe(""); - expect(emptyConfig.auth.navigateToLoginRequestUrl).toBe(true); expect(emptyConfig.auth?.azureCloudOptions?.azureCloudInstance).toBe( AzureCloudInstance.None ); @@ -234,7 +233,6 @@ describe("Configuration.ts Class Unit Tests", () => { authority: TEST_CONFIG.validAuthority, redirectUri: TEST_URIS.TEST_ALTERNATE_REDIR_URI, postLogoutRedirectUri: TEST_URIS.TEST_LOGOUT_URI, - navigateToLoginRequestUrl: false, }, cache: { cacheLocation: BrowserCacheLocation.LocalStorage, @@ -261,7 +259,6 @@ describe("Configuration.ts Class Unit Tests", () => { expect(newConfig.auth.postLogoutRedirectUri).toBe( TEST_URIS.TEST_LOGOUT_URI ); - expect(newConfig.auth.navigateToLoginRequestUrl).toBe(false); // Cache config checks expect(newConfig.cache).not.toBeNull(); expect(newConfig.cache?.cacheLocation).not.toBeNull(); diff --git a/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts b/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts index 649d873718..4a56b42b84 100644 --- a/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts +++ b/lib/msal-browser/test/interaction_client/RedirectClient.spec.ts @@ -346,7 +346,6 @@ describe("RedirectClient", () => { auth: { // @ts-ignore ...pca.config.auth, - navigateToLoginRequestUrl: false, }, }, browserStorage, @@ -368,7 +367,10 @@ describe("RedirectClient", () => { TEST_HASHES.TEST_SUCCESS_CODE_HASH_REDIRECT, testRequest, TEST_CONFIG.TEST_VERIFIER, - rootMeasurement + rootMeasurement, + { + navigateToLoginRequestUrl: false, + } ) .catch((e) => { expect(e).toEqual("Error in handleResponse"); @@ -850,7 +852,6 @@ describe("RedirectClient", () => { let pca = new PublicClientApplication({ auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, - navigateToLoginRequestUrl: false, }, }); @@ -883,7 +884,10 @@ describe("RedirectClient", () => { "", testRequest, TEST_CONFIG.TEST_VERIFIER, - rootMeasurement + rootMeasurement, + { + navigateToLoginRequestUrl: false, + } ); expect(tokenResponse?.uniqueId).toEqual(testTokenResponse.uniqueId); expect(tokenResponse?.tenantId).toEqual(testTokenResponse.tenantId); @@ -1147,7 +1151,6 @@ describe("RedirectClient", () => { let pca = new PublicClientApplication({ auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, - navigateToLoginRequestUrl: false, }, }); @@ -1180,7 +1183,10 @@ describe("RedirectClient", () => { "", testRequest, TEST_CONFIG.TEST_VERIFIER, - rootMeasurement + rootMeasurement, + { + navigateToLoginRequestUrl: false, + } ); expect(tokenResponse?.uniqueId).toEqual(testTokenResponse.uniqueId); expect(tokenResponse?.tenantId).toEqual(testTokenResponse.tenantId); @@ -1667,7 +1673,6 @@ describe("RedirectClient", () => { let pca = new PublicClientApplication({ auth: { clientId: TEST_CONFIG.MSAL_CLIENT_ID, - navigateToLoginRequestUrl: false, }, }); @@ -1717,7 +1722,10 @@ describe("RedirectClient", () => { "", testRequest, TEST_CONFIG.TEST_VERIFIER, - rootMeasurement + rootMeasurement, + { + navigateToLoginRequestUrl: false, + } ); }); From 9192903491e9d78715f578a7d1ee5720e01ead60 Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 19 Jun 2025 14:09:28 -0700 Subject: [PATCH 11/15] Update interface and docs --- lib/msal-browser/FAQ.md | 2 +- lib/msal-browser/docs/v4-migration.md | 7 +++++++ lib/msal-browser/src/app/PublicClientApplication.ts | 7 +++++-- lib/msal-browser/src/controllers/IController.ts | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/msal-browser/FAQ.md b/lib/msal-browser/FAQ.md index f555db01f2..94b35db387 100644 --- a/lib/msal-browser/FAQ.md +++ b/lib/msal-browser/FAQ.md @@ -171,7 +171,7 @@ For a full implementation, please refer to the app creation scripts in the [Vani The redirect flow can be confusing, as redirecting away from the page means you are creating a whole new instance of the application when you return. This means that calling a redirect method cannot return anything. Rather, what happens is that the page is redirected away, you enter your credentials, and you are redirected back to your application with the response in the url hash. -If `navigateToLoginRequestUrl` property in MSAL configuration parameters is set to **true**, you will be redirected again to the page you were on when you called `loginRedirect`, unless that page was also set as your `redirectUri`. On the final page your application must call `handleRedirectPromise()` in order to process the hash and cache tokens in local/session storage. +If `navigateToLoginRequestUrl` property is passed in as an option to `handleRedirectPromise` and set to **true**, you will be redirected again to the page you were on when you called `loginRedirect`, unless that page was also set as your `redirectUri`. On the final page your application must call `handleRedirectPromise()` in order to process the hash and cache tokens in local/session storage. As this function returns a promise you can call `.then` and `.catch`, similar to `loginPopup`. diff --git a/lib/msal-browser/docs/v4-migration.md b/lib/msal-browser/docs/v4-migration.md index 884446f2a9..0c5e007943 100644 --- a/lib/msal-browser/docs/v4-migration.md +++ b/lib/msal-browser/docs/v4-migration.md @@ -65,6 +65,13 @@ await loadExternalTokens( 1. The `skipAuthorityMetadataCache` parameter has been removed from BrowserAuthOptions in Configuration. 1. The `protocolMode` parameter has been moved to SystemOptions instead of BrowserAuthOptions in Configuration. 1. The `supportsNestedAppAuth` parameter has been removed. Use the `createNestablePublicClientApplication` API for Nested Apps instead. Read more about Nested Apps [here](./initialization.md#nested-app-configuration). +1. The `navigateTologinRequestUrl` parameter has been removed from BrowserAuthOptions in Configuration. Default behavior remains the same (set to true), in order to maintain custom configuration: + 1. For APIs that take a `RedirectRequest` object, the type now has a `navigateToLoginRequestUrl` parameter + 1. For `handleRedirectPromise`, `navigateToLoginRequestUrl` has to be passed in as an option as follows: + + ```typescript + pca.handleRedirectPromise(hash, { navigateToLoginRequestUrl: false }) + ``` ### CacheOptions changes diff --git a/lib/msal-browser/src/app/PublicClientApplication.ts b/lib/msal-browser/src/app/PublicClientApplication.ts index fc630be260..41fc26438f 100644 --- a/lib/msal-browser/src/app/PublicClientApplication.ts +++ b/lib/msal-browser/src/app/PublicClientApplication.ts @@ -215,9 +215,12 @@ export class PublicClientApplication implements IPublicClientApplication { * @returns Token response or null. If the return value is null, then no auth redirect was detected. */ handleRedirectPromise( - hash?: string | undefined + hash?: string | undefined, + options?: { + navigateToLoginRequestUrl?: boolean; + } ): Promise { - return this.controller.handleRedirectPromise(hash); + return this.controller.handleRedirectPromise(hash, options); } /** diff --git a/lib/msal-browser/src/controllers/IController.ts b/lib/msal-browser/src/controllers/IController.ts index c70722614e..c76f78cd9a 100644 --- a/lib/msal-browser/src/controllers/IController.ts +++ b/lib/msal-browser/src/controllers/IController.ts @@ -72,7 +72,7 @@ export interface IController { getAllAccounts(accountFilter?: AccountFilter): AccountInfo[]; - handleRedirectPromise(hash?: string): Promise; + handleRedirectPromise(hash?: string, options?: { navigateToLoginRequestUrl?: boolean }): Promise; loginPopup(request?: PopupRequest): Promise; From 7fae6d2abc2314237e3df885052ec70c3d4122b6 Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 19 Jun 2025 14:12:28 -0700 Subject: [PATCH 12/15] Change files --- ...-msal-browser-cfc942e4-b61f-4c9d-b4af-ed9cb7636840.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@azure-msal-browser-cfc942e4-b61f-4c9d-b4af-ed9cb7636840.json diff --git a/change/@azure-msal-browser-cfc942e4-b61f-4c9d-b4af-ed9cb7636840.json b/change/@azure-msal-browser-cfc942e4-b61f-4c9d-b4af-ed9cb7636840.json new file mode 100644 index 0000000000..dca180a76e --- /dev/null +++ b/change/@azure-msal-browser-cfc942e4-b61f-4c9d-b4af-ed9cb7636840.json @@ -0,0 +1,7 @@ +{ + "type": "major", + "comment": "Move navigateToLoginRequestUrl to request config", + "packageName": "@azure/msal-browser", + "email": "hemoral@microsoft.com", + "dependentChangeType": "patch" +} From 12a247a08c164c733e9755644816d9972f303bff Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 19 Jun 2025 14:15:45 -0700 Subject: [PATCH 13/15] Update API review and documentating comments --- lib/msal-browser/apiReview/msal-browser.api.md | 12 ++++++++---- lib/msal-browser/src/app/PublicClientApplication.ts | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/msal-browser/apiReview/msal-browser.api.md b/lib/msal-browser/apiReview/msal-browser.api.md index 5b6f362868..4dba901a92 100644 --- a/lib/msal-browser/apiReview/msal-browser.api.md +++ b/lib/msal-browser/apiReview/msal-browser.api.md @@ -281,7 +281,6 @@ export type BrowserAuthOptions = { authorityMetadata?: string; redirectUri?: string; postLogoutRedirectUri?: string | null; - navigateToLoginRequestUrl?: boolean; clientCapabilities?: Array; OIDCOptions?: OIDCOptions; azureCloudOptions?: AzureCloudOptions; @@ -731,7 +730,9 @@ export interface IController { // @internal (undocumented) getPerformanceClient(): IPerformanceClient; // (undocumented) - handleRedirectPromise(hash?: string): Promise; + handleRedirectPromise(hash?: string, options?: { + navigateToLoginRequestUrl?: boolean; + }): Promise; // (undocumented) hydrateCache(result: AuthenticationResult, request: SilentRequest | SsoSilentRequest | RedirectRequest | PopupRequest): Promise; // (undocumented) @@ -1257,7 +1258,9 @@ export class PublicClientApplication implements IPublicClientApplication { getConfiguration(): BrowserConfiguration; getLogger(): Logger; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen - handleRedirectPromise(hash?: string | undefined): Promise; + handleRedirectPromise(hash?: string | undefined, options?: { + navigateToLoginRequestUrl?: boolean; + }): Promise; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen hydrateCache(result: AuthenticationResult, request: SilentRequest | SsoSilentRequest | RedirectRequest | PopupRequest): Promise; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen @@ -1382,6 +1385,7 @@ function redirectPreflightCheck(initialized: boolean, config: BrowserConfigurati export type RedirectRequest = Partial> & { scopes: Array; redirectStartPage?: string; + navigateToLoginRequestUrl?: boolean; }; // Warning: (ae-missing-release-tag) "replaceHash" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1569,7 +1573,7 @@ export type WrapperSKU = (typeof WrapperSKU)[keyof typeof WrapperSKU]; // src/cache/LocalStorage.ts:297:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/cache/LocalStorage.ts:355:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/cache/LocalStorage.ts:386:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// src/config/Configuration.ts:210:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts +// src/config/Configuration.ts:207:5 - (ae-forgotten-export) The symbol "InternalAuthOptions" needs to be exported by the entry point index.d.ts // src/event/EventHandler.ts:113:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/event/EventHandler.ts:139:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen // src/index.ts:8:12 - (tsdoc-characters-after-block-tag) The token "@azure" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" diff --git a/lib/msal-browser/src/app/PublicClientApplication.ts b/lib/msal-browser/src/app/PublicClientApplication.ts index 41fc26438f..94b69e79d9 100644 --- a/lib/msal-browser/src/app/PublicClientApplication.ts +++ b/lib/msal-browser/src/app/PublicClientApplication.ts @@ -212,6 +212,7 @@ export class PublicClientApplication implements IPublicClientApplication { * has loaded during redirect flows. This should be invoked on all page loads involved in redirect * auth flows. * @param hash Hash to process. Defaults to the current value of window.location.hash. Only needs to be provided explicitly if the response to be handled is not contained in the current value. + * @param options Object containing optional configuration for redirect promise handling. * @returns Token response or null. If the return value is null, then no auth redirect was detected. */ handleRedirectPromise( From 609284ab334e68e43cc62a804e534a2de57d02dc Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 19 Jun 2025 14:23:59 -0700 Subject: [PATCH 14/15] Update API review --- lib/msal-browser/apiReview/msal-browser.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/msal-browser/apiReview/msal-browser.api.md b/lib/msal-browser/apiReview/msal-browser.api.md index 4dba901a92..0cf3ff234f 100644 --- a/lib/msal-browser/apiReview/msal-browser.api.md +++ b/lib/msal-browser/apiReview/msal-browser.api.md @@ -1258,6 +1258,7 @@ export class PublicClientApplication implements IPublicClientApplication { getConfiguration(): BrowserConfiguration; getLogger(): Logger; // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen handleRedirectPromise(hash?: string | undefined, options?: { navigateToLoginRequestUrl?: boolean; }): Promise; From d81962cd11f9372182c333d6655ccf41f6f457ac Mon Sep 17 00:00:00 2001 From: Hector Morales Date: Thu, 19 Jun 2025 14:32:25 -0700 Subject: [PATCH 15/15] Format fix --- lib/msal-browser/src/controllers/IController.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/msal-browser/src/controllers/IController.ts b/lib/msal-browser/src/controllers/IController.ts index c76f78cd9a..478436eac6 100644 --- a/lib/msal-browser/src/controllers/IController.ts +++ b/lib/msal-browser/src/controllers/IController.ts @@ -72,7 +72,10 @@ export interface IController { getAllAccounts(accountFilter?: AccountFilter): AccountInfo[]; - handleRedirectPromise(hash?: string, options?: { navigateToLoginRequestUrl?: boolean }): Promise; + handleRedirectPromise( + hash?: string, + options?: { navigateToLoginRequestUrl?: boolean } + ): Promise; loginPopup(request?: PopupRequest): Promise;