Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -52,30 +52,34 @@ private async Task<AbstractManagedIdentity> GetOrSelectManagedIdentitySourceAsyn
{
requestContext.Logger.Info($"[Managed Identity] Selecting managed identity source if not cached. Cached value is {s_sourceName} ");

var source = ManagedIdentitySource.None;
ManagedIdentitySource source;

// If the source is not already set, determine it
if (s_sourceName == ManagedIdentitySource.None)
{
// First invocation: detect and cache
source = await GetManagedIdentitySourceAsync(requestContext).ConfigureAwait(false);
}
// If the source has already been set to ImdsV2 (via this method, or GetManagedIdentitySourceAsync in ManagedIdentityApplication.cs) and mTLS PoP was NOT requested
else
{
// Reuse cached value
source = s_sourceName;
}

// If the source has already been set to ImdsV2 (via this method,
// or GetManagedIdentitySourceAsync in ManagedIdentityApplication.cs) and mTLS PoP was NOT requested
// In this case, we need to fall back to ImdsV1, because ImdsV2 currently only supports mTLS PoP requests
else if ((s_sourceName == ManagedIdentitySource.ImdsV2) && !isMtlsPopRequested)
if (source == ManagedIdentitySource.ImdsV2 && !isMtlsPopRequested)
{
requestContext.Logger.Info("[Managed Identity] ImdsV2 detected, but mTLS PoP was not requested. Falling back to ImdsV1 for this request only. Please use the \"WithMtlsProofOfPossession\" API to request a token via ImdsV2.");

// keep the cached source (s_sourceName) as ImdsV2, since the developer may decide to use mTLS PoP in subsequent requests

// Do NOT modify s_sourceName; keep cached ImdsV2 so future PoP
// requests can leverage it.
source = ManagedIdentitySource.DefaultToImds;
}
else
{
source = s_sourceName;
}

// If the source is determined to be ImdsV1 and mTLS PoP was requested, throw an exception since ImdsV1 does not support mTLS PoP
if ((source == ManagedIdentitySource.DefaultToImds) && isMtlsPopRequested)
// If the source is determined to be ImdsV1 and mTLS PoP was requested,
// throw an exception since ImdsV1 does not support mTLS PoP
if (source == ManagedIdentitySource.DefaultToImds && isMtlsPopRequested)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be elif? You want both if statements to run?

{
throw new MsalClientException(
MsalError.MtlsPopTokenNotSupportedinImdsV1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1462,5 +1462,42 @@ private static void AssertCertSubjectCnDc(X509Certificate2 cert, string expected
}

#endregion

#region Fallback Behavior Tests
// Verifies non-mTLS request after IMDSv2 detection falls back per-request to IMDSv1 (Bearer),
[DataTestMethod]
[DataRow(UserAssignedIdentityId.None, null)]
[DataRow(UserAssignedIdentityId.ClientId, TestConstants.ClientId)]
Comment on lines +1468 to +1470
Copy link
Contributor

Choose a reason for hiding this comment

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

To be consistent with all other test, can you also add cases for ResourceId and ObjectId?

public async Task NonMtlsRequest_FallbackToImdsV1(
UserAssignedIdentityId idKind,
string idValue)
{
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
ManagedIdentityClient.ResetSourceForTest();
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we have something that runs before each test? Shouldn't this be put there?

SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);

var mi = await CreateManagedIdentityAsync(httpManager, idKind, idValue, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);

// Fallback token (Bearer) mock
httpManager.AddManagedIdentityMockHandler(
ManagedIdentityTests.ImdsEndpoint,
ManagedIdentityTests.Resource,
MockHelpers.GetMsiSuccessfulResponse(),
ManagedIdentitySource.Imds,
userAssignedIdentityId: idKind,
userAssignedId: idValue);

var token = await mi.AcquireTokenForManagedIdentity(ManagedIdentityTests.Resource)
// No .WithMtlsProofOfPossession() => triggers fallback
.ExecuteAsync().ConfigureAwait(false);

Assert.AreEqual(Bearer, token.TokenType);
Assert.IsNull(token.BindingCertificate, "Bearer token should not have binding certificate.");
Assert.AreEqual(TokenSource.IdentityProvider, token.AuthenticationResultMetadata.TokenSource);
}
}
#endregion
}
}