diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs index 3d45a15dae..896a5331d7 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenForClientParameterBuilder.cs @@ -94,25 +94,17 @@ public AcquireTokenForClientParameterBuilder WithSendX5C(bool withSendX5C) /// For more information, refer to the Proof-of-Possession documentation. /// /// The current instance of to enable method chaining. + /// + /// The application must be configured with a certificate credential (via WithCertificate) or a client assertion + /// that provides a TokenBindingCertificate. Certificate validation and MTLS setup will be performed during token acquisition. + /// public AcquireTokenForClientParameterBuilder WithMtlsProofOfPossession() { - if (ServiceBundle.Config.ClientCredential is CertificateClientCredential certificateCredential) - { - if (certificateCredential.Certificate == null) - { - throw new MsalClientException( - MsalError.MtlsCertificateNotProvided, - MsalErrorMessage.MtlsCertificateNotProvidedMessage); - } - - CommonParameters.AuthenticationOperation = new MtlsPopAuthenticationOperation(certificateCredential.Certificate); - CommonParameters.MtlsCertificate = certificateCredential.Certificate; - } - CommonParameters.IsMtlsPopRequested = true; return this; } + /// /// Please use WithAzureRegion on the ConfidentialClientApplicationBuilder object /// @@ -193,21 +185,51 @@ internal override Task ExecuteInternalAsync(CancellationTo /// for a comment inside this function for AzureRegion. protected override void Validate() { - if (CommonParameters.MtlsCertificate != null) + base.Validate(); + + // MTLS PoP validation + if (CommonParameters.IsMtlsPopRequested) { - // Check for Azure region only if the authority is AAD - // AzureRegion is by default set to null or set to null when the application is created - // with region set to DisableForceRegion (see ConfidentialClientApplicationBuilder.Validate) - if (ServiceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad && - ServiceBundle.Config.AzureRegion == null) + var credential = ServiceBundle.Config.ClientCredential; + + // Check 1: Credential type - only specific types support MTLS + // CertificateAndClaimsClientCredential (WithClientClaims) does NOT support MTLS + bool supportsMtls = credential is CertificateClientCredential || + credential is ClientAssertionDelegateCredential; + + if (!supportsMtls) { throw new MsalClientException( - MsalError.MtlsPopWithoutRegion, - MsalErrorMessage.MtlsPopWithoutRegion); + MsalError.MtlsCertificateNotProvided, + "MTLS Proof-of-Possession requires a certificate-based credential. " + + "Use WithCertificate() to configure a certificate, or use WithClientAssertion() with a TokenBindingCertificate. " + + "WithClientClaims() is not supported for MTLS PoP."); } - } - base.Validate(); + // Check 2: For CertificateClientCredential, validate cert and region now (fail-fast) + // For ClientAssertionDelegateCredential, defer to runtime (can't check TokenBindingCertificate synchronously) + if (credential is CertificateClientCredential certCred) + { + // Validate cert is provided + if (certCred.Certificate == null) + { + throw new MsalClientException( + MsalError.MtlsCertificateNotProvided, + "MTLS Proof-of-Possession requires a certificate, but WithCertificate() was called with a null certificate."); + } + + // For AAD, validate region is configured + if (ServiceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad && + ServiceBundle.Config.AzureRegion == null) + { + throw new MsalClientException( + MsalError.MtlsPopWithoutRegion, + MsalErrorMessage.MtlsPopWithoutRegion); + } + } + // Note: For ClientAssertionDelegateCredential, cert availability and region checks + // happen at runtime in orchestrator (need to call GetAssertionAsync to check TokenBindingCertificate) + } // Force refresh + AccessTokenHashToRefresh APIs cannot be used together if (Parameters.ForceRefresh && !string.IsNullOrEmpty(Parameters.AccessTokenHashToRefresh)) diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ClientApplicationBaseExecutor.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ClientApplicationBaseExecutor.cs index 0556dd2569..6e98ebbb8c 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ClientApplicationBaseExecutor.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ClientApplicationBaseExecutor.cs @@ -28,7 +28,7 @@ public async Task ExecuteAsync( AcquireTokenSilentParameters silentParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); var requestParameters = await _clientApplicationBase.CreateRequestParametersAsync( commonParameters, @@ -47,7 +47,7 @@ public async Task ExecuteAsync( AcquireTokenByRefreshTokenParameters refreshTokenParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); if (commonParameters.Scopes == null || !commonParameters.Scopes.Any()) { commonParameters.Scopes = new SortedSet diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs index 82b6a0be09..2fd1379069 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ConfidentialClientExecutor.cs @@ -2,18 +2,13 @@ // Licensed under the MIT License. using System; -using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Microsoft.Identity.Client.ApiConfig.Parameters; using Microsoft.Identity.Client.AuthScheme.PoP; -using Microsoft.Identity.Client.Instance.Discovery; -using Microsoft.Identity.Client.Instance.Validation; using Microsoft.Identity.Client.Internal; using Microsoft.Identity.Client.Internal.ClientCredential; using Microsoft.Identity.Client.Internal.Requests; -using Microsoft.Identity.Client.ManagedIdentity; -using Microsoft.Identity.Client.Utils; namespace Microsoft.Identity.Client.ApiConfig.Executors { @@ -37,7 +32,7 @@ public async Task ExecuteAsync( AcquireTokenByAuthorizationCodeParameters authorizationCodeParameters, CancellationToken cancellationToken) { - RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); AuthenticationRequestParameters requestParams = await _confidentialClientApplication.CreateRequestParametersAsync( commonParameters, @@ -59,10 +54,27 @@ public async Task ExecuteAsync( AcquireTokenForClientParameters clientParameters, CancellationToken cancellationToken) { - await commonParameters.InitMtlsPopParametersAsync(ServiceBundle, cancellationToken) - .ConfigureAwait(false); + // Resolve certificate and configure MTLS PoP if requested + ClientCertificateContext certContext = null; + if (commonParameters.IsMtlsPopRequested) + { + certContext = await ClientCertificateOrchestrator.ResolveCertificateAsync( + ServiceBundle.Config.ClientCredential, + ServiceBundle, + isMtlsPopRequested: true, // Already validated by if condition + commonParameters.Claims, + cancellationToken).ConfigureAwait(false); + + // Set authentication operation for MTLS PoP + commonParameters.AuthenticationOperation = new MtlsPopAuthenticationOperation( + certContext.Certificate); + } + + RequestContext requestContext = CreateRequestContextAndLogVersionInfo( + commonParameters.CorrelationId, + certContext?.Certificate, + cancellationToken); - RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); AuthenticationRequestParameters requestParams = await _confidentialClientApplication.CreateRequestParametersAsync( commonParameters, @@ -72,6 +84,12 @@ await commonParameters.InitMtlsPopParametersAsync(ServiceBundle, cancellationTok requestParams.SendX5C = clientParameters.SendX5C ?? false; + // Store resolved certificate context for telemetry and tracking + if (certContext != null) + { + requestParams.CertificateContext = certContext; + } + var handler = new ClientCredentialRequest( ServiceBundle, requestParams, @@ -85,7 +103,7 @@ public async Task ExecuteAsync( AcquireTokenOnBehalfOfParameters onBehalfOfParameters, CancellationToken cancellationToken) { - RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); AuthenticationRequestParameters requestParams = await _confidentialClientApplication.CreateRequestParametersAsync( commonParameters, @@ -110,7 +128,7 @@ public async Task ExecuteAsync( GetAuthorizationRequestUrlParameters authorizationRequestUrlParameters, CancellationToken cancellationToken) { - RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); AuthenticationRequestParameters requestParameters = await _confidentialClientApplication.CreateRequestParametersAsync( commonParameters, @@ -147,7 +165,7 @@ public async Task ExecuteAsync( AcquireTokenByUsernamePasswordParameters usernamePasswordParameters, CancellationToken cancellationToken) { - RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + RequestContext requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); AuthenticationRequestParameters requestParams = await _confidentialClientApplication.CreateRequestParametersAsync( commonParameters, diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ManagedIdentityExecutor.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ManagedIdentityExecutor.cs index c6d70db147..2076a66ab5 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ManagedIdentityExecutor.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/ManagedIdentityExecutor.cs @@ -1,15 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Identity.Client.ApiConfig.Parameters; -using Microsoft.Identity.Client.Instance.Discovery; using Microsoft.Identity.Client.Internal; using Microsoft.Identity.Client.Internal.Requests; -using Microsoft.Identity.Client.ManagedIdentity; -using Microsoft.Identity.Client.Utils; namespace Microsoft.Identity.Client.ApiConfig.Executors { @@ -33,7 +29,7 @@ public async Task ExecuteAsync( AcquireTokenForManagedIdentityParameters managedIdentityParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); var requestParams = await _managedIdentityApplication.CreateRequestParametersAsync( commonParameters, diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/PublicClientExecutor.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/PublicClientExecutor.cs index 28e25abf65..4280b65753 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Executors/PublicClientExecutor.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Executors/PublicClientExecutor.cs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Identity.Client.ApiConfig.Parameters; using Microsoft.Identity.Client.Internal; using Microsoft.Identity.Client.Internal.Requests; -using Microsoft.Identity.Client.UI; namespace Microsoft.Identity.Client.ApiConfig.Executors { @@ -26,7 +24,7 @@ public async Task ExecuteAsync( AcquireTokenInteractiveParameters interactiveParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); AuthenticationRequestParameters requestParams = await _publicClientApplication.CreateRequestParametersAsync( commonParameters, @@ -48,7 +46,7 @@ public async Task ExecuteAsync( AcquireTokenWithDeviceCodeParameters deviceCodeParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); var requestParams = await _publicClientApplication.CreateRequestParametersAsync( commonParameters, @@ -69,7 +67,7 @@ public async Task ExecuteAsync( AcquireTokenByIntegratedWindowsAuthParameters integratedWindowsAuthParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); var requestParams = await _publicClientApplication.CreateRequestParametersAsync( commonParameters, @@ -90,7 +88,7 @@ public async Task ExecuteAsync( AcquireTokenByUsernamePasswordParameters usernamePasswordParameters, CancellationToken cancellationToken) { - var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, commonParameters.MtlsCertificate, cancellationToken); + var requestContext = CreateRequestContextAndLogVersionInfo(commonParameters.CorrelationId, null, cancellationToken); var requestParams = await _publicClientApplication.CreateRequestParametersAsync( commonParameters, diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs index 28e732efa6..cc477fc5ef 100644 --- a/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs +++ b/src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenCommonParameters.cs @@ -35,7 +35,6 @@ internal class AcquireTokenCommonParameters public IDictionary ExtraHttpHeaders { get; set; } public PoPAuthenticationConfiguration PopAuthenticationConfiguration { get; set; } public IList> OnBeforeTokenRequestHandler { get; internal set; } - public X509Certificate2 MtlsCertificate { get; internal set; } public List AdditionalCacheParameters { get; set; } public SortedList>> CacheKeyComponents { get; internal set; } public string FmiPathSuffix { get; internal set; } @@ -49,74 +48,5 @@ internal class AcquireTokenCommonParameters /// Returns null for non-attested flows. /// public Func> AttestationTokenProvider { get; set; } - - internal async Task InitMtlsPopParametersAsync(IServiceBundle serviceBundle, CancellationToken ct) - { - if (!IsMtlsPopRequested) - { - return; // PoP not requested - } - - // ──────────────────────────────────── - // Case 1 – Certificate credential - // ──────────────────────────────────── - if (serviceBundle.Config.ClientCredential is CertificateClientCredential certCred) - { - if (certCred.Certificate == null) - { - throw new MsalClientException( - MsalError.MtlsCertificateNotProvided, - MsalErrorMessage.MtlsCertificateNotProvidedMessage); - } - - return; - } - - // ──────────────────────────────────── - // Case 2 – Client‑assertion delegate - // ──────────────────────────────────── - if (serviceBundle.Config.ClientCredential is ClientAssertionDelegateCredential cadc) - { - var opts = new AssertionRequestOptions - { - ClientID = serviceBundle.Config.ClientId, - ClientCapabilities = serviceBundle.Config.ClientCapabilities, - Claims = Claims, - CancellationToken = ct - }; - - ClientSignedAssertion ar = await cadc.GetAssertionAsync(opts, ct).ConfigureAwait(false); - - if (ar.TokenBindingCertificate == null) - { - throw new MsalClientException( - MsalError.MtlsCertificateNotProvided, - MsalErrorMessage.MtlsCertificateNotProvidedMessage); - } - - InitMtlsPopParameters(ar.TokenBindingCertificate, serviceBundle); - return; - } - - // ──────────────────────────────────── - // Case 3 – Any other credential (client‑secret etc.) - // ──────────────────────────────────── - throw new MsalClientException( - MsalError.MtlsCertificateNotProvided, - MsalErrorMessage.MtlsCertificateNotProvidedMessage); - } - - private void InitMtlsPopParameters(X509Certificate2 cert, IServiceBundle serviceBundle) - { - // region check (AAD only) - if (serviceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad && - serviceBundle.Config.AzureRegion == null) - { - throw new MsalClientException(MsalError.MtlsPopWithoutRegion, MsalErrorMessage.MtlsPopWithoutRegion); - } - - AuthenticationOperation = new MtlsPopAuthenticationOperation(cert); - MtlsCertificate = cert; - } } } diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AssertionRequestOptions.cs b/src/client/Microsoft.Identity.Client/AppConfig/AssertionRequestOptions.cs index 4cacac7cb2..4fcf703e49 100644 --- a/src/client/Microsoft.Identity.Client/AppConfig/AssertionRequestOptions.cs +++ b/src/client/Microsoft.Identity.Client/AppConfig/AssertionRequestOptions.cs @@ -76,5 +76,11 @@ internal AssertionRequestOptions(ApplicationConfiguration appConfig, string toke /// FMI path to be used for client assertion. Tokens are associated with this path in the cache. /// public string ClientAssertionFmiPath { get; set; } + + /// + /// Indicates whether MTLS Proof-of-Possession is requested for this authentication operation. + /// When true, the certificate (if provided) will be used for TLS binding rather than just JWT signing. + /// + public bool IsMtlsPopRequested { get; set; } } } diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/CertificateAndClaimsClientCredential.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/CertificateAndClaimsClientCredential.cs index cc7480c734..17b6c8bd8f 100644 --- a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/CertificateAndClaimsClientCredential.cs +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/CertificateAndClaimsClientCredential.cs @@ -14,7 +14,7 @@ namespace Microsoft.Identity.Client.Internal.ClientCredential { - internal class CertificateAndClaimsClientCredential : IClientCredential + internal class CertificateAndClaimsClientCredential : IClientCredential, IClientCertificateProvider { private readonly IDictionary _claimsToSign; private readonly bool _appendDefaultClaims = true; @@ -60,16 +60,42 @@ public async Task AddConfidentialClientParametersAsync( // Log the incoming request parameters for diagnostic purposes requestParameters.RequestContext.Logger.Verbose(() => $"Building assertion from certificate with clientId: {clientId} at endpoint: {tokenEndpoint}"); - if (requestParameters.MtlsCertificate == null) + // Resolve certificate if not already resolved by orchestrator + // Orchestrator sets CertificateContext when MTLS is requested, but this method is also called + // in non-MTLS scenarios where cert needs to be resolved for JWT signing + if (requestParameters.CertificateContext == null) { - requestParameters.RequestContext.Logger.Verbose(() => "Proceeding with JWT token creation and adding client assertion."); + requestParameters.RequestContext.Logger.Verbose(() => "Certificate not yet resolved. Resolving from provider for JWT signing."); + + // Build options for certificate resolution + var options = new AssertionRequestOptions( + requestParameters.AppConfig, + tokenEndpoint, + requestParameters.AuthorityManager.Authority.TenantId) + { + Claims = requestParameters.Claims, + ClientCapabilities = requestParameters.AppConfig.ClientCapabilities, + IsMtlsPopRequested = false, // Non-MTLS path - cert used for JWT signing + CancellationToken = cancellationToken + }; + + // Resolve certificate context via interface method + ClientCertificateContext certContext = await GetCertificateAsync(options, cancellationToken).ConfigureAwait(false); + + // Validate the resolved certificate (GetCertificateAsync returns null if provider returns null) + ValidateCertificate(certContext?.Certificate, requestParameters.RequestContext.Logger); + + requestParameters.CertificateContext = certContext; + } - // Resolve the certificate via the provider - X509Certificate2 certificate = await ResolveCertificateAsync(requestParameters, tokenEndpoint, cancellationToken).ConfigureAwait(false); + // Check if MTLS PoP is being used (cert already in context with MTLS usage) + bool isMtlsPoP = requestParameters.CertificateContext.Usage == ClientCertificateUsage.MtlsBinding; - // Store the resolved certificate in request parameters for later use (e.g., ExecutionResult) - requestParameters.ResolvedCertificate = certificate; + if (!isMtlsPoP) + { + requestParameters.RequestContext.Logger.Verbose(() => "Proceeding with JWT token creation and adding client assertion."); + X509Certificate2 certificate = requestParameters.CertificateContext.Certificate; bool useSha2 = requestParameters.AuthorityManager.Authority.AuthorityInfo.IsSha2CredentialSupported; JsonWebToken jwtToken; @@ -101,48 +127,21 @@ public async Task AddConfidentialClientParametersAsync( { // Log that MTLS PoP is required and JWT token creation is skipped requestParameters.RequestContext.Logger.Verbose(() => "MTLS PoP Client credential request. Skipping client assertion."); - - // Store the mTLS certificate in request parameters for later use (e.g., ExecutionResult) - requestParameters.ResolvedCertificate = requestParameters.MtlsCertificate; } } /// - /// Resolves the certificate to use for signing the client assertion. - /// Invokes the certificate provider delegate to get the certificate. + /// Validates that a certificate is suitable for use in authentication. /// - /// The authentication request parameters containing app config - /// The token endpoint URL - /// Cancellation token for the async operation - /// The X509Certificate2 to use for signing - /// Thrown if the certificate provider returns null or an invalid certificate - private async Task ResolveCertificateAsync( - AuthenticationRequestParameters requestParameters, - string tokenEndpoint, - CancellationToken cancellationToken) + /// The certificate to validate + /// Logger for diagnostic messages + /// Thrown if certificate is null or invalid + private static void ValidateCertificate(X509Certificate2 certificate, ILoggerAdapter logger) { - requestParameters.RequestContext.Logger.Verbose( - () => "[CertificateAndClaimsClientCredential] Resolving certificate from provider."); - - // Create AssertionRequestOptions for the callback - var options = new AssertionRequestOptions( - requestParameters.AppConfig, - tokenEndpoint, - requestParameters.AuthorityManager.Authority.TenantId) - { - Claims = requestParameters.Claims, - ClientCapabilities = requestParameters.AppConfig.ClientCapabilities, - CancellationToken = cancellationToken - }; - - // Invoke the provider to get the certificate - X509Certificate2 certificate = await _certificateProvider(options).ConfigureAwait(false); - // Validate the certificate returned by the provider if (certificate == null) { - requestParameters.RequestContext.Logger.Error( - "[CertificateAndClaimsClientCredential] Certificate provider returned null."); + logger.Error("[CertificateAndClaimsClientCredential] Certificate provider returned null."); throw new MsalClientException( MsalError.InvalidClientAssertion, @@ -153,8 +152,7 @@ private async Task ResolveCertificateAsync( { if (!certificate.HasPrivateKey) { - requestParameters.RequestContext.Logger.Error( - "[CertificateAndClaimsClientCredential] Certificate from provider does not have a private key."); + logger.Error("[CertificateAndClaimsClientCredential] Certificate does not have a private key."); throw new MsalClientException( MsalError.CertWithoutPrivateKey, @@ -163,8 +161,7 @@ private async Task ResolveCertificateAsync( } catch (System.Security.Cryptography.CryptographicException ex) { - requestParameters.RequestContext.Logger.Error( - "[CertificateAndClaimsClientCredential] A cryptographic error occurred while accessing the certificate."); + logger.Error("[CertificateAndClaimsClientCredential] A cryptographic error occurred while accessing the certificate."); throw new MsalClientException( MsalError.CryptographicError, @@ -172,11 +169,32 @@ private async Task ResolveCertificateAsync( ex); } - requestParameters.RequestContext.Logger.Info( - () => $"[CertificateAndClaimsClientCredential] Successfully resolved certificate from provider. " + - $"Thumbprint: {certificate.Thumbprint}"); + logger.Info(() => $"[CertificateAndClaimsClientCredential] Successfully validated certificate. Thumbprint: {certificate.Thumbprint}"); + } + + public async Task GetCertificateAsync( + AssertionRequestOptions options, + CancellationToken cancellationToken) + { + // Resolve the certificate via the provider delegate + X509Certificate2 certificate = await _certificateProvider(options).ConfigureAwait(false); + + if (certificate == null) + { + return null; + } + + // Determine usage based on whether MTLS PoP is requested + // Same certificate can be used for either JWT signing OR MTLS binding + var usage = options.IsMtlsPopRequested + ? ClientCertificateUsage.MtlsBinding + : ClientCertificateUsage.Assertion; - return certificate; + return new ClientCertificateContext + { + Certificate = certificate, + Usage = usage + }; } } } diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientAssertionDelegateCredential.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientAssertionDelegateCredential.cs index 19190df8f9..e57de57326 100644 --- a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientAssertionDelegateCredential.cs +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientAssertionDelegateCredential.cs @@ -19,7 +19,7 @@ namespace Microsoft.Identity.Client.Internal.ClientCredential /// Handles client assertions supplied via a delegate that returns an /// (JWT + optional certificate bound for mTLS‑PoP). /// - internal sealed class ClientAssertionDelegateCredential : IClientCredential + internal sealed class ClientAssertionDelegateCredential : IClientCredential, IClientCertificateProvider { private readonly Func> _provider; @@ -82,5 +82,28 @@ public async Task AddConfidentialClientParametersAsync( oAuth2Client.AddBodyParameter(OAuth2Parameter.ClientAssertion, resp.Assertion); } + + public async Task GetCertificateAsync( + AssertionRequestOptions options, + CancellationToken cancellationToken) + { + // Call the assertion provider to get the signed assertion + ClientSignedAssertion assertion = await _provider(options, cancellationToken) + .ConfigureAwait(false); + + // If no TokenBindingCertificate, return null (no certificate provided) + if (assertion?.TokenBindingCertificate == null) + { + return null; + } + + // TokenBindingCertificate is ALWAYS for MTLS binding (by definition) + // It's not used for JWT signing - it's for TLS channel binding + return new ClientCertificateContext + { + Certificate = assertion.TokenBindingCertificate, + Usage = ClientCertificateUsage.MtlsBinding + }; + } } } diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateContext.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateContext.cs new file mode 100644 index 0000000000..c67bdfba07 --- /dev/null +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateContext.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Cryptography.X509Certificates; + +namespace Microsoft.Identity.Client.Internal.ClientCredential +{ + /// + /// Represents a resolved client certificate with metadata about its intended usage. + /// + /// + /// This class encapsulates both the certificate and how it will be used in the authentication flow. + /// The same certificate may be used for different purposes (JWT signing vs MTLS binding) depending + /// on the request configuration (IsMtlsPopRequested flag). + /// + internal class ClientCertificateContext + { + /// + /// Gets or sets the X509 certificate to be used for authentication. + /// + public X509Certificate2 Certificate { get; set; } + + /// + /// Gets or sets how this certificate will be used in the authentication flow. + /// + public ClientCertificateUsage Usage { get; set; } + } +} diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateOrchestrator.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateOrchestrator.cs new file mode 100644 index 0000000000..8f9358a07f --- /dev/null +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateOrchestrator.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Identity.Client.Internal; + +namespace Microsoft.Identity.Client.Internal.ClientCredential +{ + /// + /// Orchestrates client certificate resolution and validation for confidential client authentication. + /// + /// + /// This orchestrator encapsulates the complexity of resolving certificates from various credential types + /// and applies appropriate validation based on the authentication scenario (JWT signing vs MTLS PoP). + /// It handles both certificate-based credentials (WithCertificate) and assertion-based credentials + /// (WithClientAssertion) that may optionally provide a TokenBindingCertificate. + /// + internal static class ClientCertificateOrchestrator + { + /// + /// Resolves and validates a client certificate for authentication. + /// + /// The client credential which may or may not support certificate provision. + /// Service bundle containing configuration and authority information. + /// Flag indicating if MTLS Proof-of-Possession is requested. + /// Optional claims to include in the certificate request. + /// Cancellation token for the async operation. + /// + /// A containing the resolved certificate and its intended usage. + /// The certificate may be null if the credential doesn't support certificates and MTLS is not requested. + /// + /// + /// Thrown if: + /// - MTLS PoP is requested but the credential type does not support certificates + /// - The certificate provider returns null when MTLS is requested + /// - MTLS PoP is requested for AAD without regional endpoint configuration + /// + public static async Task ResolveCertificateAsync( + IClientCredential credential, + IServiceBundle serviceBundle, + bool isMtlsPopRequested, + string claims, + CancellationToken cancellationToken) + { + ClientCertificateContext certContext = null; + + // Check if credential supports certificate provision + if (credential is IClientCertificateProvider certProvider) + { + // Build options for certificate resolution + var options = new AssertionRequestOptions + { + ClientID = serviceBundle.Config.ClientId, + Claims = claims, + ClientCapabilities = serviceBundle.Config.ClientCapabilities, + IsMtlsPopRequested = isMtlsPopRequested, // Pass flag to provider + CancellationToken = cancellationToken + }; + + // Resolve the certificate context from the provider + // Provider now returns context with usage already determined + certContext = await certProvider + .GetCertificateAsync(options, cancellationToken) + .ConfigureAwait(false); + } + + // Validate if MTLS PoP is requested + if (isMtlsPopRequested) + { + // Check 1: Certificate must be provided (before checking region) + if (certContext?.Certificate == null) + { + // Credential doesn't support certs or provider returned null + string credentialTypeName = credential.GetType().Name; + throw new MsalClientException( + MsalError.MtlsCertificateNotProvided, + $"MTLS Proof-of-Possession requires a certificate. " + + $"The credential type '{credentialTypeName}' does not support certificates or did not provide one. " + + $"Use WithCertificate() to configure a certificate, or use WithClientAssertion() with a TokenBindingCertificate."); + } + + // Check 2: Validate certificate for MTLS (region check happens here) + ClientCertificateValidation.ValidateForMtlsPoP(certContext.Certificate, serviceBundle); + } + + // Return the context as-is (usage already set by provider) + return certContext; + } + } +} diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateUsage.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateUsage.cs new file mode 100644 index 0000000000..4088d501bb --- /dev/null +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateUsage.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Identity.Client.Internal.ClientCredential +{ + /// + /// Describes how a client certificate is used in the authentication flow. + /// + internal enum ClientCertificateUsage + { + /// + /// No certificate or not applicable. + /// + None, + + /// + /// Certificate is used to sign client assertions (JWT). + /// This is the default usage for certificate-based authentication. + /// + Assertion, + + /// + /// Certificate is used for MTLS Proof-of-Possession binding. + /// The certificate is used for the TLS handshake and binds the token to the TLS channel. + /// + MtlsBinding + } +} diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateValidation.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateValidation.cs new file mode 100644 index 0000000000..f3af2a1b90 --- /dev/null +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/ClientCertificateValidation.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Cryptography.X509Certificates; +using Microsoft.Identity.Client.Internal; + +namespace Microsoft.Identity.Client.Internal.ClientCredential +{ + /// + /// Shared validation utilities for client certificate scenarios. + /// + internal static class ClientCertificateValidation + { + /// + /// Validates that a certificate is suitable for MTLS Proof-of-Possession. + /// + /// The certificate to validate. + /// Service bundle containing configuration. + /// + /// Thrown if the certificate is null or if MTLS PoP is requested for AAD without regional configuration. + /// + public static void ValidateForMtlsPoP( + X509Certificate2 certificate, + IServiceBundle serviceBundle) + { + // Validate certificate presence + if (certificate == null) + { + throw new MsalClientException( + MsalError.MtlsCertificateNotProvided, + MsalErrorMessage.MtlsCertificateNotProvidedMessage); + } + + // Region validation: MTLS PoP for AAD requires regional endpoint configuration + if (serviceBundle.Config.Authority.AuthorityInfo.AuthorityType == AuthorityType.Aad && + serviceBundle.Config.AzureRegion == null) + { + throw new MsalClientException( + MsalError.MtlsPopWithoutRegion, + MsalErrorMessage.MtlsPopWithoutRegion); + } + } + } +} diff --git a/src/client/Microsoft.Identity.Client/Internal/ClientCredential/IClientCertificateProvider.cs b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/IClientCertificateProvider.cs new file mode 100644 index 0000000000..c32428575f --- /dev/null +++ b/src/client/Microsoft.Identity.Client/Internal/ClientCredential/IClientCertificateProvider.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Identity.Client.Internal.ClientCredential +{ + /// + /// Adapter interface for providing client certificates from various sources. + /// + /// + /// This interface enables the adapter pattern to unify certificate resolution + /// from different sources (IClientCredential implementations, managed identity sources, etc.). + /// Implementations return a that includes both the certificate + /// and its intended usage (Assertion for JWT signing vs MtlsBinding for TLS binding). + /// + internal interface IClientCertificateProvider + { + /// + /// Gets the client certificate context asynchronously, including the certificate and its usage type. + /// + /// + /// Options for the certificate request, including client ID, token endpoint, and whether MTLS PoP is requested. + /// The IsMtlsPopRequested flag helps determine the certificate usage type. + /// + /// Cancellation token for the async operation. + /// + /// A containing the certificate and its usage type, + /// or null if this provider does not supply certificates. + /// The usage type is determined by: + /// - TokenBindingCertificate → always + /// - Regular certificate → if MTLS PoP requested, + /// otherwise + /// + Task GetCertificateAsync( + AssertionRequestOptions options, + CancellationToken cancellationToken); + } +} diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs index bb23445538..920cbe0035 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/AuthenticationRequestParameters.cs @@ -14,6 +14,7 @@ using Microsoft.Identity.Client.Core; using Microsoft.Identity.Client.Extensibility; using Microsoft.Identity.Client.Instance; +using Microsoft.Identity.Client.Internal.ClientCredential; using Microsoft.Identity.Client.TelemetryCore.Internal.Events; using Microsoft.Identity.Client.Utils; @@ -111,15 +112,13 @@ public AuthenticationRequestParameters( public Guid CorrelationId => _commonParameters.CorrelationId; - public X509Certificate2 MtlsCertificate => _commonParameters.MtlsCertificate; - public bool IsMtlsPopRequested => _commonParameters.IsMtlsPopRequested; /// - /// The certificate resolved and used for client authentication (if certificate-based authentication was used). - /// This is set during the token request when the certificate is resolved. + /// The certificate context containing the resolved certificate and its usage type. + /// This is set during the token request when the certificate is resolved via the orchestrator. /// - public X509Certificate2 ResolvedCertificate { get; set; } + public ClientCertificateContext CertificateContext { get; set; } /// /// Indicates if the user configured claims via .WithClaims. Not affected by Client Capabilities diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs index 194e5cdbb0..2b481bd76c 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs @@ -12,6 +12,7 @@ using Microsoft.Identity.Client.Core; using Microsoft.Identity.Client.Extensibility; using Microsoft.Identity.Client.Instance; +using Microsoft.Identity.Client.Internal.ClientCredential; using Microsoft.Identity.Client.OAuth2; using Microsoft.Identity.Client.PlatformsCommon.Interfaces; using Microsoft.Identity.Client.Utils; @@ -179,6 +180,11 @@ private async Task GetAccessTokenAsync( { retryCount++; logger.Info($"[ClientCredentialRequest] OnMsalServiceFailure returned true. Retrying token request (Retry #{retryCount})."); + + // Clear certificate context to force re-resolution on retry + // This enables certificate rotation scenarios where provider returns different cert on retry + AuthenticationRequestParameters.CertificateContext = null; + continue; // Retry the loop } @@ -271,7 +277,7 @@ private async Task InvokeOnMsalServiceFailureCallbackAsync( Successful = false, Result = null, Exception = serviceException, - ClientCertificate = AuthenticationRequestParameters.ResolvedCertificate + ClientCertificate = AuthenticationRequestParameters.CertificateContext?.Certificate }; bool shouldRetry = await AuthenticationRequestParameters.AppConfig @@ -319,7 +325,7 @@ private async Task InvokeOnCompletionCallbackAsync( Successful = authResult != null, Result = authResult, Exception = exception, - ClientCertificate = AuthenticationRequestParameters.ResolvedCertificate + ClientCertificate = AuthenticationRequestParameters.CertificateContext?.Certificate }; await AuthenticationRequestParameters.AppConfig @@ -396,12 +402,11 @@ private bool ShouldUseCachedToken(MsalAccessTokenCacheItem cacheItem) return false; } - // 2) If an mTLS cert is supplied for THIS request, reuse cache only if + // 2) If an mTLS cert is used for token binding (not just JWT signing), reuse cache only if // the cached token's KeyId matches the one provided in the request. - X509Certificate2 requestCert = AuthenticationRequestParameters.MtlsCertificate; - - if (requestCert != null) + if (AuthenticationRequestParameters.CertificateContext?.Usage == ClientCertificateUsage.MtlsBinding) { + X509Certificate2 requestCert = AuthenticationRequestParameters.CertificateContext.Certificate; string expectedKid = CoreHelpers.ComputeX5tS256KeyId(requestCert); // If the certificate cannot produce a valid KeyId (SPKI-SHA256), expectedKid will be null or empty. @@ -409,12 +414,12 @@ private bool ShouldUseCachedToken(MsalAccessTokenCacheItem cacheItem) if (!string.Equals(cacheItem.KeyId, expectedKid, StringComparison.Ordinal)) { AuthenticationRequestParameters.RequestContext.Logger.Verbose(() => - "[ClientCredentialRequest] Cached token KeyId does not match request certificate (SPKI-SHA256 mismatch). Bypassing cache."); + "[ClientCredentialRequest] Cached token KeyId does not match MTLS binding certificate (SPKI-SHA256 mismatch). Bypassing cache."); return false; } AuthenticationRequestParameters.RequestContext.Logger.Verbose(() => - "[ClientCredentialRequest] Cached token KeyId matches request certificate (SPKI-SHA256). Using cached token."); + "[ClientCredentialRequest] Cached token KeyId matches MTLS binding certificate (SPKI-SHA256). Using cached token."); } // 3) If the token's hash matches AccessTokenHashToRefresh, ignore it diff --git a/src/client/Microsoft.Identity.Client/OAuth2/TokenClient.cs b/src/client/Microsoft.Identity.Client/OAuth2/TokenClient.cs index 2753726580..1a602b132f 100644 --- a/src/client/Microsoft.Identity.Client/OAuth2/TokenClient.cs +++ b/src/client/Microsoft.Identity.Client/OAuth2/TokenClient.cs @@ -38,7 +38,7 @@ public TokenClient(AuthenticationRequestParameters requestParams) _oAuth2Client = new OAuth2Client( _serviceBundle.ApplicationLogger, _serviceBundle.HttpManager, - requestParams.MtlsCertificate); + requestParams.CertificateContext?.Certificate); } public async Task SendTokenRequestAsync( 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 0db0290228..b7c56e3c35 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt @@ -9,4 +9,6 @@ Microsoft.Identity.Client.ManagedIdentityPopExtensions static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithAttributes(string attributeJson) -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.WithCertificate(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> certificateProvider, Microsoft.Identity.Client.AppConfig.CertificateOptions certificateOptions) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.get -> bool +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.set -> void const Microsoft.Identity.Client.MsalError.MtlsPopNotSupportedForEnvironment = "mtls_pop_not_supported_for_environment" -> 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 4741e5090d..616cdda0fa 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt @@ -9,4 +9,6 @@ Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.WithCertificate(S static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.WithCertificate(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> certificateProvider, Microsoft.Identity.Client.AppConfig.CertificateOptions certificateOptions) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithAttributes(string attributeJson) -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.get -> bool +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.set -> void const Microsoft.Identity.Client.MsalError.MtlsPopNotSupportedForEnvironment = "mtls_pop_not_supported_for_environment" -> 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 37f859d5dd..be8c6e5c3d 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 @@ -9,4 +9,6 @@ static Microsoft.Identity.Client.Extensibility.AbstractConfidentialClientAcquire Microsoft.Identity.Client.ManagedIdentityPopExtensions static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithAttributes(string attributeJson) -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.get -> bool +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.set -> void const Microsoft.Identity.Client.MsalError.MtlsPopNotSupportedForEnvironment = "mtls_pop_not_supported_for_environment" -> 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 37f859d5dd..be8c6e5c3d 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 @@ -9,4 +9,6 @@ static Microsoft.Identity.Client.Extensibility.AbstractConfidentialClientAcquire Microsoft.Identity.Client.ManagedIdentityPopExtensions static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithAttributes(string attributeJson) -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.get -> bool +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.set -> void const Microsoft.Identity.Client.MsalError.MtlsPopNotSupportedForEnvironment = "mtls_pop_not_supported_for_environment" -> 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 37f859d5dd..5eeaf3c08b 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 @@ -3,6 +3,8 @@ Microsoft.Identity.Client.AppConfig.CertificateOptions.AssociateTokensWithCertif Microsoft.Identity.Client.AppConfig.CertificateOptions.AssociateTokensWithCertificate.init -> void Microsoft.Identity.Client.AppConfig.CertificateOptions.SendX5C.get -> bool Microsoft.Identity.Client.AppConfig.CertificateOptions.SendX5C.init -> void +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.get -> bool +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.set -> void Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.WithCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, Microsoft.Identity.Client.AppConfig.CertificateOptions certificateOptions) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder static Microsoft.Identity.Client.Extensibility.ConfidentialClientApplicationBuilderExtensions.WithCertificate(this Microsoft.Identity.Client.ConfidentialClientApplicationBuilder builder, System.Func> certificateProvider, Microsoft.Identity.Client.AppConfig.CertificateOptions certificateOptions) -> Microsoft.Identity.Client.ConfidentialClientApplicationBuilder static Microsoft.Identity.Client.Extensibility.AbstractConfidentialClientAcquireTokenParameterBuilderExtension.WithExtraClientAssertionClaims(this Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder builder, string clientAssertionClaims) -> Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder 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 37f859d5dd..be8c6e5c3d 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 @@ -9,4 +9,6 @@ static Microsoft.Identity.Client.Extensibility.AbstractConfidentialClientAcquire Microsoft.Identity.Client.ManagedIdentityPopExtensions static Microsoft.Identity.Client.ManagedIdentityPopExtensions.WithMtlsProofOfPossession(this Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder builder) -> Microsoft.Identity.Client.AcquireTokenForManagedIdentityParameterBuilder Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder.WithAttributes(string attributeJson) -> Microsoft.Identity.Client.AcquireTokenForClientParameterBuilder +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.get -> bool +Microsoft.Identity.Client.AssertionRequestOptions.IsMtlsPopRequested.set -> void const Microsoft.Identity.Client.MsalError.MtlsPopNotSupportedForEnvironment = "mtls_pop_not_supported_for_environment" -> string diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpAndServiceBundle.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpAndServiceBundle.cs index e3cb1fac57..972b7eadf4 100644 --- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpAndServiceBundle.cs +++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/MockHttpAndServiceBundle.cs @@ -67,7 +67,7 @@ public AuthenticationRequestParameters CreateAuthenticationRequestParameters( }; var authorityObj = Authority.CreateAuthority(authority, validateAuthority); - var requestContext = new RequestContext(ServiceBundle, Guid.NewGuid(), commonParameters.MtlsCertificate); + var requestContext = new RequestContext(ServiceBundle, Guid.NewGuid(), null); AuthenticationRequestParameters authenticationRequestParameters = new AuthenticationRequestParameters( ServiceBundle, diff --git a/tests/Microsoft.Identity.Test.Common/TestCommon.cs b/tests/Microsoft.Identity.Test.Common/TestCommon.cs index 0fd0163719..f99e204a0f 100644 --- a/tests/Microsoft.Identity.Test.Common/TestCommon.cs +++ b/tests/Microsoft.Identity.Test.Common/TestCommon.cs @@ -98,7 +98,7 @@ public static AuthenticationRequestParameters CreateAuthenticationRequestParamet }; authority ??= Authority.CreateAuthority(TestConstants.AuthorityTestTenant); - requestContext ??= new RequestContext(serviceBundle, Guid.NewGuid(), commonParameters.MtlsCertificate) + requestContext ??= new RequestContext(serviceBundle, Guid.NewGuid(), null) { ApiEvent = new Client.TelemetryCore.Internal.Events.ApiEvent(Guid.NewGuid()) }; diff --git a/tests/Microsoft.Identity.Test.Unit/RequestsTests/SilentRequestTests.cs b/tests/Microsoft.Identity.Test.Unit/RequestsTests/SilentRequestTests.cs index 7218fbff17..8df6d74100 100644 --- a/tests/Microsoft.Identity.Test.Unit/RequestsTests/SilentRequestTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/RequestsTests/SilentRequestTests.cs @@ -277,7 +277,7 @@ public AuthenticationRequestParameters CreateRequestParams( AuthorityOverride = authorityOverride }; - var requestContext = new RequestContext(ServiceBundle, Guid.NewGuid(), commonParameters.MtlsCertificate); + var requestContext = new RequestContext(ServiceBundle, Guid.NewGuid(), null); var authority = Microsoft.Identity.Client.Instance.Authority.CreateAuthorityForRequestAsync( requestContext,