Add WithMtlsBearerToken() API and E2E test: Get attested ****** via IMDSv2 mTLS flow#6086
Add WithMtlsBearerToken() API and E2E test: Get attested ****** via IMDSv2 mTLS flow#6086Copilot wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an end-to-end regression test in the Managed Identity IMDSv2 E2E suite to validate that when .WithMtlsProofOfPossession() is omitted (even if .WithAttestationSupport() is present), MSAL returns a standard Bearer token (no binding certificate) on an IMDSv2-capable host.
Changes:
- Added a new
[DataRow]-driven E2E test covering SAMI and UAMI (clientId) scenarios for the “no mTLS PoP requested” path. - Asserts Bearer token shape (
TokenType == "Bearer",BindingCertificate == null) and that the token was acquired from the identity provider.
| Assert.IsFalse(string.IsNullOrEmpty(result.AccessToken), "AccessToken should not be empty."); | ||
| Assert.AreEqual("Bearer", result.TokenType, "Token type should be 'Bearer' when mTLS PoP is not requested."); | ||
| Assert.IsNull(result.BindingCertificate, "BindingCertificate should be null for a Bearer token."); | ||
|
|
|
We will need a new Bearer API to support this e2e |
| [TestMethod] | ||
| [DataRow(null /*SAMI*/, null, DisplayName = "AcquireToken_OnImdsV2_WithAttestation_NoMtlsPoP_ReturnsBearer-SAMI")] | ||
| [DataRow(UamiClientId, "clientid", DisplayName = "AcquireToken_OnImdsV2_WithAttestation_NoMtlsPoP_ReturnsBearer-UAMI-ClientId")] | ||
| public async Task AcquireToken_OnImdsV2_WithAttestation_NoMtlsPoP_ReturnsBearer(string id, string idType) |
There was a problem hiding this comment.
@copilot This adds an E2E test covering the IMDSv1 path on an IMDSv2-capable host: when .WithMtlsProofOfPossession() is not called, MSAL routes the request through IMDSv1 regardless of .WithAttestationSupport(), returning a bearer token with no binding certificate.
But, to get a Bearer token .WithAttestationSupport(), we may need a new let's say name not final - WithMtlsBearer() - think of a good name. If we added this, then we should use the Attested Flow in IMDSv2-capable host and get a bearer token. We just need to set the token type to bearer when we call ESTS finally with the cert over mTLS. Can you add the new api and update this test to return a bearer token using attestation?
There was a problem hiding this comment.
Added WithMtlsBearerToken() in commit 9c06b52.
The new API follows the same IMDSv2 attested flow as WithMtlsProofOfPossession() — it obtains a Credential Guard–issued certificate, connects to ESTS over mTLS — but sends token_type=bearer instead of token_type=mtls_pop, returning a plain bearer token with no binding certificate.
Key changes:
WithMtlsBearerToken()extension added toManagedIdentityPopExtensions.cs(Windows-only, same as the PoP variant)IsMtlsBearerRequestedflag plumbed from the builder through parameters, routing, andImdsV2ManagedIdentitySource.CreateRequestAsync- Routing in
ManagedIdentityClienttreatsIsMtlsBearerRequestedthe same asIsMtlsPopRequestedfor IMDSv2 source selection - The KeyGuard check in
ImdsV2ManagedIdentitySourcenow covers both PoP and ****** (unified error codecredential_guard_not_available) - mTLS-bearer tokens get their own cache partition (
mtls_bearerkey component) to keep them separate from both PoP tokens and plain IMDSv1 bearer tokens - E2E test updated to call
.WithMtlsBearerToken().WithAttestationSupport()and assertsTokenType == "Bearer",BindingCertificate == null, and absence of thecnfclaim in the JWT payload
|
@copilot resolve conflicts |
… IsMtlsBearerRequested and MtlsPopMinStrength
…ng in delegation path
Conflicts resolved in commit
|
Adds a new public API
WithMtlsBearerToken()that uses the full IMDSv2 attested mTLS flow (Credential Guard–issued certificate, mTLS connection to ESTS) but requeststoken_type=bearer, returning a standard bearer token with no binding certificate. Also adds the corresponding E2E test.Changes proposed in this request
WithMtlsBearerToken()extension method toManagedIdentityPopExtensions.cs(Windows-only, mirrorsWithMtlsProofOfPossession()but sendstoken_type=bearerto ESTS instead oftoken_type=mtls_pop)IsMtlsBearerRequestedflag throughAcquireTokenCommonParameters,AcquireTokenForManagedIdentityParameters,AuthenticationRequestParameters,AcquireTokenForManagedIdentityParameterBuilder,ManagedIdentityAuthRequest, andAbstractManagedIdentityManagedIdentityClientto routeIsMtlsBearerRequestedto IMDSv2 (same asIsMtlsPopRequested)ImdsV2ManagedIdentitySource.CreateRequestAsyncto cover both PoP and ****** under a unifiedcredential_guard_not_availableerror codemtls_bearerkey component) to keep them distinct from PoP tokens and plain IMDSv1 bearer tokensPublicAPI.Unshipped.txtfor all target frameworksAcquireToken_OnImdsV2_WithAttestation_NoMtlsPoP_ReturnsBearerE2E test withAcquireToken_OnImdsV2_WithMtlsBearerToken_ReturnsBearer, which calls.WithMtlsBearerToken().WithAttestationSupport()and assertsTokenType == "Bearer",BindingCertificate == null, absence of thecnfclaim in the JWT payload, andTokenSource == IdentityProvidercredential_guard_not_availableTesting
Updated E2E test uses
.WithMtlsBearerToken()and runs on the MSALMSIV2 pool (Credential Guard / VBS enabled). Gracefully marks inconclusive if Credential Guard is unavailable. Unit test for the KeyGuard-not-available error path updated to match the new unified error code.Performance impact
None.
Documentation