Skip to content

Added token revocation functionality to Managed Identity's App Service source #7772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
920bac3
Initial Implementation
Robbie-Microsoft Apr 2, 2025
73ffd22
Change files
Robbie-Microsoft Apr 2, 2025
f4f002a
Improvements
Robbie-Microsoft Apr 2, 2025
468de01
Improvements
Robbie-Microsoft Apr 2, 2025
dcd382f
Added unit tests
Robbie-Microsoft Apr 3, 2025
d8fbbca
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft Apr 3, 2025
b09a464
Implemented some github feedback
Robbie-Microsoft Apr 3, 2025
0f7b2b9
Implemented github feedback
Robbie-Microsoft Apr 3, 2025
b32f45e
package-lock
Robbie-Microsoft Apr 3, 2025
604194d
Defined sha256 hash for test access token
Robbie-Microsoft Apr 4, 2025
3405bce
Switched to hex encoding, instead of base64, for the sha256 hash
Robbie-Microsoft Apr 7, 2025
f1d096f
Change files
Robbie-Microsoft Apr 7, 2025
203d5d0
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft Apr 7, 2025
d9f15e6
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft Apr 17, 2025
0832c02
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft Apr 22, 2025
753ec2b
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft May 2, 2025
aa2a42d
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft May 19, 2025
0f15894
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft May 21, 2025
b48caf9
Implemented Neha's feedback
Robbie-Microsoft May 21, 2025
865697d
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft May 21, 2025
2295235
Removed app service
Robbie-Microsoft May 21, 2025
3990385
added token revocation to app service
Robbie-Microsoft May 21, 2025
c34e1e4
undid code deletion
Robbie-Microsoft May 21, 2025
99cf38e
Merge branch 'msi_v1_token_revocation' into app_service_token_revocation
Robbie-Microsoft May 21, 2025
f9b13fb
Added back happy path test
Robbie-Microsoft May 22, 2025
9f1f862
Merge branch 'msi_v1_token_revocation' into app_service_token_revocation
Robbie-Microsoft May 22, 2025
e7e0af7
added additional unit test
Robbie-Microsoft May 22, 2025
4a59853
typos
Robbie-Microsoft May 22, 2025
a3434fa
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft May 22, 2025
bdc6841
Merge branch 'msi_v1_token_revocation' into app_service_token_revocation
Robbie-Microsoft May 22, 2025
77c94d0
fixed incorrect comment
Robbie-Microsoft May 23, 2025
b29be2f
Merge branch 'dev' into msi_v1_token_revocation
Robbie-Microsoft May 27, 2025
4bd2045
Merge branch 'msi_v1_token_revocation' into app_service_token_revocation
Robbie-Microsoft May 27, 2025
56c1103
Improved unit test
Robbie-Microsoft May 27, 2025
656bbe3
Change files
Robbie-Microsoft May 27, 2025
5bab9ed
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft May 28, 2025
760e7b7
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft May 28, 2025
fe1a1ca
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft May 28, 2025
73ea7a8
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft Jun 4, 2025
c635d40
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft Jun 5, 2025
58fb4a3
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft Jun 12, 2025
2fda006
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft Jun 17, 2025
599c576
Merge branch 'dev' into app_service_token_revocation
Robbie-Microsoft Jun 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Added token revocation functionality to Managed Identity's App Service source #7772",
"packageName": "@azure/msal-node",
"email": "[email protected]",
"dependentChangeType": "patch"
}
5 changes: 4 additions & 1 deletion lib/msal-node/src/client/ManagedIdentityApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ import { ManagedIdentityId } from "../config/ManagedIdentityId.js";
import { HashUtils } from "../crypto/HashUtils.js";

const SOURCES_THAT_SUPPORT_TOKEN_REVOCATION: Array<ManagedIdentitySourceNames> =
[ManagedIdentitySourceNames.SERVICE_FABRIC];
[
ManagedIdentitySourceNames.APP_SERVICE,
ManagedIdentitySourceNames.SERVICE_FABRIC,
];

