Skip to content

MTLS Without Tokens Support - MicrosoftIdentityMessageHandler Support#3815

Open
tlupes wants to merge 18 commits into
masterfrom
MicrosoftIdentityMessageHandlerMtls
Open

MTLS Without Tokens Support - MicrosoftIdentityMessageHandler Support#3815
tlupes wants to merge 18 commits into
masterfrom
MicrosoftIdentityMessageHandlerMtls

Conversation

@tlupes
Copy link
Copy Markdown
Collaborator

@tlupes tlupes commented May 13, 2026

MTLS Without Tokens Support - MicrosoftIdentityMessageHandler Support

Continuation of the PR #3747 , this adds the same level of support to MicrosoftIdentityMessageHandler.

Description

  • Moved shared constants from DownstreamAPI to Microsoft.Identity.Web.TokenAcquisition/Constants.cs
  • Moved UnauthorizedHttpRequestException to Microsoft.Identity.Web.TokenAcquisition (Internal class)
  • Add InternalsVisibleTo between Microsoft.Identity.Web.TokenAcquisition and Microsoft.Identity.Web.DownstreamApi so that the above changes are accessible.
  • Minor changes to MicrosoftIdentityHttpClientBuilderExtensions:
    • Previously this would always pass a null logger to the MicrosoftIdentityMessageHandler. Now, it will pass one if configured.
    • Previously it would always make a new IMsalMtlsHttpFactory even if one were configured. Now, it will use a configured one if available.
    • Support to pass a ICredentialsProvider to the handler.
  • Support within MicrosoftIdentityMessageHandler for mTLS without tokens

@tlupes tlupes requested a review from a team as a code owner May 13, 2026 16:24
Copy link
Copy Markdown
Contributor

@neha-bhargava neha-bhargava left a comment

Choose a reason for hiding this comment

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

Left some comments

Comment thread src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityMessageHandler.cs Outdated
Comment thread src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityMessageHandler.cs Outdated
Comment thread src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityMessageHandler.cs Outdated
Comment thread src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs Outdated
Comment thread src/Microsoft.Identity.Web.TokenAcquisition/GlobalSuppressions.cs Outdated
<ItemGroup Condition="'$(TargetFramework)' == 'net462' or '$(TargetFramework)' == 'net472' or '$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="..\Shared\CodeAnalysisAttributes.cs" Link="Polyfills\CodeAnalysisAttributes.cs" />
</ItemGroup>
<ItemGroup>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this removed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

With the added InternalsVisibleTo it's no longer needed (and in fact results in errors).

