Skip to content

Add MSI mTLS PoP support: pure MI + FIC-with-MI (impl for devex #3832)#3839

Merged
gladjohn merged 4 commits into
masterfrom
gladjohn/msi-fic-mtls-pop-fresh
Jun 10, 2026
Merged

Add MSI mTLS PoP support: pure MI + FIC-with-MI (impl for devex #3832)#3839
gladjohn merged 4 commits into
masterfrom
gladjohn/msi-fic-mtls-pop-fresh

Conversation

@gladjohn

@gladjohn gladjohn commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the devex proposal in #3832: mTLS Proof-of-Possession for Managed Identity
(pure MSI) and FIC backed by Managed Identity (federated identity credentials with
MI as the assertion source).

Adds a small public extension point on the existing public ClientAssertionProviderBase
so any future binding-capable assertion provider (e.g. OIDC FIC, tracked separately in
#3851) can opt in without further plumbing changes in Microsoft.Identity.Web.TokenAcquisition.

Triggered by setting AuthorizationHeaderProviderOptions.ProtocolScheme = "MTLS_POP" on
the downstream API options (together with RequestAppToken = true). No new public API
on the IdWeb consumer surface.

Devex (the entire dev-facing surface)

"AzureKeyVault": {
  "RequestAppToken": true,
  "ProtocolScheme": "MTLS_POP",
  "AcquireTokenOptions": {
    "ManagedIdentity": { "UserAssignedClientId": "..." }
  }
}

For FIC-with-MI, add the SignedAssertionFromManagedIdentity credential under
AzureAd.ClientCredentials — the binding cert minted by MI flows through to the outer
CCA automatically.

What changed

Source

File Change
ClientAssertionProviderBase.cs Two new public virtual extension points: SupportsTokenBinding (default false) and GetSignedAssertionWithBindingAsync (default returns null).
ManagedIdentityClientAssertion.cs Overrides both: SupportsTokenBinding => true, GetSignedAssertionWithBindingAsync returns a ClientSignedAssertion (assertion + binding cert pair). Bearer and bound paths share a private AcquireManagedIdentityTokenAsync helper.
ConfidentialClientApplicationBuilderExtension.cs Generic dispatch: checks credential.CachedValue is ClientAssertionProviderBase { SupportsTokenBinding: true } — no narrowing to a concrete type. Other signed-assertion sources still throw IDW10115.
TokenAcquisition.cs Chains WithMtlsProofOfPossession().WithAttestationSupport() on the pure-MI builder when IsTokenBinding is set.
IDWebErrorMessage.cs Reworded IDW10115 to reflect the new opt-in model.
PublicAPI/PublicAPI.Unshipped.txt +4 entries for the new public virtuals + MI overrides.
Microsoft.Identity.Web.Certificateless.csproj + Directory.Build.props Reference Microsoft.Identity.Client.KeyAttestation via a centrally-managed version variable.

No new internal API and no InternalsVisibleTo entry — the extension point lives on the
existing public base type.

Tests + samples

File Change
tests/Microsoft.Identity.Web.Test/Certificates/WithClientCredentialsTests.cs +91 lines of new unit coverage
tests/DevApps/daemon-app/daemon-app-msi-mtls/ New sample: pure MI mTLS PoP
tests/DevApps/daemon-app/daemon-app-fic-mtls/ New sample: FIC-with-MI mTLS PoP
Microsoft.Identity.Web.sln Wires up the two new sample projects

Design notes

The opt-in lives on the public abstract ClientAssertionProviderBase rather than on the
concrete ManagedIdentityClientAssertion. This:

Stateless by design: GetSignedAssertionWithBindingAsync returns the assertion + cert
pair fresh each call. No shared mutable flags, no DI-singleton thread-safety races.

Tests

  • New unit tests in WithClientCredentialsTests.cs:
    • WithBindingCertificateAsync_FicWithManagedIdentityAssertion_ReturnsBuilder — dispatch succeeds.
    • WithBindingCertificateAsync_FicWithFileBasedAssertion_StillThrowsIDW10115 regression guard.
  • Full suite: 3,003 passed, 19 skipped, 0 failed.
    (Microsoft.Identity.Web.Test net462/472/net8/net9/net10 + Microsoft.Identity.Web.UI.Test net8/net9.)

Builds

  • Microsoft.Identity.Web.Certificateless (all TFMs): 0 warn / 0 err.
  • Microsoft.Identity.Web.TokenAcquisition (all TFMs): 0 warn / 0 err.
  • Both samples: 0 warn / 0 err.
  • TreatWarningsAsErrors=true enforced.

Linked

Comment thread src/Microsoft.Identity.Web.Certificateless/ManagedIdentityClientAssertion.cs Outdated
@gladjohn gladjohn marked this pull request as ready for review June 8, 2026 15:25
@gladjohn gladjohn requested a review from a team as a code owner June 8, 2026 15:25
gladjohn and others added 2 commits June 8, 2026 08:53
Implements devex spec #3832:

- TokenAcquisition: chain WithMtlsProofOfPossession().WithAttestationSupport()
  on the pure-MI builder when ProtocolScheme=MTLS_POP.
- ConfidentialClientApplicationBuilderExtension: dispatch FIC-with-MI to a
  bound-assertion delegate that returns ClientSignedAssertion (carrying both
  the JWT and the MI-minted binding certificate). All other signed-assertion
  source types still throw IDW10115 (preserved by regression test).
- ManagedIdentityClientAssertion: new internal GetSignedAssertionWithBindingAsync
  that calls AcquireTokenForManagedIdentity(...).WithMtlsProofOfPossession()
  .WithAttestationSupport() and returns the bound assertion + cert pair.
- Reference Microsoft.Identity.Client.KeyAttestation 4.84.1-preview.
- IVT from Certificateless to TokenAcquisition (3rd entry in established file).
- 2 new unit tests in WithClientCredentialsTests.cs (930 total pass, 0 fail).
- 2 new daemon samples: daemon-app-msi-mtls, daemon-app-fic-mtls.

No new public API surface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…o GA 4.84.2

- Rename CancellationToken variable per cpp11nullptr review feedback to
  avoid confusion with OAuth/auth tokens (effectiveToken ->
  effectiveCancellationToken in GetSignedAssertionWithBindingAsync).
- Bump Microsoft.Identity.Client.KeyAttestation from 4.84.1-preview to
  GA 4.84.2 so it aligns with MSAL 4.84.2 already on master.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gladjohn gladjohn force-pushed the gladjohn/msi-fic-mtls-pop-fresh branch from 83aae6b to 8a4db7f Compare June 8, 2026 15:57
Comment thread src/Microsoft.Identity.Web.Certificateless/Properties/InternalsVisibleTo.cs Outdated
Comment thread src/Microsoft.Identity.Web.Certificateless/ManagedIdentityClientAssertion.cs Outdated

@bgavrilMS bgavrilMS left a comment

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.

LGTM, but please make sure you also update the OIDC FIC (i.e. FIC from other Identity Provider) code path.

@gladjohn

gladjohn commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

LGTM, but please make sure you also update the OIDC FIC (i.e. FIC from other Identity Provider) code path.

Thanks @bgavrilMS. Just to make sure we're aligned before I touch that path - by "OIDC FIC code path" do you mean the federated assertion coming from OidcIdpSignedAssertionProvider (i.e., Microsoft.Identity.Web.OidcFIC)?

From the code I see that OIDC IdP only hands us a JWT, not a binding certificate. For mTLS PoP on that path, are you expecting:

  • a clearer error when someone configures token binding with an OIDC-IdP credential (so it fails fast and explains why), or
  • wiring it up so the OIDC-IdP assertion can be paired with a separate binding-cert credential and the outer CCA actually issues a PoP token?

Just want to make sure I match what you have in mind.

@bgavrilMS

Copy link
Copy Markdown
Member

LGTM, but please make sure you also update the OIDC FIC (i.e. FIC from other Identity Provider) code path.

Thanks @bgavrilMS. Just to make sure we're aligned before I touch that path - by "OIDC FIC code path" do you mean the federated assertion coming from OidcIdpSignedAssertionProvider (i.e., Microsoft.Identity.Web.OidcFIC)?

From the code I see that OIDC IdP only hands us a JWT, not a binding certificate. For mTLS PoP on that path, are you expecting:

  • a clearer error when someone configures token binding with an OIDC-IdP credential (so it fails fast and explains why), or
  • wiring it up so the OIDC-IdP assertion can be paired with a separate binding-cert credential and the outer CCA actually issues a PoP token?

Just want to make sure I match what you have in mind.

Yes, Microsoft.Identity.Web.OidcFIC. It should be made to work with bound FIC + bearer or bound tokens

@gladjohn

gladjohn commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

LGTM, but please make sure you also update the OIDC FIC (i.e. FIC from other Identity Provider) code path.

Thanks @bgavrilMS. Just to make sure we're aligned before I touch that path - by "OIDC FIC code path" do you mean the federated assertion coming from OidcIdpSignedAssertionProvider (i.e., Microsoft.Identity.Web.OidcFIC)?
From the code I see that OIDC IdP only hands us a JWT, not a binding certificate. For mTLS PoP on that path, are you expecting:

  • a clearer error when someone configures token binding with an OIDC-IdP credential (so it fails fast and explains why), or
  • wiring it up so the OIDC-IdP assertion can be paired with a separate binding-cert credential and the outer CCA actually issues a PoP token?

Just want to make sure I match what you have in mind.

Yes, Microsoft.Identity.Web.OidcFIC. It should be made to work with bound FIC + bearer or bound tokens

Based on our conversation - tracking OIDC work in its own task - #3851

github-actions Bot pushed a commit to EelcoLos/nx-tinkering that referenced this pull request Jun 30, 2026
Pinned
[Microsoft.Identity.Web](https://github.com/AzureAD/microsoft-identity-web)
at 4.11.0.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Identity.Web's
releases](https://github.com/AzureAD/microsoft-identity-web/releases)._

## 4.11.0

## What's Changed
* Bump vitest from 3.2.4 to 4.1.0 in
/tests/DevApps/SidecarAdapter/typescript by @​dependabot[bot] in
AzureAD/microsoft-identity-web#3836
* Bump MSAL.NET to 4.84.2 and align OWIN binding redirects by @​gladjohn
with @​Copilot in
AzureAD/microsoft-identity-web#3844
* docs(design): devex proposal for mTLS PoP on Managed Identity and FIC
by @​gladjohn in
AzureAD/microsoft-identity-web#3832
* Prevent OpenIdConnectMiddlewareDiagnostics from logging sensitive
values by @​iarekk in
AzureAD/microsoft-identity-web#3850
* Add MSI mTLS PoP support: pure MI + FIC-with-MI (impl for devex
#​3832) by @​gladjohn in
AzureAD/microsoft-identity-web#3839
* docs(design): devex proposal for Bearer tokens with bound credentials
by @​gladjohn in
AzureAD/microsoft-identity-web#3833
* Add bound-credential support for Bearer tokens (cert + mTLS) by
@​gladjohn in
AzureAD/microsoft-identity-web#3835
* Upgrade IdWeb Sidecar to .NET 10 (LTS) by @​soodt in
AzureAD/microsoft-identity-web#3841
* MTLS Without Tokens Support - MicrosoftIdentityMessageHandler Support
by @​tlupes in
AzureAD/microsoft-identity-web#3815
* fix: include isTokenBinding in CCA cache key to prevent bearer/PoP
collision by @​gladjohn in
AzureAD/microsoft-identity-web#3867
* Test + doc: x-ms-tokenboundauth header for AKV mTLS PoP via
ExtraHeaderParameters by @​gladjohn in
AzureAD/microsoft-identity-web#3864
* Add mTLS PoP Copilot skill (certificate, MSI, FIC) by @​gladjohn in
AzureAD/microsoft-identity-web#3872
* Fix CVE-2026-48109: Pin MessagePack to patched version 2.5.301 by
@​soodt in AzureAD/microsoft-identity-web#3865
* Sidecar: gate agent identity parameters behind AllowOverrides by
@​iNinja in AzureAD/microsoft-identity-web#3871
* Bump System.Formats.Asn1 base version to 10.0.2 by @​iarekk in
AzureAD/microsoft-identity-web#3875
* Bump Microsoft.IdentityModel.* from 8.18.0 to 8.19.1 by @​iarekk in
AzureAD/microsoft-identity-web#3879
* Use IIdentityLogger for MSAL logging in TokenAcquisition and
ManagedIdentityClientAssertion (#​3820) by @​neha-bhargava in
AzureAD/microsoft-identity-web#3880
* Update Microsoft.Identity.Abstractions to 12.2.0 and MSAL to 4.85.0 by
@​neha-bhargava in
AzureAD/microsoft-identity-web#3881
* Surface MSAL AuthenticationResultMetadata + exception details on
AcquireTokenResult by @​neha-bhargava in
AzureAD/microsoft-identity-web#3856
* Flow outgoing request to header providers via AcquireTokenOptions by
@​neha-bhargava in
AzureAD/microsoft-identity-web#3876
* Throw on Authority vs Instance/TenantId conflict (OIDC + MSAL parity)
by @​iarekk in
AzureAD/microsoft-identity-web#3873
* Delete .github/workflows/evergreen.yml by @​bgavrilMS in
AzureAD/microsoft-identity-web#3803
* Add comprehensive authority configuration and precedence documentation
by @​jmprieur with @​Copilot in
AzureAD/microsoft-identity-web#3617
* Bump js-yaml from 4.1.1 to 4.2.0 in
/tests/DevApps/SidecarAdapter/typescript by @​dependabot[bot] in
AzureAD/microsoft-identity-web#3862
* Move authority docs into docs/authority-configuration/ subfolder by
@​iarekk in AzureAD/microsoft-identity-web#3885
* Revert "Throw on Authority vs Instance/TenantId conflict (#​3873)" by
@​iarekk in AzureAD/microsoft-identity-web#3888
* Update Microsoft.Identity.Client to 4.85.1 by @​neha-bhargava in
AzureAD/microsoft-identity-web#3889
* Enable E2E test coverage on internal Azure DevOps pipelines by
@​gladjohn in
AzureAD/microsoft-identity-web#3883
* Bump esbuild and tsx in /tests/DevApps/SidecarAdapter/typescript by
@​dependabot[bot] in
AzureAD/microsoft-identity-web#3859
* Skip AcquireTokenWithMtlsPop test: AAD westus3 test slice returns
Bearer by @​neha-bhargava in
AzureAD/microsoft-identity-web#3892

## New Contributors
* @​iarekk made their first contribution in
AzureAD/microsoft-identity-web#3850
* @​soodt made their first contribution in
AzureAD/microsoft-identity-web#3841

**Full Changelog**:
AzureAD/microsoft-identity-web@4.10.0...4.11.0

Commits viewable in [compare
view](AzureAD/microsoft-identity-web@4.10.0...4.11.0).
</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Jun 30, 2026
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.

3 participants