Spike #6042: delegate IMDSv2 token leg to internal TokenClient exchange#6062
Closed
Robbie-Microsoft wants to merge 2 commits into
Closed
Spike #6042: delegate IMDSv2 token leg to internal TokenClient exchange#6062Robbie-Microsoft wants to merge 2 commits into
Robbie-Microsoft wants to merge 2 commits into
Conversation
Phase 1 of spike #6042. Documents delegating the MSI v2 post-mint token leg to MSAL's internal ClientCredentialRequest/TokenClient exchange path (not a public ConfidentialClientApplication), to unblock MSIv2 claims/NSP support (#5982). Captures current-state map, the tokenEndpointOverride seam, the invalid_client remint wrapper, risks, and the lines-removed-vs-added measurement methodology. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 2 throwaway PoC for spike #6042. Behind a default-off flag (s_delegateTokenLegToInternalExchange), delegates the MSI v2 post-mint token request to MSAL's internal TokenClient exchange path while keeping the cert-mint flow unchanged. Injects the IMDS-minted cert, targets the ESTS-R endpoint via tokenEndpointOverride (no instance discovery), and wraps an invalid_client re-mint-and-retry-once. Adds mTLSPop_DelegatedTokenLeg_HappyPath_Spike6042; all 424 ManagedIdentity tests pass. Records the lines-removed-vs-added measurement in the design doc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This spike explores delegating the IMDSv2 (Managed Identity v2) post-mint token exchange to MSAL’s internal TokenClient path (instead of the bespoke managed-identity token POST), gated behind a default-off flag for experimentation and measurement tied to #6042.
Changes:
- Added a default-off flag and a delegation branch in
ManagedIdentityAuthRequestthat mints an IMDSv2 binding certificate and callsTokenClient.SendTokenRequestAsync(..., tokenEndpointOverride: ...), with a one-timeinvalid_clientre-mint retry. - Refactored IMDSv2 cert minting into a reusable helper (
AcquireMtlsBindingAsync) and introduced a mint-only delegation entry point. - Added a unit test proving the delegated happy path + caching behavior, and added a design/measurement doc describing the approach and tradeoffs.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs | Adds a spike test covering delegated token-leg happy path and cache hit behavior. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs | Extracts cert-mint into a helper and adds a mint-only entrypoint used by the delegated path. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityClient.cs | Adds an internal helper to acquire the IMDSv2 mTLS binding without sending the bespoke token request. |
| src/client/Microsoft.Identity.Client/Internal/Requests/ManagedIdentityAuthRequest.cs | Adds the flag-gated delegated token-leg branch and invalid_client re-mint retry wrapper. |
| docs/imdsv2_cca_delegation_spike.md | Documents current state, delegation seam, risks, and measurement for the spike. |
Comment on lines
+253
to
+270
| // Cert-mint mocks (unchanged path) + delegated ESTS-R token mock. | ||
| httpManager.AddMockHandler(MockHelpers.MockCsrResponse()); | ||
| httpManager.AddMockHandler(MockHelpers.MockCertificateRequestResponse()); | ||
| httpManager.AddMockHandler(CreateDelegatedEntraTokenMock()); | ||
|
|
||
| // Act | ||
| var result = await managedIdentityApp.AcquireTokenForManagedIdentity(ManagedIdentityTests.Resource) | ||
| .WithMtlsProofOfPossession() | ||
| .WithAttestationSupport() | ||
| .ExecuteAsync().ConfigureAwait(false); | ||
|
|
||
| // Assert - token acquired from the delegated exchange path and cert-bound. | ||
| Assert.IsNotNull(result); | ||
| Assert.IsNotNull(result.AccessToken); | ||
| Assert.AreEqual(MTLSPoP, result.TokenType); | ||
| Assert.IsNotNull(result.BindingCertificate); | ||
| Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); | ||
|
|
Comment on lines
+283
to
+285
| catch (MsalServiceException ex) when ( | ||
| string.Equals(ex.ErrorCode, "invalid_client", StringComparison.OrdinalIgnoreCase)) | ||
| { |
Contributor
Author
|
Implemented in #6070 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spike #6042 — IMDSv2: delegate the token leg to MSAL's internal exchange path
This is a throwaway spike PR for #6042. All production behavior is gated behind a default-off flag (
s_delegateTokenLegToInternalExchange); it is not intended to merge as-is.What & why
Per the discussion on #6042, this validates replacing IMDSv2's hand-rolled post-mint token POST with delegation to the internal
TokenClientexchange. The driver is unblocking MSIv2 claims/NSP support (#5982), so claims→body, CP1 merge, claims-in-cache-key, and ESTS error handling are inherited rather than re-ported.Deliverables
docs/imdsv2_cca_delegation_spike.md(current-state map, the delegation seam, two integration depths — recommends option A, feasibility Q&A, risks, measurement).AcquireMtlsBindingAsync(behavior-neutral); added a mint-only delegation entrypoint +invalid_clientcert eviction.ManagedIdentityClient.AcquireImdsV2MtlsBindingAsyncmints without the bespoke POST.ManagedIdentityAuthRequestdelegation branch →new TokenClient(reqParams).SendTokenRequestAsync(body, scopeOverride, tokenEndpointOverride)with the minted cert +mtls_popscheme, wrapped ininvalid_clientre-mint-and-retry-once.Feasibility: confirmed — no
TokenClientchange neededtokenEndpointOverridehits ESTS-R with zero instance discovery; canonicalclient_idoverridesAppConfig.ClientIdvia body params; the minted cert flows as the mTLS transport cert; MI caching is preserved (lookup runs upstream, branch only fires on cache miss).Validation
mTLSPop_DelegatedTokenLeg_HappyPath_Spike6042passes (IdP then Cache, cert-bound).Measurement (the issue''s ask)
+163/−24 across 3 prod files: ~50 moved (refactor), ~95 net-new glue, vs ~26 bespoke token-path lines bypassed. Happy-path line count is ~net-neutral, but the qualitative win is inheriting the shared exchange machinery for MSIv2 NSP (#5982).
Spike only — tracking issue: #6042.