/**
* Class to initialize a managed identity and identify the service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ManagedIdentityId } from "../../config/ManagedIdentityId.js";
import { NodeStorage } from "../../cache/NodeStorage.js";

// MSI Constants. Docs for MSI are available here https://docs.microsoft.com/azure/app-service/overview-managed-identity
const APP_SERVICE_MSI_API_VERSION: string = "2019-08-01";
const APP_SERVICE_MSI_API_VERSION: string = "2025-03-30";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't merge this until you get green light from @gladjohn , who is in contact with App Service.


/**
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/AppServiceManagedIdentitySource.cs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@

import { ManagedIdentityApplication } from "../../../src/client/ManagedIdentityApplication.js";
import {
CAE_CONSTANTS,
DEFAULT_MANAGED_IDENTITY_AUTHENTICATION_RESULT_ACCESS_TOKEN_SHA256_HASH_IN_HEX,
DEFAULT_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT,
DEFAULT_USER_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT,
MANAGED_IDENTITY_APP_SERVICE_NETWORK_REQUEST_400_ERROR,
MANAGED_IDENTITY_RESOURCE,
MANAGED_IDENTITY_RESOURCE_ID,
TEST_CONFIG,
} from "../../test_kit/StringConstants.js";

import {
Expand All @@ -28,6 +31,7 @@ import {
import { ManagedIdentityClient } from "../../../src/client/ManagedIdentityClient.js";
import {
ManagedIdentityEnvironmentVariableNames,
ManagedIdentityQueryParameters,
ManagedIdentitySourceNames,
} from "../../../src/utils/Constants.js";
import { ManagedIdentityUserAssignedIdQueryParameterNames } from "../../../src/client/ManagedIdentitySources/BaseManagedIdentitySource.js";
Expand Down Expand Up @@ -176,6 +180,86 @@ describe("Acquires a token successfully via an App Service Managed Identity", ()
});
});

describe("Miscellaneous", () => {
it.each([
[
CAE_CONSTANTS.CLIENT_CAPABILITIES,
CAE_CONSTANTS.CLIENT_CAPABILITIES.toString(),
],
[undefined, null],
])(
"ignores a cached token when claims are provided (regardless of if client capabilities are provided or not) and the Managed Identity does support token revocation, and ensures the token revocation query parameter token_sha256_to_refresh is included in the network request to the Managed Identity",
async (providedCapabilities, capabilitiesOnNetworkRequest) => {
const sendGetRequestAsyncSpy: jest.SpyInstance = jest.spyOn(
networkClient,
<any>"sendGetRequestAsync"
);

const managedIdentityApplication: ManagedIdentityApplication =
new ManagedIdentityApplication({
...systemAssignedConfig,
clientCapabilities: providedCapabilities,
});
expect(
managedIdentityApplication.getManagedIdentitySource()
).toBe(ManagedIdentitySourceNames.APP_SERVICE);

let networkManagedIdentityResult: AuthenticationResult =
await managedIdentityApplication.acquireToken({
resource: MANAGED_IDENTITY_RESOURCE,
});
expect(networkManagedIdentityResult.fromCache).toBe(false);
expect(networkManagedIdentityResult.accessToken).toEqual(
DEFAULT_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT.accessToken
);

expect(sendGetRequestAsyncSpy.mock.calls.length).toEqual(1);
const firstNetworkRequestUrlParams: URLSearchParams =
new URLSearchParams(
sendGetRequestAsyncSpy.mock.lastCall[0]
);
expect(
firstNetworkRequestUrlParams.get(
ManagedIdentityQueryParameters.XMS_CC
)
).toEqual(capabilitiesOnNetworkRequest);

const cachedManagedIdentityResult: AuthenticationResult =
await managedIdentityApplication.acquireToken({
resource: MANAGED_IDENTITY_RESOURCE,
});
expect(cachedManagedIdentityResult.fromCache).toBe(true);
expect(cachedManagedIdentityResult.accessToken).toEqual(
DEFAULT_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT.accessToken
);
expect(sendGetRequestAsyncSpy.mock.calls.length).toEqual(1);

networkManagedIdentityResult =
await managedIdentityApplication.acquireToken({
claims: TEST_CONFIG.CLAIMS,
resource: MANAGED_IDENTITY_RESOURCE,
});
expect(networkManagedIdentityResult.fromCache).toBe(false);
expect(networkManagedIdentityResult.accessToken).toEqual(
DEFAULT_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT.accessToken
);

expect(sendGetRequestAsyncSpy.mock.calls.length).toEqual(2);
const secondNetworkRequestUrlParams: URLSearchParams =
new URLSearchParams(
sendGetRequestAsyncSpy.mock.lastCall[0]
);
expect(
secondNetworkRequestUrlParams.get(
ManagedIdentityQueryParameters.SHA256_TOKEN_TO_REFRESH
)
).toEqual(
DEFAULT_MANAGED_IDENTITY_AUTHENTICATION_RESULT_ACCESS_TOKEN_SHA256_HASH_IN_HEX
);
}
);
});

describe("Errors", () => {
test("ensures that the error format is correct", async () => {
const managedIdentityNetworkErrorClient400 =
Expand Down