diff --git a/change/@azure-msal-node-57db4095-d80e-49e1-b4ca-835fe9d50cb3.json b/change/@azure-msal-node-57db4095-d80e-49e1-b4ca-835fe9d50cb3.json new file mode 100644 index 0000000000..ec7a5b53b4 --- /dev/null +++ b/change/@azure-msal-node-57db4095-d80e-49e1-b4ca-835fe9d50cb3.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Added API for Managed Identity to detect the current environment #7093", + "packageName": "@azure/msal-node", + "email": "rginsburg@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/lib/msal-node/apiReview/msal-node.api.md b/lib/msal-node/apiReview/msal-node.api.md index 8a3ef577b3..4465215271 100644 --- a/lib/msal-node/apiReview/msal-node.api.md +++ b/lib/msal-node/apiReview/msal-node.api.md @@ -412,13 +412,11 @@ export { Logger } export { LogLevel } -// Warning: (ae-missing-release-tag) "ManagedIdentityApplication" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export class ManagedIdentityApplication { constructor(configuration?: ManagedIdentityConfiguration); - // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen acquireToken(managedIdentityRequestParams: ManagedIdentityRequestParams): Promise; + getManagedIdentitySource(): ManagedIdentitySourceNames; } // Warning: (ae-missing-release-tag) "ManagedIdentityConfiguration" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -450,6 +448,19 @@ export type ManagedIdentityRequestParams = { resource: string; }; +// @public +export const ManagedIdentitySourceNames: { + readonly APP_SERVICE: "AppService"; + readonly AZURE_ARC: "AzureArc"; + readonly CLOUD_SHELL: "CloudShell"; + readonly DEFAULT_TO_IMDS: "DefaultToImds"; + readonly IMDS: "Imds"; + readonly SERVICE_FABRIC: "ServiceFabric"; +}; + +// @public +export type ManagedIdentitySourceNames = (typeof ManagedIdentitySourceNames)[keyof typeof ManagedIdentitySourceNames]; + export { NetworkRequestOptions } export { NetworkResponse } diff --git a/lib/msal-node/src/client/ManagedIdentityApplication.ts b/lib/msal-node/src/client/ManagedIdentityApplication.ts index 6208dfb002..a14ef67ec2 100644 --- a/lib/msal-node/src/client/ManagedIdentityApplication.ts +++ b/lib/msal-node/src/client/ManagedIdentityApplication.ts @@ -31,10 +31,14 @@ import { ClientCredentialClient } from "./ClientCredentialClient"; import { ManagedIdentityClient } from "./ManagedIdentityClient"; import { ManagedIdentityRequestParams } from "../request/ManagedIdentityRequestParams"; import { NodeStorage } from "../cache/NodeStorage"; -import { DEFAULT_AUTHORITY_FOR_MANAGED_IDENTITY } from "../utils/Constants"; +import { + DEFAULT_AUTHORITY_FOR_MANAGED_IDENTITY, + ManagedIdentitySourceNames, +} from "../utils/Constants"; /** * Class to initialize a managed identity and identify the service + * @public */ export class ManagedIdentityApplication { private config: ManagedIdentityNodeConfiguration; @@ -113,7 +117,7 @@ export class ManagedIdentityApplication { /** * Acquire an access token from the cache or the managed identity - * @param managedIdentityRequest + * @param managedIdentityRequest - the ManagedIdentityRequestParams object passed in by the developer * @returns the access token */ public async acquireToken( @@ -183,4 +187,15 @@ export class ManagedIdentityApplication { ); } } + + /** + * Determine the Managed Identity Source based on available environment variables. This API is consumed by Azure Identity SDK. + * @returns ManagedIdentitySourceNames - The Managed Identity source's name + */ + public getManagedIdentitySource(): ManagedIdentitySourceNames { + return ( + ManagedIdentityClient.sourceName || + this.managedIdentityClient.getManagedIdentitySource() + ); + } } diff --git a/lib/msal-node/src/client/ManagedIdentityClient.ts b/lib/msal-node/src/client/ManagedIdentityClient.ts index c2a74db1c5..8ca8a13c03 100644 --- a/lib/msal-node/src/client/ManagedIdentityClient.ts +++ b/lib/msal-node/src/client/ManagedIdentityClient.ts @@ -23,6 +23,7 @@ import { ManagedIdentityRequest } from "../request/ManagedIdentityRequest"; import { ManagedIdentityId } from "../config/ManagedIdentityId"; import { NodeStorage } from "../cache/NodeStorage"; import { BaseManagedIdentitySource } from "./ManagedIdentitySources/BaseManagedIdentitySource"; +import { ManagedIdentitySourceNames } from "../utils/Constants"; /* * Class to initialize a managed identity and identify the service. @@ -35,6 +36,7 @@ export class ManagedIdentityClient { private cryptoProvider: CryptoProvider; private static identitySource?: BaseManagedIdentitySource; + public static sourceName?: ManagedIdentitySourceNames; constructor( logger: Logger, @@ -73,6 +75,43 @@ export class ManagedIdentityClient { ); } + private allEnvironmentVariablesAreDefined( + environmentVariables: Array + ): boolean { + return Object.values(environmentVariables).every( + (environmentVariable) => { + return environmentVariable !== undefined; + } + ); + } + + /** + * Determine the Managed Identity Source based on available environment variables. This API is consumed by ManagedIdentityApplication's getManagedIdentitySource. + * @returns ManagedIdentitySourceNames - The Managed Identity source's name + */ + public getManagedIdentitySource(): ManagedIdentitySourceNames { + ManagedIdentityClient.sourceName = + this.allEnvironmentVariablesAreDefined( + ServiceFabric.getEnvironmentVariables() + ) + ? ManagedIdentitySourceNames.SERVICE_FABRIC + : this.allEnvironmentVariablesAreDefined( + AppService.getEnvironmentVariables() + ) + ? ManagedIdentitySourceNames.APP_SERVICE + : this.allEnvironmentVariablesAreDefined( + CloudShell.getEnvironmentVariables() + ) + ? ManagedIdentitySourceNames.CLOUD_SHELL + : this.allEnvironmentVariablesAreDefined( + AzureArc.getEnvironmentVariables() + ) + ? ManagedIdentitySourceNames.AZURE_ARC + : ManagedIdentitySourceNames.DEFAULT_TO_IMDS; + + return ManagedIdentityClient.sourceName; + } + /** * Tries to create a managed identity source for all sources * @returns the managed identity Source diff --git a/lib/msal-node/src/client/ManagedIdentitySources/AppService.ts b/lib/msal-node/src/client/ManagedIdentitySources/AppService.ts index 2300a2979e..8f4ac9ca4e 100644 --- a/lib/msal-node/src/client/ManagedIdentitySources/AppService.ts +++ b/lib/msal-node/src/client/ManagedIdentitySources/AppService.ts @@ -43,12 +43,7 @@ export class AppService extends BaseManagedIdentitySource { this.identityHeader = identityHeader; } - public static tryCreate( - logger: Logger, - nodeStorage: NodeStorage, - networkClient: INetworkModule, - cryptoProvider: CryptoProvider - ): AppService | null { + public static getEnvironmentVariables(): Array { const identityEndpoint: string | undefined = process.env[ ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT @@ -58,6 +53,18 @@ export class AppService extends BaseManagedIdentitySource { ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER ]; + return [identityEndpoint, identityHeader]; + } + + public static tryCreate( + logger: Logger, + nodeStorage: NodeStorage, + networkClient: INetworkModule, + cryptoProvider: CryptoProvider + ): AppService | null { + const [identityEndpoint, identityHeader] = + AppService.getEnvironmentVariables(); + // if either of the identity endpoint or identity header variables are undefined, this MSI provider is unavailable. if (!identityEndpoint || !identityHeader) { logger.info( diff --git a/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts b/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts index f0ae556004..779b958ea3 100644 --- a/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts +++ b/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts @@ -56,6 +56,17 @@ export class AzureArc extends BaseManagedIdentitySource { this.identityEndpoint = identityEndpoint; } + public static getEnvironmentVariables(): Array { + const identityEndpoint: string | undefined = + process.env[ + ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT + ]; + const imdsEndpoint: string | undefined = + process.env[ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT]; + + return [identityEndpoint, imdsEndpoint]; + } + public static tryCreate( logger: Logger, nodeStorage: NodeStorage, @@ -63,12 +74,8 @@ export class AzureArc extends BaseManagedIdentitySource { cryptoProvider: CryptoProvider, managedIdentityId: ManagedIdentityId ): AzureArc | null { - const identityEndpoint: string | undefined = - process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT - ]; - const imdsEndpoint: string | undefined = - process.env[ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT]; + const [identityEndpoint, imdsEndpoint] = + AzureArc.getEnvironmentVariables(); // if either of the identity or imds endpoints are undefined, this MSI provider is unavailable. if (!identityEndpoint || !imdsEndpoint) { diff --git a/lib/msal-node/src/client/ManagedIdentitySources/CloudShell.ts b/lib/msal-node/src/client/ManagedIdentitySources/CloudShell.ts index a62781d009..030823af32 100644 --- a/lib/msal-node/src/client/ManagedIdentitySources/CloudShell.ts +++ b/lib/msal-node/src/client/ManagedIdentitySources/CloudShell.ts @@ -40,6 +40,13 @@ export class CloudShell extends BaseManagedIdentitySource { this.msiEndpoint = msiEndpoint; } + public static getEnvironmentVariables(): Array { + const msiEndpoint: string | undefined = + process.env[ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT]; + + return [msiEndpoint]; + } + public static tryCreate( logger: Logger, nodeStorage: NodeStorage, @@ -47,8 +54,7 @@ export class CloudShell extends BaseManagedIdentitySource { cryptoProvider: CryptoProvider, managedIdentityId: ManagedIdentityId ): CloudShell | null { - const msiEndpoint: string | undefined = - process.env[ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT]; + const [msiEndpoint] = CloudShell.getEnvironmentVariables(); // if the msi endpoint environment variable is undefined, this MSI provider is unavailable. if (!msiEndpoint) { diff --git a/lib/msal-node/src/client/ManagedIdentitySources/ServiceFabric.ts b/lib/msal-node/src/client/ManagedIdentitySources/ServiceFabric.ts index 553d4259e7..c1c157e545 100644 --- a/lib/msal-node/src/client/ManagedIdentitySources/ServiceFabric.ts +++ b/lib/msal-node/src/client/ManagedIdentitySources/ServiceFabric.ts @@ -43,13 +43,7 @@ export class ServiceFabric extends BaseManagedIdentitySource { this.identityHeader = identityHeader; } - public static tryCreate( - logger: Logger, - nodeStorage: NodeStorage, - networkClient: INetworkModule, - cryptoProvider: CryptoProvider, - managedIdentityId: ManagedIdentityId - ): ServiceFabric | null { + public static getEnvironmentVariables(): Array { const identityEndpoint: string | undefined = process.env[ ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT @@ -64,6 +58,19 @@ export class ServiceFabric extends BaseManagedIdentitySource { .IDENTITY_SERVER_THUMBPRINT ]; + return [identityEndpoint, identityHeader, identityServerThumbprint]; + } + + public static tryCreate( + logger: Logger, + nodeStorage: NodeStorage, + networkClient: INetworkModule, + cryptoProvider: CryptoProvider, + managedIdentityId: ManagedIdentityId + ): ServiceFabric | null { + const [identityEndpoint, identityHeader, identityServerThumbprint] = + ServiceFabric.getEnvironmentVariables(); + /* * if either of the identity endpoint, identity header, or identity server thumbprint * environment variables are undefined, this MSI provider is unavailable. diff --git a/lib/msal-node/src/index.ts b/lib/msal-node/src/index.ts index b2ba3251c6..5678805a3e 100644 --- a/lib/msal-node/src/index.ts +++ b/lib/msal-node/src/index.ts @@ -62,6 +62,9 @@ export { } from "./cache/serializer/SerializerTypes.js"; export { DistributedCachePlugin } from "./cache/distributed/DistributedCachePlugin.js"; +// Constants +export { ManagedIdentitySourceNames } from "./utils/Constants.js"; + // Crypto export { CryptoProvider } from "./crypto/CryptoProvider.js"; diff --git a/lib/msal-node/src/utils/Constants.ts b/lib/msal-node/src/utils/Constants.ts index ebb9328c77..eef876c2a0 100644 --- a/lib/msal-node/src/utils/Constants.ts +++ b/lib/msal-node/src/utils/Constants.ts @@ -32,14 +32,20 @@ export type ManagedIdentityEnvironmentVariableNames = /** * Managed Identity Source Names + * @public */ export const ManagedIdentitySourceNames = { - APP_SERVICE: "App Service", - AZURE_ARC: "Azure Arc", - CLOUD_SHELL: "Cloud Shell", - IMDS: "IMDS", - SERVICE_FABRIC: "Service Fabric", + APP_SERVICE: "AppService", + AZURE_ARC: "AzureArc", + CLOUD_SHELL: "CloudShell", + DEFAULT_TO_IMDS: "DefaultToImds", + IMDS: "Imds", + SERVICE_FABRIC: "ServiceFabric", } as const; +/** + * The ManagedIdentitySourceNames type + * @public + */ export type ManagedIdentitySourceNames = (typeof ManagedIdentitySourceNames)[keyof typeof ManagedIdentitySourceNames]; diff --git a/lib/msal-node/test/client/ManagedIdentitySources/AppService.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/AppService.spec.ts index 773a796c03..9fbbdf50ec 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/AppService.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/AppService.spec.ts @@ -11,14 +11,16 @@ import { } from "../../test_kit/StringConstants"; import { - ManagedIdentityTestUtils, userAssignedClientIdConfig, managedIdentityRequestParams, systemAssignedConfig, } from "../../test_kit/ManagedIdentityTestUtils"; import { AuthenticationResult } from "@azure/msal-common"; import { ManagedIdentityClient } from "../../../src/client/ManagedIdentityClient"; -import { ManagedIdentityEnvironmentVariableNames } from "../../../src/utils/Constants"; +import { + ManagedIdentityEnvironmentVariableNames, + ManagedIdentitySourceNames, +} from "../../../src/utils/Constants"; describe("Acquires a token successfully via an App Service Managed Identity", () => { beforeAll(() => { @@ -44,10 +46,11 @@ describe("Acquires a token successfully via an App Service Managed Identity", () }); test("acquires a User Assigned Client Id token", async () => { - expect(ManagedIdentityTestUtils.isAppService()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedClientIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.APP_SERVICE + ); const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( @@ -65,11 +68,12 @@ describe("Acquires a token successfully via an App Service Managed Identity", () managedIdentityApplication = new ManagedIdentityApplication( systemAssignedConfig ); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.APP_SERVICE + ); }); test("acquires a token", async () => { - expect(ManagedIdentityTestUtils.isAppService()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( managedIdentityRequestParams @@ -82,8 +86,6 @@ describe("Acquires a token successfully via an App Service Managed Identity", () }); test("returns an already acquired token from the cache", async () => { - expect(ManagedIdentityTestUtils.isAppService()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, diff --git a/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts index 6fcc448fae..88b477a782 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts @@ -15,7 +15,6 @@ import { } from "../../test_kit/StringConstants"; import { - ManagedIdentityTestUtils, ManagedIdentityNetworkClient, ManagedIdentityNetworkErrorClient, systemAssignedConfig, @@ -30,7 +29,10 @@ import { } from "../../../src/error/ManagedIdentityError"; import { ARC_API_VERSION } from "../../../src/client/ManagedIdentitySources/AzureArc"; import * as fs from "fs"; -import { ManagedIdentityEnvironmentVariableNames } from "../../../src/utils/Constants"; +import { + ManagedIdentityEnvironmentVariableNames, + ManagedIdentitySourceNames, +} from "../../../src/utils/Constants"; jest.mock("fs"); @@ -64,11 +66,12 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = managedIdentityApplication = new ManagedIdentityApplication( systemAssignedConfig ); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.AZURE_ARC + ); }); test("acquires a token", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( managedIdentityRequestParams @@ -81,8 +84,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = }); test("returns an already acquired token from the cache", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, @@ -104,8 +105,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = }); test("attempts to acquire a token, a 401 and www-authenticate header are returned form the azure arc managed identity, then retries the network request with the www-authenticate header", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const networkClient: ManagedIdentityNetworkClient = new ManagedIdentityNetworkClient(MANAGED_IDENTITY_RESOURCE_ID); @@ -116,6 +115,9 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = // managedIdentityIdParams will be omitted for system assigned }, }); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.AZURE_ARC + ); const networkErrorClient: ManagedIdentityNetworkErrorClient = new ManagedIdentityNetworkErrorClient(); @@ -170,10 +172,11 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = describe("Errors", () => { test("throws an error when a user assigned managed identity is used", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedClientIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.AZURE_ARC + ); await expect( managedIdentityApplication.acquireToken( @@ -187,8 +190,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = }); test("throws an error when the www-authenticate header is missing", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication({ system: { @@ -199,6 +200,9 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = // managedIdentityIdParams will be omitted for system assigned }, }); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.AZURE_ARC + ); await expect( managedIdentityApplication.acquireToken( @@ -212,8 +216,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = }); test("throws an error when the www-authenticate header is in an unsupported format", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication({ system: { @@ -223,6 +225,9 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = // managedIdentityIdParams will be omitted for system assigned }, }); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.AZURE_ARC + ); await expect( managedIdentityApplication.acquireToken( @@ -236,8 +241,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = }); test("throws an error when the secret file cannot be found", async () => { - expect(ManagedIdentityTestUtils.isAzureArc()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication({ system: { @@ -247,6 +250,9 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = // managedIdentityIdParams will be omitted for system assigned }, }); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.AZURE_ARC + ); jest.spyOn(fs, "readFileSync").mockImplementationOnce(() => { throw new Error(); diff --git a/lib/msal-node/test/client/ManagedIdentitySources/CloudShell.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/CloudShell.spec.ts index b589aa6a50..4085dd6a1c 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/CloudShell.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/CloudShell.spec.ts @@ -10,14 +10,16 @@ import { } from "../../test_kit/StringConstants"; import { - ManagedIdentityTestUtils, userAssignedClientIdConfig, managedIdentityRequestParams, systemAssignedConfig, } from "../../test_kit/ManagedIdentityTestUtils"; import { AuthenticationResult } from "@azure/msal-common"; import { ManagedIdentityClient } from "../../../src/client/ManagedIdentityClient"; -import { ManagedIdentityEnvironmentVariableNames } from "../../../src/utils/Constants"; +import { + ManagedIdentityEnvironmentVariableNames, + ManagedIdentitySourceNames, +} from "../../../src/utils/Constants"; import { ManagedIdentityErrorCodes, createManagedIdentityError, @@ -47,11 +49,12 @@ describe("Acquires a token successfully via an App Service Managed Identity", () managedIdentityApplication = new ManagedIdentityApplication( systemAssignedConfig ); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.CLOUD_SHELL + ); }); test("acquires a token", async () => { - expect(ManagedIdentityTestUtils.isCloudShell()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( managedIdentityRequestParams @@ -64,8 +67,6 @@ describe("Acquires a token successfully via an App Service Managed Identity", () }); test("returns an already acquired token from the cache", async () => { - expect(ManagedIdentityTestUtils.isCloudShell()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, @@ -89,10 +90,11 @@ describe("Acquires a token successfully via an App Service Managed Identity", () describe("Errors", () => { test("throws an error when a user assigned managed identity is used", async () => { - expect(ManagedIdentityTestUtils.isCloudShell()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedClientIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.CLOUD_SHELL + ); await expect( managedIdentityApplication.acquireToken( diff --git a/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts index e05a62a5ec..c145d89248 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/Imds.spec.ts @@ -18,7 +18,6 @@ import { } from "../../test_kit/StringConstants"; import { - ManagedIdentityTestUtils, ManagedIdentityNetworkClient, ManagedIdentityNetworkErrorClient, networkClient, @@ -26,7 +25,10 @@ import { managedIdentityRequestParams, systemAssignedConfig, } from "../../test_kit/ManagedIdentityTestUtils"; -import { DEFAULT_MANAGED_IDENTITY_ID } from "../../../src/utils/Constants"; +import { + DEFAULT_MANAGED_IDENTITY_ID, + ManagedIdentitySourceNames, +} from "../../../src/utils/Constants"; import { AccessTokenEntity, AuthenticationResult, @@ -83,10 +85,11 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { describe("User Assigned", () => { test("acquires a User Assigned Client Id token", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedClientIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.DEFAULT_TO_IMDS + ); const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( @@ -99,10 +102,11 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("acquires a User Assigned Object Id token", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedObjectIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.DEFAULT_TO_IMDS + ); const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( @@ -115,10 +119,11 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("acquires a User Assigned Resource Id token", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedResourceIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.DEFAULT_TO_IMDS + ); const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( @@ -137,11 +142,12 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { managedIdentityApplication = new ManagedIdentityApplication( systemAssignedConfig ); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.DEFAULT_TO_IMDS + ); }); test("acquires a token", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( managedIdentityRequestParams @@ -154,8 +160,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("returns an already acquired token from the cache", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, @@ -184,11 +188,12 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { managedIdentityApplication = new ManagedIdentityApplication( userAssignedClientIdConfig ); + expect( + managedIdentityApplication.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); }); test("returns a 500 error response from the network request, just the first time", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const sendGetRequestAsyncSpy: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") // override the networkClient's sendGetRequestAsync method to return a 500. @@ -212,8 +217,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("returns a 500 error response from the network request permanently", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const sendGetRequestAsyncSpy: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") // permanently override the networkClient's sendGetRequestAsync method to return a 500 @@ -245,11 +248,12 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { managedIdentityApplication = new ManagedIdentityApplication( systemAssignedConfig ); + expect( + managedIdentityApplication.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); }); test("returns a 500 error response from the network request, just the first time, with no retry-after header", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const sendGetRequestAsyncSpy: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") // override the networkClient's sendGetRequestAsync method to return a 500. @@ -283,8 +287,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("returns a 500 error response from the network request, just the first time, with a retry-after header of 3 seconds", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const headers: Record = { "Retry-After": "3", // 3 seconds }; @@ -323,8 +325,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("returns a 500 error response from the network request, just the first time, with a retry-after header of 3 seconds (extrapolated from an http-date)", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - var retryAfterHttpDate = new Date(); retryAfterHttpDate.setSeconds( retryAfterHttpDate.getSeconds() + 4 // 4 seconds. An extra second has been added to account for this date operation @@ -367,8 +367,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("returns a 500 error response from the network request permanently", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const sendGetRequestAsyncSpy: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") // permanently override the networkClient's sendGetRequestAsync method to return a 500 @@ -394,8 +392,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("makes three acquireToken calls on the same managed identity application (which returns a 500 error response from the network request permanently) to ensure that retry policy lifetime is per request", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const sendGetRequestAsyncSpyApp: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") // permanently override the networkClient's sendGetRequestAsync method to return a 500 @@ -431,8 +427,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }, 15000); // triple the timeout value for this test because there are 3 acquireToken calls (3 x 1 second in between retries) test("ensures that a retry does not happen when the http status code from a failed network response is not included in the retry policy", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const sendGetRequestAsyncSpyApp: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") // permanently override the networkClient's sendGetRequestAsync method to return a 400 @@ -455,8 +449,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("ensures that a retry does not happen when the http status code from a failed network response is included in the retry policy, but the retry policy has been disabled", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const managedIdentityApplicationNoRetry: ManagedIdentityApplication = new ManagedIdentityApplication({ system: { @@ -464,6 +456,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { disableInternalRetries: true, }, }); + expect( + managedIdentityApplicationNoRetry.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); const sendGetRequestAsyncSpy: jest.SpyInstance = jest .spyOn(networkClient, "sendGetRequestAsync") @@ -496,11 +491,12 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { beforeEach(() => { systemAssignedManagedIdentityApplication = new ManagedIdentityApplication(systemAssignedConfig); + expect( + systemAssignedManagedIdentityApplication.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); }); test("acquires a token from the network and then the same token from the cache, then acquires a different token for another scope", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - let networkManagedIdentityResult: AuthenticationResult = await systemAssignedManagedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, @@ -533,8 +529,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("ignores a cached token when forceRefresh is set to true", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - let networkManagedIdentityResult: AuthenticationResult = await systemAssignedManagedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, @@ -567,8 +561,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("proactively refreshes a token in the background when its refresh_in value is expired.", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - let networkManagedIdentityResult: AuthenticationResult = await systemAssignedManagedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, @@ -645,8 +637,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }, 10000); // double the timeout value for this test because it waits two seconds in between the acquireToken call and the cache lookup test("requests three tokens with two different resources while switching between user and system assigned, then requests them again to verify they are retrieved from the cache, then verifies that their cache keys are correct", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - // the imported systemAssignedManagedIdentityApplication is the default System Assigned Managed Identity Application. // for reference, in this case it is equivalent to systemAssignedManagedIdentityApplicationResource1 @@ -659,6 +649,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { userAssignedClientId: MANAGED_IDENTITY_RESOURCE_ID, }, }); + expect( + userAssignedClientIdManagedIdentityApplicationResource1.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); const userAssignedObjectIdManagedIdentityApplicationResource2: ManagedIdentityApplication = new ManagedIdentityApplication({ @@ -671,6 +664,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { userAssignedObjectId: MANAGED_IDENTITY_RESOURCE_ID_2, }, }); + expect( + userAssignedObjectIdManagedIdentityApplicationResource2.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); // ********** begin: return access tokens from a network request ********** // resource R1 for system assigned - returned from a network request @@ -707,6 +703,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { // resource R1 for system assigned - new application (to prove static cache persists), but same request as before, returned from the cache this time const systemAssignedManagedIdentityApplicationClone: ManagedIdentityApplication = new ManagedIdentityApplication(systemAssignedConfig); + expect( + systemAssignedManagedIdentityApplicationClone.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); let cachedManagedIdentityResult: AuthenticationResult = await systemAssignedManagedIdentityApplicationClone.acquireToken( { @@ -725,6 +724,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { userAssignedClientId: MANAGED_IDENTITY_RESOURCE_ID, }, }); + expect( + userAssignedClientIdManagedIdentityApplicationResource1Clone.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); cachedManagedIdentityResult = await userAssignedClientIdManagedIdentityApplicationResource1Clone.acquireToken( { @@ -745,6 +747,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { userAssignedObjectId: MANAGED_IDENTITY_RESOURCE_ID_2, }, }); + expect( + userAssignedObjectIdManagedIdentityApplicationResource2Clone.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); cachedManagedIdentityResult = await userAssignedObjectIdManagedIdentityApplicationResource2Clone.acquireToken( { @@ -780,10 +785,11 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { describe("Errors", () => { test("throws an error when an invalid resource is provided", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const systemAssignedManagedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(systemAssignedConfig); + expect( + systemAssignedManagedIdentityApplication.getManagedIdentitySource() + ).toBe(ManagedIdentitySourceNames.DEFAULT_TO_IMDS); await expect( systemAssignedManagedIdentityApplication.acquireToken({ @@ -797,8 +803,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("throws an error when more than one managed identity type is provided", () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const badUserAssignedClientIdConfig: ManagedIdentityConfiguration = { system: { @@ -820,8 +824,6 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { }); test("managed identity token response contains an error message and correlation id when an error is returned from the managed identity", async () => { - expect(ManagedIdentityTestUtils.isIMDS()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication({ system: { @@ -829,6 +831,9 @@ describe("Acquires a token successfully via an IMDS Managed Identity", () => { // managedIdentityIdParams will be omitted for system assigned }, }); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.DEFAULT_TO_IMDS + ); let serverError: ServerError = new ServerError(); try { diff --git a/lib/msal-node/test/client/ManagedIdentitySources/ServiceFabric.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/ServiceFabric.spec.ts index 0be67d8848..81e5bccb28 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/ServiceFabric.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/ServiceFabric.spec.ts @@ -11,14 +11,16 @@ import { } from "../../test_kit/StringConstants"; import { - ManagedIdentityTestUtils, userAssignedClientIdConfig, managedIdentityRequestParams, systemAssignedConfig, } from "../../test_kit/ManagedIdentityTestUtils"; import { AuthenticationResult } from "@azure/msal-common"; import { ManagedIdentityClient } from "../../../src/client/ManagedIdentityClient"; -import { ManagedIdentityEnvironmentVariableNames } from "../../../src/utils/Constants"; +import { + ManagedIdentityEnvironmentVariableNames, + ManagedIdentitySourceNames, +} from "../../../src/utils/Constants"; describe("Acquires a token successfully via an App Service Managed Identity", () => { beforeAll(() => { @@ -50,10 +52,11 @@ describe("Acquires a token successfully via an App Service Managed Identity", () }); test("acquires a User Assigned Client Id token", async () => { - expect(ManagedIdentityTestUtils.isServiceFabric()).toBe(true); - const managedIdentityApplication: ManagedIdentityApplication = new ManagedIdentityApplication(userAssignedClientIdConfig); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.SERVICE_FABRIC + ); const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( @@ -71,11 +74,12 @@ describe("Acquires a token successfully via an App Service Managed Identity", () managedIdentityApplication = new ManagedIdentityApplication( systemAssignedConfig ); + expect(managedIdentityApplication.getManagedIdentitySource()).toBe( + ManagedIdentitySourceNames.SERVICE_FABRIC + ); }); test("acquires a token", async () => { - expect(ManagedIdentityTestUtils.isServiceFabric()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken( managedIdentityRequestParams @@ -88,8 +92,6 @@ describe("Acquires a token successfully via an App Service Managed Identity", () }); test("returns an already acquired token from the cache", async () => { - expect(ManagedIdentityTestUtils.isServiceFabric()).toBe(true); - const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken({ resource: MANAGED_IDENTITY_RESOURCE, diff --git a/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts b/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts index dceea72efb..85ed8fb302 100644 --- a/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts +++ b/lib/msal-node/test/test_kit/ManagedIdentityTestUtils.ts @@ -18,7 +18,6 @@ import { TEST_TOKENS, TEST_TOKEN_LIFETIMES, } from "./StringConstants"; -import { ManagedIdentityEnvironmentVariableNames } from "../../src/utils/Constants"; import { ManagedIdentityTokenResponse } from "../../src/response/ManagedIdentityTokenResponse"; import { ManagedIdentityRequestParams } from "../../src"; import { ManagedIdentityConfiguration } from "../../src/config/Configuration"; @@ -26,62 +25,6 @@ import { mockAuthenticationResult } from "../utils/TestConstants"; const EMPTY_HEADERS: Record = {}; -export class ManagedIdentityTestUtils { - public static isAppService(): boolean { - return ( - // !! converts to boolean - !!process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT - ] && - !!process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER - ] - ); - } - - public static isAzureArc(): boolean { - return ( - // !! converts to boolean - !!process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT - ] && - !!process.env[ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT] - ); - } - - public static isCloudShell(): boolean { - return ( - // !! converts to boolean - !!process.env[ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT] - ); - } - - public static isIMDS(): boolean { - return ( - !ManagedIdentityTestUtils.isAppService() && - !ManagedIdentityTestUtils.isAzureArc() && - !ManagedIdentityTestUtils.isCloudShell() && - !ManagedIdentityTestUtils.isServiceFabric() - ); - } - - public static isServiceFabric(): boolean { - return ( - // !! converts to boolean - !!process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT - ] && - !!process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER - ] && - !!process.env[ - ManagedIdentityEnvironmentVariableNames - .IDENTITY_SERVER_THUMBPRINT - ] - ); - } -} - export class ManagedIdentityNetworkClient implements INetworkModule { private clientId: string; private resource: string | undefined;