Skip to content

Commit 2d65ccd

Browse files
authored
Bearer Requests should Fallback to IMDS in Preview (#5562)
* fallback to IMDS in PROD * comment updates
1 parent 3f5ed6a commit 2d65ccd

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityClient.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,30 +52,34 @@ private async Task<AbstractManagedIdentity> GetOrSelectManagedIdentitySourceAsyn
5252
{
5353
requestContext.Logger.Info($"[Managed Identity] Selecting managed identity source if not cached. Cached value is {s_sourceName} ");
5454

55-
var source = ManagedIdentitySource.None;
55+
ManagedIdentitySource source;
5656

5757
// If the source is not already set, determine it
5858
if (s_sourceName == ManagedIdentitySource.None)
5959
{
60+
// First invocation: detect and cache
6061
source = await GetManagedIdentitySourceAsync(requestContext).ConfigureAwait(false);
6162
}
62-
// If the source has already been set to ImdsV2 (via this method, or GetManagedIdentitySourceAsync in ManagedIdentityApplication.cs) and mTLS PoP was NOT requested
63+
else
64+
{
65+
// Reuse cached value
66+
source = s_sourceName;
67+
}
68+
69+
// If the source has already been set to ImdsV2 (via this method,
70+
// or GetManagedIdentitySourceAsync in ManagedIdentityApplication.cs) and mTLS PoP was NOT requested
6371
// In this case, we need to fall back to ImdsV1, because ImdsV2 currently only supports mTLS PoP requests
64-
else if ((s_sourceName == ManagedIdentitySource.ImdsV2) && !isMtlsPopRequested)
72+
if (source == ManagedIdentitySource.ImdsV2 && !isMtlsPopRequested)
6573
{
6674
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.");
67-
68-
// keep the cached source (s_sourceName) as ImdsV2, since the developer may decide to use mTLS PoP in subsequent requests
69-
75+
// Do NOT modify s_sourceName; keep cached ImdsV2 so future PoP
76+
// requests can leverage it.
7077
source = ManagedIdentitySource.DefaultToImds;
7178
}
72-
else
73-
{
74-
source = s_sourceName;
75-
}
7679

77-
// If the source is determined to be ImdsV1 and mTLS PoP was requested, throw an exception since ImdsV1 does not support mTLS PoP
78-
if ((source == ManagedIdentitySource.DefaultToImds) && isMtlsPopRequested)
80+
// If the source is determined to be ImdsV1 and mTLS PoP was requested,
81+
// throw an exception since ImdsV1 does not support mTLS PoP
82+
if (source == ManagedIdentitySource.DefaultToImds && isMtlsPopRequested)
7983
{
8084
throw new MsalClientException(
8185
MsalError.MtlsPopTokenNotSupportedinImdsV1,

tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,5 +1462,42 @@ private static void AssertCertSubjectCnDc(X509Certificate2 cert, string expected
14621462
}
14631463

14641464
#endregion
1465+
1466+
#region Fallback Behavior Tests
1467+
// Verifies non-mTLS request after IMDSv2 detection falls back per-request to IMDSv1 (Bearer),
1468+
[DataTestMethod]
1469+
[DataRow(UserAssignedIdentityId.None, null)]
1470+
[DataRow(UserAssignedIdentityId.ClientId, TestConstants.ClientId)]
1471+
public async Task NonMtlsRequest_FallbackToImdsV1(
1472+
UserAssignedIdentityId idKind,
1473+
string idValue)
1474+
{
1475+
using (new EnvVariableContext())
1476+
using (var httpManager = new MockHttpManager())
1477+
{
1478+
ManagedIdentityClient.ResetSourceForTest();
1479+
SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
1480+
1481+
var mi = await CreateManagedIdentityAsync(httpManager, idKind, idValue, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
1482+
1483+
// Fallback token (Bearer) mock
1484+
httpManager.AddManagedIdentityMockHandler(
1485+
ManagedIdentityTests.ImdsEndpoint,
1486+
ManagedIdentityTests.Resource,
1487+
MockHelpers.GetMsiSuccessfulResponse(),
1488+
ManagedIdentitySource.Imds,
1489+
userAssignedIdentityId: idKind,
1490+
userAssignedId: idValue);
1491+
1492+
var token = await mi.AcquireTokenForManagedIdentity(ManagedIdentityTests.Resource)
1493+
// No .WithMtlsProofOfPossession() => triggers fallback
1494+
.ExecuteAsync().ConfigureAwait(false);
1495+
1496+
Assert.AreEqual(Bearer, token.TokenType);
1497+
Assert.IsNull(token.BindingCertificate, "Bearer token should not have binding certificate.");
1498+
Assert.AreEqual(TokenSource.IdentityProvider, token.AuthenticationResultMetadata.TokenSource);
1499+
}
1500+
}
1501+
#endregion
14651502
}
14661503
}

0 commit comments

Comments
 (0)