diff --git a/CHANGELOG.md b/CHANGELOG.md
index 17a217cc10..4a8de3ec48 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
======
### New Features
+- Added `ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(CancellationToken)` returning a `ManagedIdentityCapabilities` object that reports the detected managed identity `Source`, the host's `MaxSupportedBindingStrength` (new `MtlsBindingStrength` enum: `None`, `Software`, `KeyGuard`), and a derived `IsMtlsPopSupportedByHost`. Replaces `GetManagedIdentitySourceAsync()`/`ManagedIdentitySourceResult`. The public `ManagedIdentitySource.ImdsV2` value is folded into `Imds` (v1/v2 routing remains internal). [#6049](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/6049)
- Added `CacheOptions.DisableInternalCacheOptions` static property and `CacheOptions.IsInternalCacheDisabled` to allow disabling MSAL's internal token cache. Added `CacheRefreshReason.CacheDisabled` and `MsalError.InternalCacheDisabled` to support this scenario. [#5947](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/5947)
- Added `AuthenticationResultExtensions.GetRefreshToken()` extension method for accessing refresh tokens from `AuthenticationResult`. [#5947](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/5947)
- Added `WithAttributeTokens` and `WithExtraBodyParameters` extension methods on `AbstractConfidentialClientAcquireTokenParameterBuilder` for enhanced extensibility. [#5888](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/5888)
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/MtlsBindingStrength.cs b/src/client/Microsoft.Identity.Client/AppConfig/MtlsBindingStrength.cs
new file mode 100644
index 0000000000..e792b60f86
--- /dev/null
+++ b/src/client/Microsoft.Identity.Client/AppConfig/MtlsBindingStrength.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Microsoft.Identity.Client.AppConfig
+{
+ ///
+ /// Describes the strength with which a token can be bound to a cryptographic key on the
+ /// current host. Higher values indicate stronger binding. The value reflects what the host
+ /// is capable of producing, not what a particular request used.
+ ///
+ ///
+ /// This type is shared by managed identity and confidential client mTLS Proof-of-Possession
+ /// scenarios. A value greater than means the host can bind a token to a
+ /// key; it does not by itself imply hardware attestation. Attestation corresponds to
+ /// the tier specifically.
+ ///
+ public enum MtlsBindingStrength
+ {
+ ///
+ /// No key binding is available, so the host cannot perform mTLS Proof-of-Possession. This
+ /// is the floor of the range (for example, on .NET Framework 4.6.2, which does not support
+ /// PoP).
+ ///
+ None = 0,
+
+ ///
+ /// The token can be bound to a software-backed key (for example, a persisted CNG key on
+ /// Windows, or a software RSA key elsewhere). The key is not hardware-isolated.
+ ///
+ Software = 1,
+
+ // 2 is reserved for a future tier (for example, TPM-backed keys).
+
+ ///
+ /// The token can be bound to a key isolated by Virtualization-based Security (VBS), such
+ /// as KeyGuard on a Trusted Launch (TVM) or Confidential (CVM) virtual machine. This is
+ /// the only tier that implies hardware-backed attestation.
+ ///
+ KeyGuard = 3
+ }
+}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/AbstractManagedIdentity.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/AbstractManagedIdentity.cs
index 2668c14c02..a4771ba7fc 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/AbstractManagedIdentity.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/AbstractManagedIdentity.cs
@@ -38,6 +38,11 @@ protected AbstractManagedIdentity(RequestContext requestContext, ManagedIdentity
_sourceType = sourceType;
}
+ // True only for the IMDSv1 source. IMDSv1 and IMDSv2 both report
+ // publicly, so this flag preserves the
+ // v1-specific MSIv1 claims validation without relying on the (folded) source label.
+ protected virtual bool RequiresMsiV1ClaimsValidation => false;
+
private const string XmsAzNwperimid = "xms_az_nwperimid";
public virtual async Task AuthenticateAsync(
@@ -65,16 +70,16 @@ public virtual async Task AuthenticateAsync(
// ignoring the value and polluting the cache with keys the endpoint never saw.
if (!string.IsNullOrEmpty(parameters.ClientClaims))
{
- if (_sourceType != ManagedIdentitySource.Imds && _sourceType != ManagedIdentitySource.ImdsV2)
+ if (_sourceType != ManagedIdentitySource.Imds)
{
throw new MsalClientException(
MsalError.InvalidRequest,
$"WithClaimsFromClient is only supported for IMDS-based managed identity sources. " +
$"The detected source is {_sourceType}. " +
- "Only ManagedIdentitySource.Imds and ManagedIdentitySource.ImdsV2 support the 'claims' parameter.");
+ "Only ManagedIdentitySource.Imds supports the 'claims' parameter.");
}
- if (_sourceType == ManagedIdentitySource.Imds)
+ if (RequiresMsiV1ClaimsValidation)
{
ValidateMsiv1Claims(parameters.ClientClaims);
}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ComputeMetadataResponse.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ComputeMetadataResponse.cs
new file mode 100644
index 0000000000..b358685ead
--- /dev/null
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ComputeMetadataResponse.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Identity.Client.Platforms.net;
+using JsonProperty = System.Text.Json.Serialization.JsonPropertyNameAttribute;
+
+namespace Microsoft.Identity.Client.ManagedIdentity
+{
+ ///
+ /// Represents compute metadata retrieved from the Azure Instance Metadata Service (IMDS).
+ ///
+ [JsonObject]
+ [Preserve(AllMembers = true)]
+ internal class ComputeMetadataResponse
+ {
+ /// Operating system type (e.g., Windows, Linux).
+ [JsonProperty("osType")]
+ public string OsType { get; set; }
+
+ ///
+ /// Security profile indicating platform security posture. May be null when IMDS
+ /// does not return security profile information for the current VM.
+ ///
+ [JsonProperty("securityProfile")]
+ public ComputeSecurityProfile SecurityProfile { get; set; }
+ }
+
+ ///
+ /// Represents the security profile of an Azure VM from IMDS compute metadata.
+ ///
+ [JsonObject]
+ [Preserve(AllMembers = true)]
+ internal class ComputeSecurityProfile
+ {
+ /// Security type of the VM (e.g., TrustedLaunch, ConfidentialVM).
+ [JsonProperty("securityType")]
+ public string SecurityType { get; set; }
+ }
+}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsComputeMetadataManager.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsComputeMetadataManager.cs
new file mode 100644
index 0000000000..b076faaa94
--- /dev/null
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsComputeMetadataManager.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client.Core;
+using Microsoft.Identity.Client.Http;
+using Microsoft.Identity.Client.Http.Retry;
+using Microsoft.Identity.Client.Utils;
+
+namespace Microsoft.Identity.Client.ManagedIdentity
+{
+ ///
+ /// Fetches compute metadata from the Azure Instance Metadata Service (IMDS)
+ /// to determine VM characteristics such as OS type and security profile.
+ ///
+ internal static class ImdsComputeMetadataManager
+ {
+ internal const string ImdsComputePath = "/metadata/instance/compute";
+ internal const string ImdsComputeApiVersion = "2021-02-01";
+
+ internal static async Task GetComputeMetadataAsync(
+ IHttpManager httpManager,
+ ILoggerAdapter logger,
+ CancellationToken cancellationToken)
+ {
+ var headers = new Dictionary
+ {
+ { "Metadata", "true" }
+ };
+
+ try
+ {
+ string queryParams =
+ $"{ImdsManagedIdentitySource.ApiVersionQueryParam}={ImdsComputeApiVersion}";
+
+ Uri endpoint = ImdsManagedIdentitySource.GetValidatedEndpoint(
+ logger,
+ ImdsComputePath,
+ queryParams);
+
+ HttpResponse response = await httpManager.SendRequestAsync(
+ endpoint,
+ headers,
+ body: null,
+ method: HttpMethod.Get,
+ logger: logger,
+ doNotThrow: true,
+ mtlsCertificate: null,
+ validateServerCertificate: null,
+ cancellationToken: cancellationToken,
+ retryPolicy: new ImdsRetryPolicy())
+ .ConfigureAwait(false);
+
+ if (response is null || response.StatusCode != HttpStatusCode.OK)
+ {
+ logger.Info($"[Managed Identity] IMDS compute metadata request failed. " +
+ $"StatusCode: {response?.StatusCode}");
+ return null;
+ }
+
+ return JsonHelper.TryToDeserializeFromJson(response.Body);
+ }
+ catch (Exception ex) when (ex is not OperationCanceledException)
+ {
+ logger.Info($"[Managed Identity] IMDS compute metadata request failed with exception: {ex.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Determines whether the host VM supports mTLS PoP based on compute metadata.
+ /// mTLS PoP is supported when the VM runs Windows and is a TVM (TrustedLaunch) or CVM (ConfidentialVM).
+ ///
+ internal static bool IsMtlsPopSupported(ComputeMetadataResponse metadata)
+ {
+ if (metadata is null)
+ {
+ return false;
+ }
+
+ bool isWindows = string.Equals(metadata.OsType, "Windows", StringComparison.OrdinalIgnoreCase);
+
+ string securityType = metadata.SecurityProfile?.SecurityType;
+ bool isTvmOrCvm = string.Equals(securityType, "TrustedLaunch", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(securityType, "ConfidentialVM", StringComparison.OrdinalIgnoreCase);
+
+ return isWindows && isTvmOrCvm;
+ }
+ }
+}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsManagedIdentitySource.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsManagedIdentitySource.cs
index 94b8eb833d..71d1ca7c61 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsManagedIdentitySource.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ImdsManagedIdentitySource.cs
@@ -60,6 +60,9 @@ internal ImdsManagedIdentitySource(RequestContext requestContext) :
requestContext.Logger.Verbose(() => "[Managed Identity] Creating IMDS managed identity source. Endpoint URI: " + _imdsEndpoint);
}
+ // IMDSv1 enforces MSIv1-specific claims validation; IMDSv2 does not.
+ protected override bool RequiresMsiV1ClaimsValidation => true;
+
protected override Task CreateRequestAsync(string resource)
{
ManagedIdentityRequest request = new(HttpMethod.Get, _imdsEndpoint);
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityCapabilities.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityCapabilities.cs
new file mode 100644
index 0000000000..23e624aaff
--- /dev/null
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityCapabilities.cs
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Identity.Client.AppConfig;
+
+namespace Microsoft.Identity.Client.ManagedIdentity
+{
+ ///
+ /// Describes the managed identity capabilities detected for the current host, including the
+ /// detected source and the strength with which tokens can be bound to a key.
+ ///
+ ///
+ /// This type is returned by .
+ /// It is useful for credential chains such as DefaultAzureCredential to decide whether
+ /// managed identity is available and what binding strength the host supports.
+ ///
+ public class ManagedIdentityCapabilities
+ {
+ ///
+ /// Gets the detected managed identity source.
+ ///
+ ///
+ /// The detected on the environment, or
+ /// if no source was detected. The internal
+ /// IMDS v1/v2 distinction is not surfaced here; both report
+ /// .
+ ///
+ public ManagedIdentitySource Source { get; }
+
+ ///
+ /// Gets the reason detection failed, if any.
+ ///
+ ///
+ /// A single string describing why managed identity detection failed, or null when
+ /// a source was detected.
+ ///
+ public string ErrorReason { get; }
+
+ ///
+ /// Gets the highest binding strength the current host is capable of producing.
+ ///
+ ///
+ /// The strongest available on this host. This is the
+ /// primary capability signal; callers should branch on it rather than on the source label.
+ ///
+ public MtlsBindingStrength MaxSupportedBindingStrength { get; }
+
+ ///
+ /// Gets a value indicating whether the host can bind a token to a key (mTLS
+ /// Proof-of-Possession).
+ ///
+ ///
+ /// true when is greater than
+ /// . This means the host can bind a token to a key;
+ /// it does not imply hardware attestation. Callers that require attestation must
+ /// check for the tier.
+ ///
+ public bool IsMtlsPopSupportedByHost => MaxSupportedBindingStrength > MtlsBindingStrength.None;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The detected managed identity source.
+ /// The highest binding strength the host supports.
+ /// The reason detection failed, or null on success.
+ internal ManagedIdentityCapabilities(
+ ManagedIdentitySource source,
+ MtlsBindingStrength maxSupportedBindingStrength,
+ string errorReason = null)
+ {
+ Source = source;
+ MaxSupportedBindingStrength = maxSupportedBindingStrength;
+ ErrorReason = errorReason;
+ }
+ }
+}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityClient.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityClient.cs
index 005c70c5cd..df1f7e4102 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityClient.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityClient.cs
@@ -7,6 +7,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client.ApiConfig.Parameters;
+using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.ManagedIdentity.V2;
@@ -22,9 +23,13 @@ internal class ManagedIdentityClient
private const string WindowsHimdsFilePath = "%Programfiles%\\AzureConnectedMachineAgent\\himds.exe";
private const string LinuxHimdsFilePath = "/opt/azcmagent/bin/himds";
- // Non-null only after the explicit discovery API (GetManagedIdentitySourceAsync) runs.
+ // Non-null only after the explicit discovery API (GetManagedIdentityCapabilitiesAsync) runs.
// Allows caching "NoneFound" (Source=None) without confusing it with "not discovered yet".
- private static ManagedIdentitySourceResult s_cachedSourceResult = null;
+ private static ManagedIdentityDiscoveryResult s_cachedSourceResult = null;
+
+ // Serializes explicit capability discovery so concurrent callers at process startup do not
+ // issue redundant IMDS probes or provision the binding key more than once.
+ private static readonly SemaphoreSlim s_discoveryLock = new SemaphoreSlim(1, 1);
// Holds the most recently minted mTLS binding certificate for this application instance.
private X509Certificate2 _runtimeMtlsBindingCertificate;
@@ -67,11 +72,13 @@ private Task GetOrSelectManagedIdentitySourceAsync(
cancellationToken.ThrowIfCancellationRequested();
ManagedIdentitySource source;
+ bool isImdsV2 = false;
if (s_cachedSourceResult != null)
{
// Use the cached explicit discovery result (including NoneFound)
source = s_cachedSourceResult.Source;
+ isImdsV2 = s_cachedSourceResult.DetectedImdsVersion == ImdsVersion.V2;
requestContext.Logger.Info($"[Managed Identity] Using cached discovery result: {source}");
}
else
@@ -103,18 +110,18 @@ private Task GetOrSelectManagedIdentitySourceAsync(
// Per-request fallback: if ImdsV2 is cached but mTLS PoP not requested, use ImdsV1 for this request only.
// We do NOT latch this state; future PoP requests can still leverage the cached ImdsV2 discovery.
- if (source == ManagedIdentitySource.ImdsV2 && !isMtlsPopRequested)
+ if (isImdsV2 && !isMtlsPopRequested)
{
requestContext.Logger.Info("[Managed Identity] ImdsV2 detected, but mTLS PoP was not requested. Using ImdsV1 for this request only. Please use the \"WithMtlsProofOfPossession\" API to request a token via ImdsV2.");
// Do NOT modify s_cachedSourceResult; keep cached ImdsV2 so future PoP
- // requests can leverage it.
- source = ManagedIdentitySource.Imds;
+ // requests can leverage it. Route this request through IMDSv1 only.
+ isImdsV2 = false;
}
// 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.Imds && isMtlsPopRequested)
+ if (source == ManagedIdentitySource.Imds && !isImdsV2 && isMtlsPopRequested)
{
throw new MsalClientException(
MsalError.MtlsPopTokenNotSupportedinImdsV1,
@@ -128,70 +135,200 @@ private Task GetOrSelectManagedIdentitySourceAsync(
ManagedIdentitySource.MachineLearning => MachineLearningManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.CloudShell => CloudShellManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.AzureArc => AzureArcManagedIdentitySource.Create(requestContext),
- ManagedIdentitySource.ImdsV2 => ImdsV2ManagedIdentitySource.Create(requestContext),
- ManagedIdentitySource.Imds => ImdsManagedIdentitySource.Create(requestContext),
+ ManagedIdentitySource.Imds => isImdsV2
+ ? ImdsV2ManagedIdentitySource.Create(requestContext)
+ : ImdsManagedIdentitySource.Create(requestContext),
_ => throw CreateManagedIdentityUnavailableException(s_cachedSourceResult)
});
}
}
- private static ManagedIdentitySourceResult CacheDiscoveryResult(ManagedIdentitySourceResult result)
+ private static ManagedIdentityDiscoveryResult CacheDiscoveryResult(ManagedIdentityDiscoveryResult result)
{
s_cachedSourceResult = result;
return result;
}
// Detect managed identity source by probing IMDS endpoints.
- // This method is called only by the explicit discovery path (GetManagedIdentitySourceAsync in ManagedIdentityApplication.cs).
+ // This method is called only by the explicit discovery path (GetManagedIdentityCapabilitiesAsync in ManagedIdentityApplication.cs).
// It probes IMDS v2 first, then v1 if v2 fails, and caches the result.
- internal async Task GetManagedIdentitySourceAsync(
+ internal async Task GetManagedIdentityCapabilitiesAsync(
RequestContext requestContext,
CancellationToken cancellationToken)
{
- // Return cached result if explicit discovery already ran
+ // Fast path: explicit discovery already completed.
if (s_cachedSourceResult != null)
{
return s_cachedSourceResult;
}
- // First check env vars to avoid the probe if possible
- ManagedIdentitySource source = GetManagedIdentitySourceNoImds(requestContext.Logger);
-
- if (source != ManagedIdentitySource.None)
+ // Single-flight: ensure only one caller probes IMDS / provisions a binding key at a
+ // time. Concurrent callers at process startup wait here and then observe the cached
+ // result instead of issuing redundant probes. Try a non-blocking acquire first so an
+ // uncontended caller keeps the existing cancellation point (the HTTP probe); only a
+ // contended caller waits, and that wait is cancelable.
+ bool lockTaken = s_discoveryLock.Wait(0);
+ if (!lockTaken)
{
- return CacheDiscoveryResult(new ManagedIdentitySourceResult(source));
+ await s_discoveryLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+ lockTaken = true;
}
- string imdsV1FailureReason = null;
- string imdsV2FailureReason = null;
+ try
+ {
+ // Re-check under the lock in case another caller completed discovery while we waited.
+ if (s_cachedSourceResult != null)
+ {
+ return s_cachedSourceResult;
+ }
+
+ // First check env vars to avoid the probe if possible
+ ManagedIdentitySource source = GetManagedIdentitySourceNoImds(requestContext.Logger);
+
+ if (source != ManagedIdentitySource.None)
+ {
+ return CacheDiscoveryResult(new ManagedIdentityDiscoveryResult(source));
+ }
+
+ string imdsV1FailureReason = null;
+ string imdsV2FailureReason = null;
+
+ // Probe IMDS v2 first. The v2 path (CSR metadata endpoint) only exists on hosts that
+ // actually support IMDSv2; on v1-only hosts it returns 404. Probing v2 first avoids
+ // the v1 success-on-400 contract masking a v2-capable host (see issue #6024).
+ var (imdsV2Success, imdsV2Failure) = await ImdsManagedIdentitySource.ProbeImdsEndpointAsync(requestContext, ImdsVersion.V2, cancellationToken).ConfigureAwait(false);
+ if (imdsV2Success)
+ {
+ requestContext.Logger.Info("[Managed Identity] ImdsV2 detected.");
+
+ // A successful IMDSv2 probe proves the host speaks the key-bound CSR (PoP) protocol,
+ // so it can bind at least at Software strength. Probe the platform key provider to see
+ // whether it can produce a VBS-isolated KeyGuard key and thus advertise the stronger,
+ // attested KeyGuard tier. The v2 PoP token flow itself requires a KeyGuard key, so this
+ // mirrors what an actual PoP request would obtain.
+ MtlsBindingStrength v2Strength = await DetermineImdsV2BindingStrengthAsync(requestContext, cancellationToken).ConfigureAwait(false);
+ requestContext.Logger.Info($"[Managed Identity] Host max supported binding strength: {v2Strength}.");
+
+ return CacheDiscoveryResult(new ManagedIdentityDiscoveryResult(
+ ManagedIdentitySource.Imds,
+ ImdsVersion.V2,
+ v2Strength));
+ }
+ imdsV2FailureReason = imdsV2Failure;
+
+ // If v2 fails, fall back to probing IMDS v1.
+ var (imdsV1Success, imdsV1Failure) = await ImdsManagedIdentitySource.ProbeImdsEndpointAsync(requestContext, ImdsVersion.V1, cancellationToken).ConfigureAwait(false);
+ if (imdsV1Success)
+ {
+ requestContext.Logger.Info("[Managed Identity] ImdsV1 detected.");
+
+ MtlsBindingStrength strength = await DetermineImdsV1BindingStrengthAsync(requestContext, cancellationToken).ConfigureAwait(false);
+ requestContext.Logger.Info($"[Managed Identity] Host max supported binding strength: {strength}.");
- // Probe IMDS v2 first. The v2 path (CSR metadata endpoint) only exists on hosts that
- // actually support IMDSv2; on v1-only hosts it returns 404. Probing v2 first avoids
- // the v1 success-on-400 contract masking a v2-capable host (see issue #6024).
- var (imdsV2Success, imdsV2Failure) = await ImdsManagedIdentitySource.ProbeImdsEndpointAsync(requestContext, ImdsVersion.V2, cancellationToken).ConfigureAwait(false);
- if (imdsV2Success)
+ return CacheDiscoveryResult(new ManagedIdentityDiscoveryResult(
+ ManagedIdentitySource.Imds,
+ ImdsVersion.V1,
+ strength));
+ }
+ imdsV1FailureReason = imdsV1Failure;
+
+ requestContext.Logger.Info($"[Managed Identity] {MsalErrorMessage.ManagedIdentityAllSourcesUnavailable}");
+ return CacheDiscoveryResult(new ManagedIdentityDiscoveryResult(
+ ManagedIdentitySource.None,
+ imdsV1FailureReason: imdsV1FailureReason,
+ imdsV2FailureReason: imdsV2FailureReason));
+ }
+ finally
{
- requestContext.Logger.Info("[Managed Identity] ImdsV2 detected.");
- return CacheDiscoveryResult(new ManagedIdentitySourceResult(ManagedIdentitySource.ImdsV2));
+ if (lockTaken)
+ {
+ s_discoveryLock.Release();
+ }
}
- imdsV2FailureReason = imdsV2Failure;
+ }
- // If v2 fails, fall back to probing IMDS v1.
- var (imdsV1Success, imdsV1Failure) = await ImdsManagedIdentitySource.ProbeImdsEndpointAsync(requestContext, ImdsVersion.V1, cancellationToken).ConfigureAwait(false);
- if (imdsV1Success)
+ // Determines the host's maximum mTLS binding strength for IMDSv1-only hosts using the
+ // /metadata/instance/compute security profile. mTLS PoP is not supported on .NET
+ // Framework 4.6.2, so the host is reported as None there.
+ private static Task DetermineImdsV1BindingStrengthAsync(
+ RequestContext requestContext,
+ CancellationToken cancellationToken)
+ {
+#if NET462
+ return Task.FromResult(MtlsBindingStrength.None);
+#else
+ return DetermineImdsV1BindingStrengthCoreAsync(requestContext, cancellationToken);
+#endif
+ }
+
+#if !NET462
+ private static async Task DetermineImdsV1BindingStrengthCoreAsync(
+ RequestContext requestContext,
+ CancellationToken cancellationToken)
+ {
+ ComputeMetadataResponse computeMetadata = await ImdsComputeMetadataManager.GetComputeMetadataAsync(
+ requestContext.ServiceBundle.HttpManager,
+ requestContext.Logger,
+ cancellationToken).ConfigureAwait(false);
+
+ // A Windows TVM/CVM security profile indicates key-binding capability. We report
+ // Software (binding available) rather than KeyGuard: the security profile alone does
+ // not prove a successful VBS/KeyGuard attestation, and an IMDSv1-only host cannot use
+ // the v2 CSR (PoP) flow regardless, so we must not overclaim attestation.
+ return ImdsComputeMetadataManager.IsMtlsPopSupported(computeMetadata)
+ ? MtlsBindingStrength.Software
+ : MtlsBindingStrength.None;
+ }
+#endif
+
+ // Determines the IMDSv2 host's maximum mTLS binding strength. The host supports at least
+ // Software binding (the v2 CSR flow binds a token to a key); if the platform can produce a
+ // VBS-isolated KeyGuard key it supports the stronger, attested KeyGuard tier. mTLS PoP is
+ // unavailable on .NET Framework 4.6.2, so the host is reported as None there.
+ private static Task DetermineImdsV2BindingStrengthAsync(
+ RequestContext requestContext,
+ CancellationToken cancellationToken)
+ {
+#if NET462
+ return Task.FromResult(MtlsBindingStrength.None);
+#else
+ return DetermineImdsV2BindingStrengthCoreAsync(requestContext, cancellationToken);
+#endif
+ }
+
+#if !NET462
+ private static async Task DetermineImdsV2BindingStrengthCoreAsync(
+ RequestContext requestContext,
+ CancellationToken cancellationToken)
+ {
+ ManagedIdentityKeyType keyType;
+ try
{
- requestContext.Logger.Info("[Managed Identity] ImdsV1 detected.");
- return CacheDiscoveryResult(new ManagedIdentitySourceResult(ManagedIdentitySource.Imds));
+ IManagedIdentityKeyProvider keyProvider = requestContext.ServiceBundle.PlatformProxy.ManagedIdentityKeyProvider;
+ ManagedIdentityKeyInfo keyInfo = await keyProvider
+ .GetOrCreateKeyAsync(requestContext.Logger, cancellationToken)
+ .ConfigureAwait(false);
+ keyType = keyInfo.Type;
}
- imdsV1FailureReason = imdsV1Failure;
-
- requestContext.Logger.Info($"[Managed Identity] {MsalErrorMessage.ManagedIdentityAllSourcesUnavailable}");
- return CacheDiscoveryResult(new ManagedIdentitySourceResult(ManagedIdentitySource.None)
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception ex)
{
- ImdsV1FailureReason = imdsV1FailureReason,
- ImdsV2FailureReason = imdsV2FailureReason
- });
+ // Failing to obtain a key does not invalidate the host's v2/Software capability;
+ // keep the Software floor rather than failing capability discovery.
+ requestContext.Logger.Info($"[Managed Identity] KeyGuard probe failed; reporting Software binding strength. {ex.Message}");
+ return MtlsBindingStrength.Software;
+ }
+
+ // Only a VBS-isolated KeyGuard key justifies the attested KeyGuard tier. Any other key
+ // type stays at the Software floor; we never downgrade a confirmed v2 host below Software.
+ return keyType == ManagedIdentityKeyType.KeyGuard
+ ? MtlsBindingStrength.KeyGuard
+ : MtlsBindingStrength.Software;
}
+#endif
///
/// Detects the managed identity source based on the availability of environment variables.
@@ -281,24 +418,14 @@ private static bool ValidateAzureArcEnvironment(string identityEndpoint, string
/// Creates an MsalClientException for when no managed identity source is available,
/// including detailed failure information from IMDS probes if available.
///
- private static MsalClientException CreateManagedIdentityUnavailableException(ManagedIdentitySourceResult sourceResult)
+ private static MsalClientException CreateManagedIdentityUnavailableException(ManagedIdentityDiscoveryResult discoveryResult)
{
string errorMessage = MsalErrorMessage.ManagedIdentityAllSourcesUnavailable;
- if (sourceResult != null)
+ string combinedReason = discoveryResult?.GetCombinedErrorReason();
+ if (!string.IsNullOrEmpty(combinedReason))
{
- if (!string.IsNullOrEmpty(sourceResult.ImdsV1FailureReason) || !string.IsNullOrEmpty(sourceResult.ImdsV2FailureReason))
- {
- errorMessage += " MSAL was not able to detect the Azure Instance Metadata Service (IMDS) that runs on VMs:";
- if (!string.IsNullOrEmpty(sourceResult.ImdsV2FailureReason))
- {
- errorMessage += $" IMDSv2: {sourceResult.ImdsV2FailureReason}.";
- }
- if (!string.IsNullOrEmpty(sourceResult.ImdsV1FailureReason))
- {
- errorMessage += $" IMDSv1: {sourceResult.ImdsV1FailureReason}.";
- }
- }
+ errorMessage += " The Azure Instance Metadata Service (IMDS) that runs on VMs was not detected: " + combinedReason;
}
return new MsalClientException(MsalError.ManagedIdentityAllSourcesUnavailable, errorMessage);
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityDiscoveryResult.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityDiscoveryResult.cs
new file mode 100644
index 0000000000..7abdedb984
--- /dev/null
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityDiscoveryResult.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Identity.Client.AppConfig;
+using Microsoft.Identity.Client.ManagedIdentity.V2;
+
+namespace Microsoft.Identity.Client.ManagedIdentity
+{
+ ///
+ /// Internal result of managed identity source discovery. Carries the public-facing source
+ /// plus internal-only routing details (such as the detected IMDS protocol version) that are
+ /// not exposed on the public surface.
+ ///
+ internal sealed class ManagedIdentityDiscoveryResult
+ {
+ ///
+ /// The detected managed identity source. The IMDS v1/v2 distinction is folded into
+ /// ; the version is carried separately in
+ /// for internal routing.
+ ///
+ public ManagedIdentitySource Source { get; }
+
+ ///
+ /// The IMDS protocol version detected, when is
+ /// ; otherwise null. Internal routing only.
+ ///
+ public ImdsVersion? DetectedImdsVersion { get; }
+
+ ///
+ /// The highest binding strength the host can produce.
+ ///
+ public MtlsBindingStrength MaxSupportedBindingStrength { get; }
+
+ ///
+ /// Failure reason from the IMDSv1 probe, if it failed; otherwise null.
+ ///
+ public string ImdsV1FailureReason { get; }
+
+ ///
+ /// Failure reason from the IMDSv2 probe, if it failed; otherwise null.
+ ///
+ public string ImdsV2FailureReason { get; }
+
+ public ManagedIdentityDiscoveryResult(
+ ManagedIdentitySource source,
+ ImdsVersion? detectedImdsVersion = null,
+ MtlsBindingStrength maxSupportedBindingStrength = MtlsBindingStrength.None,
+ string imdsV1FailureReason = null,
+ string imdsV2FailureReason = null)
+ {
+ Source = source;
+ DetectedImdsVersion = detectedImdsVersion;
+ MaxSupportedBindingStrength = maxSupportedBindingStrength;
+ ImdsV1FailureReason = imdsV1FailureReason;
+ ImdsV2FailureReason = imdsV2FailureReason;
+ }
+
+ ///
+ /// Builds a single, combined error reason from the IMDS probe failures, or null
+ /// when neither probe reported a failure.
+ ///
+ public string GetCombinedErrorReason()
+ {
+ bool hasV1 = !string.IsNullOrEmpty(ImdsV1FailureReason);
+ bool hasV2 = !string.IsNullOrEmpty(ImdsV2FailureReason);
+
+ if (!hasV1 && !hasV2)
+ {
+ return null;
+ }
+
+ var sb = new System.Text.StringBuilder();
+ if (hasV2)
+ {
+ sb.Append($"IMDSv2: {ImdsV2FailureReason}.");
+ }
+ if (hasV1)
+ {
+ if (sb.Length > 0)
+ {
+ sb.Append(' ');
+ }
+ sb.Append($"IMDSv1: {ImdsV1FailureReason}.");
+ }
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySource.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySource.cs
index faf3e6f16c..489556bd24 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySource.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySource.cs
@@ -48,17 +48,12 @@ public enum ManagedIdentitySource
/// Indicates that the source is defaulted to IMDS since no environment variables are set.
/// This is used to detect the managed identity source.
///
- [Obsolete("In use only to support the now obsolete GetManagedIdentitySource API. Will be removed in a future version. Use GetManagedIdentitySourceAsync instead.")]
+ [Obsolete("In use only to support the now obsolete GetManagedIdentitySource API. Will be removed in a future version. Use GetManagedIdentityCapabilitiesAsync instead.")]
DefaultToImds,
///
/// The source to acquire token for managed identity is Machine Learning Service.
///
MachineLearning,
-
- ///
- /// The source to acquire token for managed identity is IMDSV2.
- ///
- ImdsV2,
}
}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySourceResult.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySourceResult.cs
deleted file mode 100644
index 5ae7eb7a22..0000000000
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentitySourceResult.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-namespace Microsoft.Identity.Client.ManagedIdentity
-{
- ///
- /// Result of managed identity source detection, including the detected source and any failure information from IMDS probes.
- ///
- ///
- /// This class is returned by to provide
- /// detailed information about managed identity source detection, including failure reasons when IMDS probes fail.
- /// This information is useful for credential chains like DefaultAzureCredential to determine whether to skip
- /// managed identity authentication entirely.
- ///
- public class ManagedIdentitySourceResult
- {
- ///
- /// Gets the detected managed identity source.
- ///
- ///
- /// The that was detected on the environment.
- /// Returns if no managed identity source was detected.
- ///
- public ManagedIdentitySource Source { get; }
-
- ///
- /// Gets or sets the failure reason from the IMDSv1 probe, if it failed.
- ///
- ///
- /// A string describing why the IMDSv1 probe failed, or null if the probe succeeded or was not attempted.
- ///
- public string ImdsV1FailureReason { get; set; }
-
- ///
- /// Gets or sets the failure reason from the IMDSv2 probe, if it failed.
- ///
- ///
- /// A string describing why the IMDSv2 probe failed, or null if the probe succeeded or was not attempted.
- ///
- public string ImdsV2FailureReason { get; set; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The detected managed identity source.
- public ManagedIdentitySourceResult(ManagedIdentitySource source)
- {
- Source = source;
- }
- }
-}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/CertificateRequestResponse.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/CertificateRequestResponse.cs
index c92d342ea1..793d4871a4 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/CertificateRequestResponse.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/CertificateRequestResponse.cs
@@ -41,7 +41,7 @@ public static void Validate(CertificateRequestResponse certificateRequestRespons
MsalError.ManagedIdentityRequestFailed,
$"[ImdsV2] ImdsV2ManagedIdentitySource.ExecuteCertificateRequestAsync failed because the certificate request response is malformed. Status code: 200",
null,
- ManagedIdentitySource.ImdsV2,
+ ManagedIdentitySource.Imds,
(int)HttpStatusCode.OK);
}
}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs b/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs
index 166f859881..db1cd63d06 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs
@@ -113,7 +113,7 @@ private static void ThrowCsrMetadataRequestException(
MsalError.ManagedIdentityRequestFailed,
$"[ImdsV2] {errorMessage}",
ex,
- ManagedIdentitySource.ImdsV2,
+ ManagedIdentitySource.Imds,
statusCode);
}
@@ -177,7 +177,7 @@ internal ImdsV2ManagedIdentitySource(RequestContext requestContext)
internal ImdsV2ManagedIdentitySource(
RequestContext requestContext,
IMtlsCertificateCache mtlsCache)
- : base(requestContext, ManagedIdentitySource.ImdsV2)
+ : base(requestContext, ManagedIdentitySource.Imds)
{
_mtlsCache = mtlsCache ?? throw new ArgumentNullException(nameof(mtlsCache));
}
@@ -312,7 +312,7 @@ private async Task ExecuteCertificateRequestAsync(
MsalError.ManagedIdentityRequestFailed,
"[ImdsV2] ImdsV2ManagedIdentitySource.ExecuteCertificateRequestAsync failed.",
ex,
- ManagedIdentitySource.ImdsV2,
+ ManagedIdentitySource.Imds,
statusCode);
}
@@ -322,7 +322,7 @@ private async Task ExecuteCertificateRequestAsync(
MsalError.ManagedIdentityRequestFailed,
$"[ImdsV2] ImdsV2ManagedIdentitySource.ExecuteCertificateRequestAsync failed due to HTTP error. Status code: {response.StatusCode} Body: {response.Body}",
null,
- ManagedIdentitySource.ImdsV2,
+ ManagedIdentitySource.Imds,
(int)response.StatusCode);
}
@@ -351,7 +351,10 @@ protected override async Task CreateRequestAsync(string
{
throw new MsalClientException(
"mtls_pop_requires_keyguard",
- $"[ImdsV2] mTLS Proof-of-Possession requires KeyGuard keys. Current key type: {keyInfo.Type}");
+ $"[ImdsV2] mTLS Proof-of-Possession currently requires a KeyGuard key, but this host produced a '{keyInfo.Type}' key. " +
+ "The host may report Software-strength binding capability (which means it can bind a token to a key), " +
+ "but the IMDSv2 PoP token flow only accepts VBS-isolated KeyGuard keys today. " +
+ "Ensure Virtualization-based Security (VBS)/KeyGuard is enabled on the host, or request a bearer token instead.");
}
}
diff --git a/src/client/Microsoft.Identity.Client/ManagedIdentityApplication.cs b/src/client/Microsoft.Identity.Client/ManagedIdentityApplication.cs
index 05a771759b..a5ae1c8624 100644
--- a/src/client/Microsoft.Identity.Client/ManagedIdentityApplication.cs
+++ b/src/client/Microsoft.Identity.Client/ManagedIdentityApplication.cs
@@ -55,20 +55,39 @@ public AcquireTokenForManagedIdentityParameterBuilder AcquireTokenForManagedIden
resource);
}
- ///
- public async Task GetManagedIdentitySourceAsync(CancellationToken cancellationToken)
+ ///
+ /// Detects the managed identity source available on the host and the strongest mTLS
+ /// binding the host can produce. Useful for credential chains (such as
+ /// DefaultAzureCredential) to decide whether managed identity is available and
+ /// what binding strength to expect.
+ ///
+ ///
+ /// On hosts capable of key binding, detecting the strongest available strength may provision
+ /// (and persist) a binding key as a side effect, pre-warming the cache reused by a subsequent
+ /// token request. The key provider is created once per process and its key is cached.
+ ///
+ /// A cancellation token to observe while waiting for the detection to complete.
+ /// A describing the detected source and host capabilities.
+ public async Task GetManagedIdentityCapabilitiesAsync(CancellationToken cancellationToken)
{
// Create a temporary RequestContext for the logger and the IMDS probe request.
var requestContext = new RequestContext(this.ServiceBundle, Guid.NewGuid(), null, cancellationToken);
- return await ManagedIdentityClient.GetManagedIdentitySourceAsync(requestContext, cancellationToken).ConfigureAwait(false);
+ ManagedIdentityDiscoveryResult discoveryResult = await ManagedIdentityClient
+ .GetManagedIdentityCapabilitiesAsync(requestContext, cancellationToken)
+ .ConfigureAwait(false);
+
+ return new ManagedIdentityCapabilities(
+ discoveryResult.Source,
+ discoveryResult.MaxSupportedBindingStrength,
+ discoveryResult.GetCombinedErrorReason());
}
///
/// Detects and returns the managed identity source available on the environment.
///
/// Managed identity source detected on the environment if any.
- [Obsolete("Use GetManagedIdentitySourceAsync() instead. \"ManagedIdentityApplication mi = miBuilder.Build() as ManagedIdentityApplication;\"")]
+ [Obsolete("Use GetManagedIdentityCapabilitiesAsync() instead. \"ManagedIdentityApplication mi = miBuilder.Build() as ManagedIdentityApplication;\"")]
public static ManagedIdentitySource GetManagedIdentitySource()
{
var source = ManagedIdentityClient.GetManagedIdentitySourceNoImds();
diff --git a/src/client/Microsoft.Identity.Client/Platforms/net/MsalJsonSerializerContext.cs b/src/client/Microsoft.Identity.Client/Platforms/net/MsalJsonSerializerContext.cs
index 4fc00ab332..e9b6384cc0 100644
--- a/src/client/Microsoft.Identity.Client/Platforms/net/MsalJsonSerializerContext.cs
+++ b/src/client/Microsoft.Identity.Client/Platforms/net/MsalJsonSerializerContext.cs
@@ -42,6 +42,8 @@ namespace Microsoft.Identity.Client.Platforms.net
[JsonSerializable(typeof(CuidInfo))]
[JsonSerializable(typeof(CertificateRequestBody))]
[JsonSerializable(typeof(CertificateRequestResponse))]
+ [JsonSerializable(typeof(ComputeMetadataResponse))]
+ [JsonSerializable(typeof(ComputeSecurityProfile))]
[JsonSerializable(typeof(Dictionary))]
[JsonSourceGenerationOptions]
internal partial class MsalJsonSerializerContext : JsonSerializerContext
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Shipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Shipped.txt
index e370d1cf34..7bf56d508d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Shipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Shipped.txt
@@ -1087,7 +1087,6 @@ const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "
const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate2) -> System.Net.Http.HttpClient
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
@@ -1111,14 +1110,6 @@ Microsoft.Identity.Client.Extensibility.ExecutionResult.Result.get -> Microsoft.
Microsoft.Identity.Client.Extensibility.ExecutionResult.Successful.get -> bool
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnCompletion(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func onCompletion) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnMsalServiceFailure(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> onMsalServiceFailure) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ManagedIdentitySourceResult(Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource source) -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
-Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
Microsoft.Identity.Client.AppConfig.CertificateOptions
@@ -1151,4 +1142,4 @@ Microsoft.Identity.Client.CacheRefreshReason.CacheDisabled = 5 -> Microsoft.Iden
Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions
const Microsoft.Identity.Client.MsalError.InternalCacheDisabled = "internal_cache_disabled" -> string
static Microsoft.Identity.Client.CacheOptions.DisableInternalCacheOptions.get -> Microsoft.Identity.Client.CacheOptions
-static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
index c08c06c96f..bc21638d2a 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
@@ -6,4 +6,14 @@ Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.CredentialEvaluationContext(System.Security.Cryptography.X509Certificates.X509Certificate2 mtlsCertificate) -> void
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.MtlsCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithCachePartitionKey(this Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder builder, string key, string value) -> T
-static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.None = 0 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.Software = 1 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.KeyGuard = 3 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.ErrorReason.get -> string
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopSupportedByHost.get -> bool
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
+Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Shipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Shipped.txt
index 00156f03fa..10711bb7da 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Shipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Shipped.txt
@@ -1087,7 +1087,6 @@ const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "
const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate2) -> System.Net.Http.HttpClient
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
@@ -1111,14 +1110,6 @@ Microsoft.Identity.Client.Extensibility.ExecutionResult.Result.get -> Microsoft.
Microsoft.Identity.Client.Extensibility.ExecutionResult.Successful.get -> bool
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnCompletion(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func onCompletion) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnMsalServiceFailure(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> onMsalServiceFailure) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ManagedIdentitySourceResult(Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource source) -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
-Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
static Microsoft.Identity.Client.Extensibility.AbstractConfidentialClientAcquireTokenParameterBuilderExtension.WithExtraClientAssertionClaims(this Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder builder, string clientAssertionClaims) -> Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder
@@ -1151,4 +1142,4 @@ Microsoft.Identity.Client.CacheRefreshReason.CacheDisabled = 5 -> Microsoft.Iden
Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions
const Microsoft.Identity.Client.MsalError.InternalCacheDisabled = "internal_cache_disabled" -> string
static Microsoft.Identity.Client.CacheOptions.DisableInternalCacheOptions.get -> Microsoft.Identity.Client.CacheOptions
-static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
index c08c06c96f..bc21638d2a 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
@@ -6,4 +6,14 @@ Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.CredentialEvaluationContext(System.Security.Cryptography.X509Certificates.X509Certificate2 mtlsCertificate) -> void
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.MtlsCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithCachePartitionKey(this Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder builder, string key, string value) -> T
-static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.None = 0 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.Software = 1 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.KeyGuard = 3 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.ErrorReason.get -> string
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopSupportedByHost.get -> bool
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
+Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Shipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Shipped.txt
index e65db40158..4c4d454304 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Shipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Shipped.txt
@@ -1053,7 +1053,6 @@ const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "
const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate2) -> System.Net.Http.HttpClient
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
@@ -1077,14 +1076,6 @@ Microsoft.Identity.Client.Extensibility.ExecutionResult.Result.get -> Microsoft.
Microsoft.Identity.Client.Extensibility.ExecutionResult.Successful.get -> bool
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnCompletion(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func onCompletion) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnMsalServiceFailure(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> onMsalServiceFailure) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ManagedIdentitySourceResult(Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource source) -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
-Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
Microsoft.Identity.Client.AppConfig.CertificateOptions
@@ -1117,4 +1108,4 @@ Microsoft.Identity.Client.CacheRefreshReason.CacheDisabled = 5 -> Microsoft.Iden
Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions
const Microsoft.Identity.Client.MsalError.InternalCacheDisabled = "internal_cache_disabled" -> string
static Microsoft.Identity.Client.CacheOptions.DisableInternalCacheOptions.get -> Microsoft.Identity.Client.CacheOptions
-static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
index c08c06c96f..bc21638d2a 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
@@ -6,4 +6,14 @@ Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.CredentialEvaluationContext(System.Security.Cryptography.X509Certificates.X509Certificate2 mtlsCertificate) -> void
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.MtlsCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithCachePartitionKey(this Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder builder, string key, string value) -> T
-static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.None = 0 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.Software = 1 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.KeyGuard = 3 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.ErrorReason.get -> string
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopSupportedByHost.get -> bool
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
+Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Shipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Shipped.txt
index 2009cb3479..8859c66843 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Shipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Shipped.txt
@@ -1055,7 +1055,6 @@ const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "
const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate2) -> System.Net.Http.HttpClient
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
@@ -1079,14 +1078,6 @@ Microsoft.Identity.Client.Extensibility.ExecutionResult.Result.get -> Microsoft.
Microsoft.Identity.Client.Extensibility.ExecutionResult.Successful.get -> bool
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnCompletion(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func onCompletion) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnMsalServiceFailure(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> onMsalServiceFailure) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ManagedIdentitySourceResult(Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource source) -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
-Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
Microsoft.Identity.Client.AppConfig.CertificateOptions
@@ -1119,4 +1110,4 @@ Microsoft.Identity.Client.CacheRefreshReason.CacheDisabled = 5 -> Microsoft.Iden
Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions
const Microsoft.Identity.Client.MsalError.InternalCacheDisabled = "internal_cache_disabled" -> string
static Microsoft.Identity.Client.CacheOptions.DisableInternalCacheOptions.get -> Microsoft.Identity.Client.CacheOptions
-static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
index c08c06c96f..bc21638d2a 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
@@ -6,4 +6,14 @@ Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.CredentialEvaluationContext(System.Security.Cryptography.X509Certificates.X509Certificate2 mtlsCertificate) -> void
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.MtlsCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithCachePartitionKey(this Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder builder, string key, string value) -> T
-static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.None = 0 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.Software = 1 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.KeyGuard = 3 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.ErrorReason.get -> string
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopSupportedByHost.get -> bool
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
+Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Shipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Shipped.txt
index 6a0d655679..a88846f0f7 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Shipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Shipped.txt
@@ -1049,7 +1049,6 @@ const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "
const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate2) -> System.Net.Http.HttpClient
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
@@ -1073,14 +1072,6 @@ Microsoft.Identity.Client.Extensibility.ExecutionResult.Result.get -> Microsoft.
Microsoft.Identity.Client.Extensibility.ExecutionResult.Successful.get -> bool
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnCompletion(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func onCompletion) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnMsalServiceFailure(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> onMsalServiceFailure) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ManagedIdentitySourceResult(Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource source) -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
-Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
Microsoft.Identity.Client.AppConfig.CertificateOptions
@@ -1113,4 +1104,4 @@ Microsoft.Identity.Client.CacheRefreshReason.CacheDisabled = 5 -> Microsoft.Iden
Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions
const Microsoft.Identity.Client.MsalError.InternalCacheDisabled = "internal_cache_disabled" -> string
static Microsoft.Identity.Client.CacheOptions.DisableInternalCacheOptions.get -> Microsoft.Identity.Client.CacheOptions
-static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
index c08c06c96f..bc21638d2a 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
@@ -6,4 +6,14 @@ Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.CredentialEvaluationContext(System.Security.Cryptography.X509Certificates.X509Certificate2 mtlsCertificate) -> void
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.MtlsCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithCachePartitionKey(this Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder builder, string key, string value) -> T
-static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.None = 0 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.Software = 1 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.KeyGuard = 3 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.ErrorReason.get -> string
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopSupportedByHost.get -> bool
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
+Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Shipped.txt
index 64058c8fc0..067f7cfdb4 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Shipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Shipped.txt
@@ -1049,7 +1049,6 @@ const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "
const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory
Microsoft.Identity.Client.IMsalMtlsHttpClientFactory.GetHttpClient(System.Security.Cryptography.X509Certificates.X509Certificate2 x509Certificate2) -> System.Net.Http.HttpClient
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder
static Microsoft.Identity.Client.ApplicationBase.ResetStateForTest() -> void
const Microsoft.Identity.Client.MsalError.ImdsServiceError = "imds_service_error" -> string
@@ -1073,14 +1072,6 @@ Microsoft.Identity.Client.Extensibility.ExecutionResult.Result.get -> Microsoft.
Microsoft.Identity.Client.Extensibility.ExecutionResult.Successful.get -> bool
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnCompletion(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func onCompletion) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.OnMsalServiceFailure(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> onMsalServiceFailure) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV1FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.get -> string
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ImdsV2FailureReason.set -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.ManagedIdentitySourceResult(Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource source) -> void
-Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySourceResult.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
-Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
Microsoft.Identity.Client.AppConfig.CertificateOptions
@@ -1113,4 +1104,4 @@ Microsoft.Identity.Client.CacheRefreshReason.CacheDisabled = 5 -> Microsoft.Iden
Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions
const Microsoft.Identity.Client.MsalError.InternalCacheDisabled = "internal_cache_disabled" -> string
static Microsoft.Identity.Client.CacheOptions.DisableInternalCacheOptions.get -> Microsoft.Identity.Client.CacheOptions
-static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AuthenticationResultExtensions.GetRefreshToken(this Microsoft.Identity.Client.AuthenticationResult result) -> string
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
index c08c06c96f..bc21638d2a 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
@@ -6,4 +6,14 @@ Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.CredentialEvaluationContext(System.Security.Cryptography.X509Certificates.X509Certificate2 mtlsCertificate) -> void
Microsoft.Identity.Client.AuthScheme.CredentialEvaluationContext.MtlsCertificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2
static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithCachePartitionKey(this Microsoft.Identity.Client.BaseAbstractAcquireTokenParameterBuilder builder, string key, string value) -> T
-static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
\ No newline at end of file
+static Microsoft.Identity.Client.Extensibility.AcquireTokenParameterBuilderExtensions.WithReservedScopes(this Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder builder, bool offlineAccessScope) -> Microsoft.Identity.Client.AcquireTokenByAuthorizationCodeParameterBuilder
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.None = 0 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.Software = 1 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.AppConfig.MtlsBindingStrength.KeyGuard = 3 -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.ErrorReason.get -> string
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.IsMtlsPopSupportedByHost.get -> bool
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.MaxSupportedBindingStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
+Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityCapabilities.Source.get -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource
+Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentityCapabilitiesAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
diff --git a/src/client/Microsoft.Identity.Lab.Api/Helpers/ManagedIdentityTestUtil.cs b/src/client/Microsoft.Identity.Lab.Api/Helpers/ManagedIdentityTestUtil.cs
index 5f151d294c..3132ffb0ae 100644
--- a/src/client/Microsoft.Identity.Lab.Api/Helpers/ManagedIdentityTestUtil.cs
+++ b/src/client/Microsoft.Identity.Lab.Api/Helpers/ManagedIdentityTestUtil.cs
@@ -63,7 +63,6 @@ public static void SetEnvironmentVariables(ManagedIdentitySource managedIdentity
break;
case ManagedIdentitySource.Imds:
- case ManagedIdentitySource.ImdsV2:
Environment.SetEnvironmentVariable("AZURE_POD_IDENTITY_AUTHORITY_HOST", endpoint);
break;
diff --git a/src/client/Microsoft.Identity.Lab.Api/Http/MockHelpers.cs b/src/client/Microsoft.Identity.Lab.Api/Http/MockHelpers.cs
index ecc4643f96..0deddf4bf9 100644
--- a/src/client/Microsoft.Identity.Lab.Api/Http/MockHelpers.cs
+++ b/src/client/Microsoft.Identity.Lab.Api/Http/MockHelpers.cs
@@ -948,6 +948,50 @@ internal static MockHttpMessageHandler MockImdsProbeFailure(
return MockImdsProbe(imdsVersion, userAssignedIdentityId, userAssignedId, success: false, retry: retry);
}
+ ///
+ /// Creates a mock IMDS compute metadata (/metadata/instance/compute) response handler.
+ ///
+ /// The OS type to return (e.g., "Windows", "Linux").
+ /// The security profile type (e.g., "TrustedLaunch", "ConfidentialVM"), or null for no profile.
+ /// A configured .
+ internal static MockHttpMessageHandler MockImdsComputeMetadata(
+ string osType = "Windows",
+ string securityType = "TrustedLaunch")
+ {
+ string securityProfileJson = securityType != null
+ ? $", \"securityProfile\": {{ \"securityType\": \"{securityType}\" }}"
+ : "";
+
+ string body = $"{{ \"osType\": \"{osType}\"{securityProfileJson} }}";
+
+ return new MockHttpMessageHandler()
+ {
+ ExpectedUrl = $"{ImdsManagedIdentitySource.DefaultImdsBaseEndpoint}{ImdsComputeMetadataManager.ImdsComputePath}",
+ ExpectedMethod = HttpMethod.Get,
+ ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(body),
+ }
+ };
+ }
+
+ ///
+ /// Creates a mock IMDS compute metadata 404 (not found) response handler.
+ ///
+ /// A configured .
+ internal static MockHttpMessageHandler MockImdsComputeMetadataNotFound()
+ {
+ return new MockHttpMessageHandler()
+ {
+ ExpectedUrl = $"{ImdsManagedIdentitySource.DefaultImdsBaseEndpoint}{ImdsComputeMetadataManager.ImdsComputePath}",
+ ExpectedMethod = HttpMethod.Get,
+ ResponseMessage = new HttpResponseMessage(HttpStatusCode.NotFound)
+ {
+ Content = new StringContent(""),
+ }
+ };
+ }
+
///
/// Creates a mock CSR metadata response handler for IMDS v2 flows.
///
diff --git a/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/AppServiceTests.cs b/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/AppServiceTests.cs
index ac75ca0e92..b233dfd0c1 100644
--- a/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/AppServiceTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/AppServiceTests.cs
@@ -70,7 +70,7 @@ public async Task TestAppServiceUpgradeScenario(
ManagedIdentityApplication mi = miBuilder.Build() as ManagedIdentityApplication;
- var miSourceResult = await mi.GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ var miSourceResult = await mi.GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
Assert.AreEqual(expectedManagedIdentitySource, miSourceResult.Source);
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs b/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs
index 7f48193be8..ea586bc078 100644
--- a/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs
@@ -132,7 +132,9 @@ private async Task CreateManagedIdentityAsync(
if (addSourceCheck)
{
- var miSourceResultV1 = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ // V1 success triggers a compute-metadata call to determine host binding strength.
+ httpManager.AddMockHandler(MockHelpers.MockImdsComputeMetadata());
+ var miSourceResultV1 = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
Assert.AreEqual(ManagedIdentitySource.Imds, miSourceResultV1.Source);
}
@@ -145,13 +147,9 @@ private async Task CreateManagedIdentityAsync(
httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V2, userAssignedIdentityId, userAssignedId));
}
- if (addSourceCheck)
- {
- var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
- Assert.AreEqual(ManagedIdentitySource.ImdsV2, miSourceResult.Source);
- }
-
- // Choose deterministic key source for tests.
+ // Choose deterministic key source for tests. This must be injected BEFORE discovery runs,
+ // because IMDSv2 capability discovery probes the key provider to determine whether the host
+ // can produce a KeyGuard key (and therefore advertise the KeyGuard binding-strength tier).
IManagedIdentityKeyProvider managedIdentityKeyProvider = keyProvider;
if (managedIdentityKeyProvider == null)
{
@@ -177,6 +175,12 @@ private async Task CreateManagedIdentityAsync(
.ServiceBundle.SetPlatformProxyForTest(platformProxy);
}
+ if (addSourceCheck)
+ {
+ var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ Assert.AreEqual(ManagedIdentitySource.Imds, miSourceResult.Source);
+ }
+
return managedIdentityApp;
}
@@ -193,7 +197,7 @@ public async Task mTLSPopTokenHappyPath(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, userAssignedIdentityId, userAssignedId, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -235,7 +239,7 @@ public async Task mTLSPopTokenIsPerIdentity(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
#region Identity 1
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, userAssignedIdentityId, userAssignedId, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -316,7 +320,7 @@ public async Task mTLSPopTokenIsReAcquiredWhenCertificateIsExpired(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, userAssignedIdentityId, userAssignedId, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -448,7 +452,7 @@ public async Task ApplicationsCanSwitchBetweenImdsVersions(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, userAssignedIdentityId, userAssignedId, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -470,8 +474,8 @@ public async Task ApplicationsCanSwitchBetweenImdsVersions(
Assert.AreEqual(Bearer, bearerResult.TokenType);
Assert.AreEqual(TokenSource.IdentityProvider, bearerResult.AuthenticationResultMetadata.TokenSource);
- var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
- Assert.AreEqual(ManagedIdentitySource.ImdsV2, miSourceResult.Source);
+ var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ Assert.AreEqual(ManagedIdentitySource.Imds, miSourceResult.Source);
// Arrange: mocks for the IMDSv2 mTLS PoP token request
AddMocksToGetEntraToken(httpManager, userAssignedIdentityId, userAssignedId);
@@ -498,7 +502,7 @@ public async Task ProbeImdsEndpointAsyncSucceeds()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Discovery order: V2 probed first (succeeds)
httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V2));
@@ -513,7 +517,7 @@ public async Task ProbeImdsEndpointAsyncSucceedsAfterRetry()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Discovery order: V2 probed first (first attempt fails with retry, second succeeds)
// `retry: true` indicates a retriable status code will be returned
@@ -531,7 +535,7 @@ public async Task ProbeImdsEndpointAsyncFails404WhichIsNonRetriableAndRetryPolic
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Discovery order: V2 probed first (fails with non-retriable 404), then V1 (succeeds)
// `retry: false` indicates a non-retriable status code (404) will be returned for V2
@@ -540,7 +544,10 @@ public async Task ProbeImdsEndpointAsyncFails404WhichIsNonRetriableAndRetryPolic
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, addProbeMock: false, addSourceCheck: false).ConfigureAwait(false);
- var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ // V1 success triggers a compute-metadata call to determine host binding strength.
+ httpManager.AddMockHandler(MockHelpers.MockImdsComputeMetadata());
+
+ var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
Assert.AreEqual(ManagedIdentitySource.Imds, miSourceResult.Source);
}
}
@@ -567,7 +574,7 @@ public async Task ImdsProbeEndpointAsync_TimeOutThrowsOperationCanceledException
var imdsProbesCancellationToken = cts.Token;
await Assert.ThrowsAsync(async () =>
- await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(imdsProbesCancellationToken)
+ await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(imdsProbesCancellationToken)
.ConfigureAwait(false))
.ConfigureAwait(false);
}
@@ -587,7 +594,7 @@ public async Task NonMtlsRequest_FallsBackToImdsV1(
using (var httpManager = new MockHttpManager())
{
ManagedIdentityClient.ResetSourceForTest();
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, userAssignedIdentityId, userAssignedId, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -609,8 +616,8 @@ public async Task NonMtlsRequest_FallsBackToImdsV1(
Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource);
// indicates ImdsV2 is still available
- var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
- Assert.AreEqual(ManagedIdentitySource.ImdsV2, miSourceResult.Source);
+ var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ Assert.AreEqual(ManagedIdentitySource.Imds, miSourceResult.Source);
}
}
@@ -620,7 +627,7 @@ public async Task BothImdsProbesFailMaxRetries_ReturnsNoneFound()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Discovery order: V2 probed first (fails with max retries), then V1 (fails) → NoneFound
const int Num500Errors = 1 + TestImdsProbeRetryPolicy.ExponentialStrategyNumRetries;
@@ -634,7 +641,7 @@ public async Task BothImdsProbesFailMaxRetries_ReturnsNoneFound()
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager, addProbeMock: false, addSourceCheck: false).ConfigureAwait(false);
- var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentitySourceAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
+ var miSourceResult = await (managedIdentityApp as ManagedIdentityApplication).GetManagedIdentityCapabilitiesAsync(ManagedIdentityTests.ImdsProbesCancellationToken).ConfigureAwait(false);
Assert.AreEqual(ManagedIdentitySource.None, miSourceResult.Source);
}
}
@@ -647,7 +654,7 @@ public async Task GetCsrMetadataAsyncFailsWithMissingServerHeader()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager).ConfigureAwait(false);
@@ -669,7 +676,7 @@ public async Task GetCsrMetadataAsyncFailsWithInvalidFormat()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var managedIdentityApp = await CreateManagedIdentityAsync(httpManager).ConfigureAwait(false);
@@ -764,7 +771,7 @@ public async Task MtlsPop_NoAttestationProvider_UsesNonAttestedFlow()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var mi = await CreateManagedIdentityAsync(httpManager, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -790,7 +797,7 @@ public async Task MtlsPop_AttestationProviderReturnsNull_UsesNonAttestedFlow()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var mi = await CreateManagedIdentityAsync(httpManager, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -815,7 +822,7 @@ public async Task MtlsPop_AttestationProviderReturnsEmptyToken_UsesNonAttestedFl
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var mi = await CreateManagedIdentityAsync(httpManager, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -840,7 +847,7 @@ public async Task mTLSPop_RequestedWithoutKeyGuard_ThrowsClientException()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Force in-memory keys (i.e., not KeyGuard)
var managedIdentityApp = await CreateManagedIdentityAsync(
@@ -872,7 +879,7 @@ public async Task mTLSPop_AttestationSupport_IsRequired_ToReusePersistedCert()
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Force KeyGuard provider so attestation support is relevant.
var mi = await CreateManagedIdentityAsync(
@@ -923,7 +930,7 @@ public async Task mTLSPop_AttestationSupport_AffectsTokenCacheKey_NonAttestedReq
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Force KeyGuard provider so attestation support is relevant.
var mi = await CreateManagedIdentityAsync(
@@ -983,7 +990,7 @@ public async Task MaaTokenCache_Hit_DoesNotCallAttestationProviderAgain()
using (var httpManager = new MockHttpManager())
{
ManagedIdentityClient.ResetSourceForTest();
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var rawCert = CreateRawCertForCsrKeyWithCnDc(
Constants.ManagedIdentityDefaultClientId, TestConstants.TenantId,
@@ -1054,7 +1061,7 @@ public async Task MaaTokenCache_ExpiredToken_CallsAttestationProviderAgain()
using (var httpManager = new MockHttpManager())
{
ManagedIdentityClient.ResetSourceForTest();
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var rawCert = CreateRawCertForCsrKeyWithCnDc(
Constants.ManagedIdentityDefaultClientId, TestConstants.TenantId,
@@ -1247,7 +1254,7 @@ public async Task MaaTokenCache_CertCacheMiss_MaaTokenCacheStillFresh_DoesNotCal
using (var httpManager = new MockHttpManager())
{
ManagedIdentityClient.ResetSourceForTest();
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var rawCert = CreateRawCertForCsrKeyWithCnDc(
Constants.ManagedIdentityDefaultClientId, TestConstants.TenantId,
@@ -1298,7 +1305,7 @@ public async Task mTLSPop_ForceRefresh_UsesCachedCert_NoIssueCredential_PostsCan
using (var httpManager = new MockHttpManager())
{
// Start clean across tests
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
var mi = await CreateManagedIdentityAsync(httpManager, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -1352,7 +1359,7 @@ public async Task mTLSPop_CachedCertIsPerIdentity_OnRefresh_Identity1UsesCache_I
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Identity 1 – first acquire (mint)
var mi1 = await CreateManagedIdentityAsync(httpManager, userAssignedIdentityId, userAssignedId1, managedIdentityKeyType: ManagedIdentityKeyType.KeyGuard).ConfigureAwait(false);
@@ -1412,7 +1419,7 @@ public async Task mTLSPopTokenHappyPath_LongLivedCert_IdentityMapping(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Force KeyGuard so the PoP path is taken
var managedIdentityApp = await CreateManagedIdentityAsync(
@@ -1480,7 +1487,7 @@ public async Task mTLSPop_LongLivedCerts_SamiVsUami_DistinctAndCached(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Create the two test certs (20-year) from the SAME RSA as CSR (XmlPrivateKey)
string rawCertSami = CreateRawCertFromXml("CN=SAMI-20Y", notAfterUtc: DateTimeOffset.UtcNow.AddYears(20));
@@ -1545,7 +1552,7 @@ public async Task mTLSPop_LongLivedCerts_SamiAndUami_ThumbprintsDiffer_AndEachCa
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Make two long-lived certs **from the CSR key** so AttachPrivateKey succeeds
string rawCertSami = CreateRawCertForCsrKey("CN=SAMI-20Y", DateTimeOffset.UtcNow.AddYears(20));
@@ -1643,7 +1650,7 @@ public async Task mTLSPop_SubjectCnDc_MatchesMetadata_AndCaches(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// Expected mapping (mirrors your live logs)
string expectedCn = isUami ? TestConstants.ClientId : Constants.ManagedIdentityDefaultClientId;
@@ -1680,7 +1687,7 @@ public async Task mTLSPoP_Uami_ClientIdThenObjectId_MintsThenCaches_SubjectCNIsC
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
string expectedCn = TestConstants.ClientId;
string expectedDc = TestConstants.TenantId;
@@ -1756,7 +1763,7 @@ public async Task mTLSPoP_Uami_ClientIdThenAlias_MintsThenCaches_SubjectCNIsClie
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
string expectedCn = TestConstants.ClientId;
string expectedDc = TestConstants.TenantId;
@@ -1834,7 +1841,7 @@ public async Task mTLSPop_ShortLivedCert_LessThan24h_NotCached_ReMints(
using (new EnvVariableContext())
using (var httpManager = new MockHttpManager())
{
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// short-lived cert #1: < 24h => must NOT be cached
var rawShort1 = CreateRawCertForCsrKeyWithCnDc(
@@ -1889,7 +1896,7 @@ public async Task mTLSPop_CertAtLeast24h_IsCached_ReusedOnSecondAcquire(
using (var httpManager = new MockHttpManager())
{
ManagedIdentityClient.ResetSourceForTest();
- SetEnvironmentVariables(ManagedIdentitySource.ImdsV2, TestConstants.ImdsEndpoint);
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, TestConstants.ImdsEndpoint);
// NotAfter >= 24h + 1min ? should be cached and reused
var rawLong = CreateRawCertForCsrKeyWithCnDc(
diff --git a/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ManagedIdentityTests.cs b/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ManagedIdentityTests.cs
index 9d85275562..cabe371ad7 100644
--- a/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ManagedIdentityTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ManagedIdentityTests.cs
@@ -13,13 +13,16 @@
using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.Internal;
using Microsoft.Identity.Client.ManagedIdentity;
+using Microsoft.Identity.Client.ManagedIdentity.KeyProviders;
using Microsoft.Identity.Client.ManagedIdentity.V2;
+using Microsoft.Identity.Client.PlatformsCommon.Interfaces;
using Microsoft.Identity.Client.TelemetryCore.Internal.Events;
using Microsoft.Identity.Test.Common;
using Microsoft.Identity.Test.Common.Core.Helpers;
using Microsoft.Identity.Test.Common.Core.Mocks;
using Microsoft.Identity.Test.Unit.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NSubstitute;
using static Microsoft.Identity.Test.Common.Core.Helpers.ManagedIdentityTestUtil;
namespace Microsoft.Identity.Test.Unit.ManagedIdentityTests
@@ -44,13 +47,21 @@ public class ManagedIdentityTests : TestBase
private readonly TestRetryPolicyFactory _testRetryPolicyFactory = new TestRetryPolicyFactory();
+ // Injects a test platform proxy that returns the supplied key provider, so that IMDSv2
+ // capability discovery deterministically resolves the host's binding strength (KeyGuard vs
+ // software) instead of probing the real platform key provider.
+ private static void InjectKeyProvider(ManagedIdentityApplication mi, IManagedIdentityKeyProvider keyProvider)
+ {
+ var platformProxy = Substitute.For();
+ platformProxy.ManagedIdentityKeyProvider.Returns(keyProvider);
+ mi.ServiceBundle.SetPlatformProxyForTest(platformProxy);
+ }
+
[TestMethod]
[DataRow("http://127.0.0.1:41564/msi/token/", ManagedIdentitySource.AppService)]
[DataRow(AppServiceEndpoint, ManagedIdentitySource.AppService)]
[DataRow(ImdsEndpoint, ManagedIdentitySource.Imds)]
[DataRow(null, ManagedIdentitySource.Imds)]
- [DataRow(ImdsEndpoint, ManagedIdentitySource.ImdsV2)]
- [DataRow(null, ManagedIdentitySource.ImdsV2)]
[DataRow(AzureArcEndpoint, ManagedIdentitySource.AzureArc)]
[DataRow(CloudShellEndpoint, ManagedIdentitySource.CloudShell)]
[DataRow(ServiceFabricEndpoint, ManagedIdentitySource.ServiceFabric)]
@@ -69,20 +80,94 @@ public async Task GetManagedIdentityTests(
ManagedIdentityApplication mi = miBuilder.Build() as ManagedIdentityApplication;
- if (managedIdentitySource == ManagedIdentitySource.ImdsV2)
- {
- // Discovery order: V2 probed first (succeeds)
- httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V2));
- }
- else if (managedIdentitySource == ManagedIdentitySource.Imds)
+ if (managedIdentitySource == ManagedIdentitySource.Imds)
{
- // Discovery order: V2 probed first (fails), then V1 (succeeds)
+ // Discovery order: V2 probed first (fails), then V1 (succeeds), then compute metadata for capability.
httpManager.AddMockHandler(MockHelpers.MockImdsProbeFailure(ImdsVersion.V2));
httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V1));
+ httpManager.AddMockHandler(MockHelpers.MockImdsComputeMetadata());
}
- var miSourceResult = await mi.GetManagedIdentitySourceAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
- Assert.AreEqual(managedIdentitySource, miSourceResult.Source);
+ var caps = await mi.GetManagedIdentityCapabilitiesAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
+ Assert.AreEqual(managedIdentitySource, caps.Source);
+ }
+ }
+
+ [TestMethod]
+ public async Task GetManagedIdentityCapabilities_ImdsV2Detected_ReportsSoftwarePoPAsync()
+ {
+ using (new EnvVariableContext())
+ using (var httpManager = new MockHttpManager())
+ {
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, ImdsEndpoint);
+
+ var mi = ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.SystemAssigned)
+ .WithHttpManager(httpManager)
+ .Build() as ManagedIdentityApplication;
+
+ // A software-backed key provider (no VBS/KeyGuard) caps the host at the Software tier.
+ InjectKeyProvider(mi, new InMemoryManagedIdentityKeyProvider());
+
+ // Discovery order: V2 probed first and succeeds, signalling host PoP capability.
+ httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V2));
+
+ var caps = await mi.GetManagedIdentityCapabilitiesAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
+
+ Assert.AreEqual(ManagedIdentitySource.Imds, caps.Source);
+ Assert.AreEqual(MtlsBindingStrength.Software, caps.MaxSupportedBindingStrength);
+ Assert.IsTrue(caps.IsMtlsPopSupportedByHost);
+ }
+ }
+
+ [TestMethod]
+ public async Task GetManagedIdentityCapabilities_ImdsV2KeyGuard_ReportsKeyGuardAsync()
+ {
+ using (new EnvVariableContext())
+ using (var httpManager = new MockHttpManager())
+ {
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, ImdsEndpoint);
+
+ var mi = ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.SystemAssigned)
+ .WithHttpManager(httpManager)
+ .Build() as ManagedIdentityApplication;
+
+ // A KeyGuard-capable key provider (VBS-isolated key) upgrades the host to the
+ // attested KeyGuard tier.
+ InjectKeyProvider(mi, new TestKeyGuardManagedIdentityKeyProvider());
+
+ // Discovery order: V2 probed first and succeeds, signalling host PoP capability.
+ httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V2));
+
+ var caps = await mi.GetManagedIdentityCapabilitiesAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
+
+ Assert.AreEqual(ManagedIdentitySource.Imds, caps.Source);
+ Assert.AreEqual(MtlsBindingStrength.KeyGuard, caps.MaxSupportedBindingStrength);
+ Assert.IsTrue(caps.IsMtlsPopSupportedByHost);
+ }
+ }
+
+ [TestMethod]
+ public async Task GetManagedIdentityCapabilities_ImdsV1NonTvm_ReportsBearerAsync()
+ {
+ using (new EnvVariableContext())
+ using (var httpManager = new MockHttpManager())
+ {
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, ImdsEndpoint);
+
+ var mi = ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.SystemAssigned)
+ .WithHttpManager(httpManager)
+ .Build() as ManagedIdentityApplication;
+
+ // V2 fails, V1 succeeds, compute metadata reports a non-TVM/CVM host (no security profile).
+ httpManager.AddMockHandler(MockHelpers.MockImdsProbeFailure(ImdsVersion.V2));
+ httpManager.AddMockHandler(MockHelpers.MockImdsProbe(ImdsVersion.V1));
+ httpManager.AddMockHandler(MockHelpers.MockImdsComputeMetadata(osType: "Linux", securityType: null));
+
+ var caps = await mi.GetManagedIdentityCapabilitiesAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
+
+ Assert.AreEqual(ManagedIdentitySource.Imds, caps.Source);
+ Assert.AreEqual(MtlsBindingStrength.None, caps.MaxSupportedBindingStrength);
+ Assert.IsFalse(caps.IsMtlsPopSupportedByHost);
}
}
@@ -1132,7 +1217,7 @@ public async Task UnavailableManagedIdentitySource_ThrowsExceptionDuringTokenAcq
httpManager.AddMockHandler(MockHelpers.MockImdsProbeFailure(ImdsVersion.V2));
httpManager.AddMockHandler(MockHelpers.MockImdsProbeFailure(ImdsVersion.V1));
- var sourceResult = await mi.GetManagedIdentitySourceAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
+ var sourceResult = await mi.GetManagedIdentityCapabilitiesAsync(ImdsProbesCancellationToken).ConfigureAwait(false);
Assert.AreEqual(ManagedIdentitySource.None, sourceResult.Source);
// Token acquisition uses cached NoneFound and throws AllSourcesUnavailable
@@ -1370,7 +1455,6 @@ public async Task ManagedIdentityWithCapabilitiesTestAsync(
[DataRow(ManagedIdentitySource.AzureArc)]
[DataRow(ManagedIdentitySource.CloudShell)]
[DataRow(ManagedIdentitySource.Imds)]
- [DataRow(ManagedIdentitySource.ImdsV2)]
[DataRow(ManagedIdentitySource.ServiceFabric)]
[DataRow(ManagedIdentitySource.MachineLearning)]
public void ValidateServerCertificate_OnlySetForServiceFabric(ManagedIdentitySource managedIdentitySource)
@@ -1407,6 +1491,26 @@ public void ValidateServerCertificate_OnlySetForServiceFabric(ManagedIdentitySou
}
}
+ [TestMethod]
+ public void ValidateServerCertificate_NotSetForImdsV2()
+ {
+ using (new EnvVariableContext())
+ using (var httpManager = new MockHttpManager())
+ {
+ SetEnvironmentVariables(ManagedIdentitySource.Imds, "https://identity.endpoint.com");
+
+ var managedIdentityApp = ManagedIdentityApplicationBuilder.Create(ManagedIdentityId.SystemAssigned)
+ .WithHttpManager(httpManager)
+ .BuildConcrete();
+ RequestContext requestContext = new RequestContext(managedIdentityApp.ServiceBundle, Guid.NewGuid(), null);
+
+ AbstractManagedIdentity managedIdentity = new ImdsV2ManagedIdentitySource(requestContext);
+
+ Assert.IsNull(managedIdentity.GetValidationCallback(),
+ "IMDSv2 source should not have ValidateServerCertificate set");
+ }
+ }
+
private AbstractManagedIdentity CreateManagedIdentitySource(ManagedIdentitySource sourceType, MockHttpManager httpManager)
{
string endpoint = "https://identity.endpoint.com";
@@ -1437,9 +1541,6 @@ private AbstractManagedIdentity CreateManagedIdentitySource(ManagedIdentitySourc
case ManagedIdentitySource.Imds:
managedIdentity = new ImdsManagedIdentitySource(requestContext);
break;
- case ManagedIdentitySource.ImdsV2:
- managedIdentity = new ImdsV2ManagedIdentitySource(requestContext);
- break;
case ManagedIdentitySource.MachineLearning:
managedIdentity = MachineLearningManagedIdentitySource.Create(requestContext);
break;