Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class ManagedIdentityAttestationExtensions
{
/// <summary>
/// Enables Credential Guard attestation support for managed identity mTLS Proof-of-Possession flows.
/// This method should be called after <see cref="ManagedIdentityPopExtensions.WithMtlsProofOfPossession"/>.
/// This method should be called after <see cref="ManagedIdentityPopExtensions.WithMtlsProofOfPossession(AcquireTokenForManagedIdentityParameterBuilder)"/>.
/// </summary>
/// <param name="builder">The AcquireTokenForManagedIdentityParameterBuilder instance.</param>
/// <returns>The builder to chain .With methods.</returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public sealed class AcquireTokenForManagedIdentityParameterBuilder :
AbstractManagedIdentityAcquireTokenParameterBuilder<AcquireTokenForManagedIdentityParameterBuilder>
{
private const string MiAttCacheKeyComponent = "mi_att";
private const string MiMinStrengthCacheKeyComponent = "mi_minstrength";
private static readonly Task<string> s_att0 = Task.FromResult("0");
private static readonly Task<string> s_att1 = Task.FromResult("1");

Expand Down Expand Up @@ -130,6 +131,7 @@ private static void ApplyMtlsPopAndAttestation(
AcquireTokenForManagedIdentityParameters acquireTokenForManagedIdentityParameters)
{
acquireTokenForManagedIdentityParameters.IsMtlsPopRequested = acquireTokenCommonParameters.IsMtlsPopRequested;
acquireTokenForManagedIdentityParameters.MtlsPopMinStrength = acquireTokenCommonParameters.MtlsPopMinStrength;
acquireTokenForManagedIdentityParameters.AttestationTokenProvider = acquireTokenCommonParameters.AttestationTokenProvider;

// PoP requests should be partitioned by attestation-support mode.
Expand All @@ -140,6 +142,11 @@ private static void ApplyMtlsPopAndAttestation(

acquireTokenCommonParameters.CacheKeyComponents[MiAttCacheKeyComponent] =
_ => acquireTokenCommonParameters.AttestationTokenProvider != null ? s_att1 : s_att0;

// Partition by the requested minimum binding strength so a higher-floor request
// cannot be satisfied from a cache entry created by a lower/no-floor request.
acquireTokenCommonParameters.CacheKeyComponents[MiMinStrengthCacheKeyComponent] =
_ => Task.FromResult(acquireTokenCommonParameters.MtlsPopMinStrength.ToString());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ internal class AcquireTokenCommonParameters
public string FmiPathSuffix { get; internal set; }
public string ClientAssertionFmiPath { get; internal set; }
public bool IsMtlsPopRequested { get; set; }

/// <summary>
/// The minimum mTLS binding strength the host must support for the request to succeed.
/// Set via <see cref="AppConfig.PoPOptions.MinStrength"/>. Defaults to
/// <see cref="MtlsBindingStrength.None"/> (no floor).
/// </summary>
public MtlsBindingStrength MtlsPopMinStrength { get; set; } = MtlsBindingStrength.None;
public string ExtraClientAssertionClaims { get; internal set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.ManagedIdentity;

namespace Microsoft.Identity.Client.ApiConfig.Parameters
Expand All @@ -32,6 +33,12 @@ internal class AcquireTokenForManagedIdentityParameters : IAcquireTokenParameter

public bool IsMtlsPopRequested { get; set; }

/// <summary>
/// The minimum mTLS binding strength the host must support for the request to succeed.
/// Defaults to <see cref="MtlsBindingStrength.None"/> (no floor).
/// </summary>
public MtlsBindingStrength MtlsPopMinStrength { get; set; } = MtlsBindingStrength.None;

internal X509Certificate2 MtlsCertificate { get; set; }

/// <summary>
Expand All @@ -54,6 +61,7 @@ public void LogParameters(ILoggerAdapter logger)
ClientClaims: {!string.IsNullOrEmpty(ClientClaims)}
RevokedTokenHash: {!string.IsNullOrEmpty(RevokedTokenHash)}
IsMtlsPopRequested: {IsMtlsPopRequested}
MtlsPopMinStrength: {MtlsPopMinStrength}
""");
}
}
Expand Down
33 changes: 33 additions & 0 deletions src/client/Microsoft.Identity.Client/AppConfig/PoPOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Identity.Client.AppConfig
{
/// <summary>
/// Options that control mTLS Proof-of-Possession (PoP) token acquisition. This type is the
/// extensibility point for PoP-related knobs so future settings can be added without growing
/// the builder surface.
/// </summary>
/// <remarks>
/// This type is shared by managed identity and confidential client mTLS Proof-of-Possession
/// scenarios.
/// </remarks>
public class PoPOptions
{
/// <summary>
/// Gets or sets the minimum binding strength the host must be able to produce for the
/// request to succeed. This is a <b>floor assertion</b>, not a downgrade selector: MSAL
/// always uses the host's maximum binding strength, and the request fails when the host
/// cannot meet this floor.
/// </summary>
/// <value>
/// The minimum required <see cref="MtlsBindingStrength"/>. The default is
/// <see cref="MtlsBindingStrength.None"/>, which imposes no floor and behaves identically
/// to the parameterless mTLS PoP request. When set to a value greater than
/// <see cref="MtlsBindingStrength.None"/>, the request fails with
/// <see cref="MsalError.MinStrengthNotMet"/> if the host's maximum binding strength is
/// lower than this value.
/// </value>
public MtlsBindingStrength MinStrength { get; set; } = MtlsBindingStrength.None;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public X509Certificate2 MtlsCertificate
}

public bool IsMtlsPopRequested => _commonParameters.IsMtlsPopRequested;
public MtlsBindingStrength MtlsPopMinStrength => _commonParameters.MtlsPopMinStrength;
public bool? SendOfflineAccessScope => _commonParameters.SendOfflineAccessScope;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ internal async Task<ManagedIdentityResponse> SendTokenRequestForManagedIdentityA
AcquireTokenForManagedIdentityParameters parameters,
CancellationToken cancellationToken)
{
// Enforce a minimum binding strength floor when requested via PoPOptions.MinStrength.
// The token-request path normally routes mTLS PoP straight to IMDSv2 without probing,
// so explicitly run discovery to learn the host's maximum binding strength and fail
// fast if the host cannot meet the required floor.
if (parameters.MtlsPopMinStrength > MtlsBindingStrength.None)
{
ManagedIdentityDiscoveryResult discovery =
await GetManagedIdentityCapabilitiesAsync(requestContext, cancellationToken).ConfigureAwait(false);

if (discovery.MaxSupportedBindingStrength < parameters.MtlsPopMinStrength)
{
throw new MsalClientException(
MsalError.MinStrengthNotMet,
MsalErrorMessage.MinStrengthNotMet(discovery.MaxSupportedBindingStrength, parameters.MtlsPopMinStrength));
}
}

AbstractManagedIdentity msi = await GetOrSelectManagedIdentitySourceAsync(requestContext, parameters.IsMtlsPopRequested, cancellationToken).ConfigureAwait(false);
return await msi.AuthenticateAsync(parameters, cancellationToken).ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.PlatformsCommon.Shared;

namespace Microsoft.Identity.Client
Expand All @@ -20,6 +21,31 @@ public static class ManagedIdentityPopExtensions
public static AcquireTokenForManagedIdentityParameterBuilder WithMtlsProofOfPossession(
this AcquireTokenForManagedIdentityParameterBuilder builder)
{
return builder.WithMtlsProofOfPossession(new PoPOptions());
}

/// <summary>
/// Enables mTLS Proof-of-Possession for managed identity token acquisition with additional
/// PoP options, such as a minimum required binding strength.
/// When attestation is required (KeyGuard scenarios), use the Msal.KeyAttestation package
/// and call .WithAttestationSupport() after this method.
/// </summary>
/// <param name="builder">The AcquireTokenForManagedIdentityParameterBuilder instance.</param>
/// <param name="options">
/// PoP options controlling the request. Use <see cref="PoPOptions.MinStrength"/> to require
/// a minimum host binding strength; the request fails with
/// <see cref="MsalError.MinStrengthNotMet"/> when the host cannot meet the floor.
/// </param>
/// <returns>The builder to chain .With methods.</returns>
public static AcquireTokenForManagedIdentityParameterBuilder WithMtlsProofOfPossession(
this AcquireTokenForManagedIdentityParameterBuilder builder,
PoPOptions options)
{
if (options == null)
{
throw new System.ArgumentNullException(nameof(options));
}

if (!DesktopOsHelper.IsWindows())
{
throw new MsalClientException(
Expand All @@ -33,6 +59,7 @@ public static AcquireTokenForManagedIdentityParameterBuilder WithMtlsProofOfPoss
MsalErrorMessage.MtlsNotSupportedForManagedIdentityMessage);
#else
builder.CommonParameters.IsMtlsPopRequested = true;
builder.CommonParameters.MtlsPopMinStrength = options.MinStrength;
return builder;
#endif
}
Expand Down
10 changes: 10 additions & 0 deletions src/client/Microsoft.Identity.Client/MsalError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,16 @@ public static class MsalError
/// </summary>
public const string MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1";

/// <summary>
/// <para>What happened?</para> A minimum mTLS binding strength was requested via
/// <c>PoPOptions.MinStrength</c>, but the current host cannot produce a key binding that
/// meets the required floor.
/// <para>Mitigation:</para> Deploy on a host capable of the required binding strength
/// (for example, a Trusted Launch or Confidential VM for KeyGuard), or lower the requested
/// minimum strength.
/// </summary>
public const string MinStrengthNotMet = "min_strength_not_met";

/// <summary>
/// All managed identity sources are unavailable.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/client/Microsoft.Identity.Client/MsalErrorMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ public static string InvalidTokenProviderResponseValue(string invalidValueName)
public const string InvalidCertificate = "The certificate received from the Imds server is invalid.";
public const string CannotSwitchBetweenImdsVersionsForPreview = "ImdsV2 is currently experimental - A Bearer token has already been received; Please restart the application to receive a mTLS PoP token.";
public const string MtlsPopTokenNotSupportedinImdsV1 = "mTLS Proof of Possession with managed identity is currently in private preview and is not supported on this VM. Ensure you're running on a supported VM image.";

public static string MinStrengthNotMet(object actualStrength, object requiredStrength) =>
$"The host's maximum mTLS binding strength '{actualStrength}' does not meet the required minimum binding strength '{requiredStrength}' specified via PoPOptions.MinStrength.";
public const string ManagedIdentityAllSourcesUnavailable = "All Managed Identity sources are unavailable.";
public const string SendCertificateOverMtlsRequiresCertificate =
"CertificateOptions.SendCertificateOverMtls is only valid with a certificate-based credential " +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Microsoft.Identity.Client.AppConfig.PoPOptions
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.set -> void
Microsoft.Identity.Client.AppConfig.PoPOptions.PoPOptions() -> void
const Microsoft.Identity.Client.MsalError.MinStrengthNotMet = "min_strength_not_met" -> string
static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder, Microsoft.Identity.Client.AppConfig.PoPOptions options) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Microsoft.Identity.Client.AppConfig.PoPOptions
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.set -> void
Microsoft.Identity.Client.AppConfig.PoPOptions.PoPOptions() -> void
const Microsoft.Identity.Client.MsalError.MinStrengthNotMet = "min_strength_not_met" -> string
static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder, Microsoft.Identity.Client.AppConfig.PoPOptions options) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Microsoft.Identity.Client.AppConfig.PoPOptions
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.set -> void
Microsoft.Identity.Client.AppConfig.PoPOptions.PoPOptions() -> void
const Microsoft.Identity.Client.MsalError.MinStrengthNotMet = "min_strength_not_met" -> string
static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder, Microsoft.Identity.Client.AppConfig.PoPOptions options) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Microsoft.Identity.Client.AppConfig.PoPOptions
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.set -> void
Microsoft.Identity.Client.AppConfig.PoPOptions.PoPOptions() -> void
const Microsoft.Identity.Client.MsalError.MinStrengthNotMet = "min_strength_not_met" -> string
static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder, Microsoft.Identity.Client.AppConfig.PoPOptions options) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Microsoft.Identity.Client.AppConfig.PoPOptions
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.set -> void
Microsoft.Identity.Client.AppConfig.PoPOptions.PoPOptions() -> void
const Microsoft.Identity.Client.MsalError.MinStrengthNotMet = "min_strength_not_met" -> string
static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder, Microsoft.Identity.Client.AppConfig.PoPOptions options) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Microsoft.Identity.Client.AppConfig.PoPOptions
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.get -> Microsoft.Identity.Client.AppConfig.MtlsBindingStrength
Microsoft.Identity.Client.AppConfig.PoPOptions.MinStrength.set -> void
Microsoft.Identity.Client.AppConfig.PoPOptions.PoPOptions() -> void
const Microsoft.Identity.Client.MsalError.MinStrengthNotMet = "min_strength_not_met" -> string
static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder, Microsoft.Identity.Client.AppConfig.PoPOptions options) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder
Loading
Loading