Comment thread tests/Microsoft.Identity.Web.Test/CertificatesObserverTests.cs Outdated
Comment thread src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs Outdated
var httpClientFactory = services.GetService<IHttpClientFactory>();
if (httpClientFactory is not null)
{
return new MsalMtlsHttpClientFactory(httpClientFactory);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't know how this helps. All mTLS traffic will bypass the factory. I guess it's fine, I don't have an alternative for this. Maybe log a warning?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's trying to handle scenarios where not all services are available, but some are. In this, if IMsalMtlsHttpClientFactory is available, then it will use that. Failing that, then it will check if IHttpClientFactory is available and use that. Failing that, it returns null.

{
if (_credentialsProvider is null)
{
throw new InvalidOperationException("mTLS authentication requires a Credentials Provider object to be registered, but no such service was found.");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Consider adding an aka.ms/idweb/mtls page where you describe the sceanrios in more detail. As an app developer, I wouldn't know how to action this exception.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added a link, I will make the link work in the near future. Once this is in I'll write up a help page for it.

Comment thread src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityMessageHandler.cs Outdated
{
if (_mtlsHttpClientFactory is null)
{
throw new InvalidOperationException("Authentication using mTLS requires a MtlsHttpClientFactory object to be registered, but no such service was found.");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A help page would be good.

BUt at the same time, can the app developer get into this state? I thought we provide an HTTP pipeline in all cases.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

HTTP pipeline will not be present if the consumer did not register an http factory (e.g. services.AddHttp();)

tlupes and others added 6 commits May 25, 2026 15:17
… dispatch

- Add missing 'using Microsoft.Identity.Web.Diagnostics' to DownstreamApi.cs so
  UnauthorizedHttpRequestException resolves.
- Fix Invoke()/invoke() casing in CertificatesObserverTests (3 sites).
- Rename SendAsync_WithMtlsPop_NullMtlsFactory_UsesBaseSendAsync to *_Throws
  and assert InvalidOperationException, matching the deliberate behavior
  change introduced when the mTLS dispatch was tightened to require an
  IMsalMtlsHttpClientFactory whenever a binding certificate is present.
- Wire SendAsync_CertificateFailureRetry_ClonesRequest through a real mTLS
  factory so the retry-clone path it covers is reachable.

Co-authored-by: Copilot <[email protected]>
…g with internal cert-failure retry

For ProtocolScheme=MTLS there is no token to refresh with WWW-Authenticate
claims, and SendWithCertRetryAsync already retried credential acquisition
on the initial 401. Allowing the outer claims-challenge retry to also fire
would issue a redundant third send and another GetCredentialAsync call,
which may hit disk or Key Vault, for no semantic benefit.

Add SendAsync_MtlsOnly_PersistentUnauthorizedWithClaims_DoesNotComposeRetries
to pin the gating: exactly two sends and two GetCredentialAsync calls when
the server keeps returning 401 + claims challenge in pure-mTLS mode.

Co-authored-by: Copilot <[email protected]>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Extends the existing mTLS “without tokens” work (previously added for DownstreamApi) to MicrosoftIdentityMessageHandler, including DI wiring and shared-constant refactoring into Microsoft.Identity.Web.TokenAcquisition.

Changes:

  • Adds mTLS-only dispatch + bounded cert-failure retry behavior to MicrosoftIdentityMessageHandler, and updates tests accordingly.
  • Refactors shared protocol/status-code constants into Microsoft.Identity.Web.TokenAcquisition/Constants.cs and updates DownstreamApi/TokenAcquisition to consume them.
  • Updates HttpClient builder extensions to resolve configured IMsalMtlsHttpClientFactory, pass a logger, and optionally construct/use an ICredentialsProvider.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/Microsoft.Identity.Web.Test/MicrosoftIdentityMessageHandlerTokenBindingTests.cs Updates/extends handler tests for mTLS-only retry and cloning behavior.
tests/Microsoft.Identity.Web.Test/MicrosoftIdentityHttpClientBuilderExtensionsTests.cs Minor formatting change in tests.
tests/Microsoft.Identity.Web.Test/CertificatesObserverTests.cs Expands observer test coverage to include handler-based mTLS path.
src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs Updates reference to renamed certificate-related STS error-code constant.
src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/NetFramework/PublicAPI.Unshipped.txt Records new public handler constructor overload for .NET Framework targets.
src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/NetCore/PublicAPI.Unshipped.txt Records new public handler constructor overload for .NET (Core) targets.
src/Microsoft.Identity.Web.TokenAcquisition/Properties/InternalsVisibleTo.cs Grants DownstreamApi access to TokenAcquisition internals used by the refactor.
src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityMessageHandler.cs Implements mTLS-only support + cert-failure retry orchestration in the handler.
src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityHttpClientBuilderExtensions.cs Improves DI resolution for logger, mTLS factory, and credentials provider.
src/Microsoft.Identity.Web.TokenAcquisition/GlobalSuppressions.cs Adds analyzer suppression for the new overload signature.
src/Microsoft.Identity.Web.TokenAcquisition/Constants.cs Centralizes protocol names and auth-failure status code set.
src/Microsoft.Identity.Web.DownstreamApi/Microsoft.Identity.Web.DownstreamApi.csproj Removes AOT attribute polyfill include (affects older TFMs).
src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs Switches to shared constants and uses moved diagnostics exception.
src/Microsoft.Identity.Web.Diagnostics/UnauthorizedHttpRequestException.cs Introduces moved exception type used for certificate-failure reporting.
Comments suppressed due to low confidence (1)

src/Microsoft.Identity.Web.DownstreamApi/Microsoft.Identity.Web.DownstreamApi.csproj:18

  • Microsoft.Identity.Web.DownstreamApi targets net462/net472/netstandard2.0 (see the PublicAPI conditional), but the csproj no longer links src/Shared/CodeAnalysisAttributes.cs. This file polyfills RequiresUnreferencedCodeAttribute / RequiresDynamicCodeAttribute for NETFRAMEWORK/NETSTANDARD2_0, and DownstreamApi uses these attributes unconditionally in DownstreamApi.cs. Without the polyfill, older TFMs will fail to compile. Re-add the conditional <Compile Include="..\Shared\CodeAnalysisAttributes.cs" ...> item group (as done in TokenAcquisition) or gate the attribute usages behind #if for TFMs that provide them.
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
    <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
  <IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
  <ItemGroup>
    <None Include="..\..\README.md">
      <Pack>True</Pack>
      <PackagePath>\</PackagePath>
    </None>
  </ItemGroup>

Comment thread src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs
Comment on lines +285 to +304
/// <summary>
/// Initializes a new instance of the <see cref="MicrosoftIdentityMessageHandler"/> class with mTLS PoP token binding support.
/// </summary>
/// <param name="headerProvider">
/// The <see cref="IAuthorizationHeaderProvider"/> used to acquire authorization headers for outgoing requests.
/// This is typically obtained from the dependency injection container.
/// </param>
/// <param name="defaultOptions">
/// Default authentication options that will be used for all requests unless overridden per-request.
/// If <see langword="null"/>, each request must specify its own authentication options or an exception will be thrown.
/// </param>
/// <param name="mtlsHttpClientFactory">
/// Optional factory for creating HTTP clients configured with mTLS client certificates for token binding
/// (mTLS PoP) scenarios. When provided and the <see cref="AuthorizationHeaderProviderOptions.ProtocolScheme"/>
/// is set to <c>"MTLS_POP"</c>, the handler will use this factory to create an HTTP client with the binding
/// certificate and send requests through it.
/// </param>
/// <param name="credentialsProvider">
/// Optional provider for certificates. This is required for mTLS-only authentication purposes.
/// </param>
Comment thread src/Microsoft.Identity.Web.TokenAcquisition/MicrosoftIdentityMessageHandler.cs Outdated
Comment thread tests/Microsoft.Identity.Web.Test/CertificatesObserverTests.cs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants