Add MSI mTLS PoP support: pure MI + FIC-with-MI (impl for devex #3832)#3839
Conversation
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>
83aae6b to
8a4db7f
Compare
bgavrilMS
left a comment
There was a problem hiding this comment.
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:
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 |
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>
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
ClientAssertionProviderBaseso 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"onthe downstream API options (together with
RequestAppToken = true). No new public APIon the IdWeb consumer surface.
Devex (the entire dev-facing surface)
For FIC-with-MI, add the
SignedAssertionFromManagedIdentitycredential underAzureAd.ClientCredentials— the binding cert minted by MI flows through to the outerCCA automatically.
What changed
Source
ClientAssertionProviderBase.cspublic virtualextension points:SupportsTokenBinding(defaultfalse) andGetSignedAssertionWithBindingAsync(default returnsnull).ManagedIdentityClientAssertion.csSupportsTokenBinding => true,GetSignedAssertionWithBindingAsyncreturns aClientSignedAssertion(assertion + binding cert pair). Bearer and bound paths share a privateAcquireManagedIdentityTokenAsynchelper.ConfidentialClientApplicationBuilderExtension.cscredential.CachedValue is ClientAssertionProviderBase { SupportsTokenBinding: true }— no narrowing to a concrete type. Other signed-assertion sources still throwIDW10115.TokenAcquisition.csWithMtlsProofOfPossession().WithAttestationSupport()on the pure-MI builder whenIsTokenBindingis set.IDWebErrorMessage.csIDW10115to reflect the new opt-in model.PublicAPI/PublicAPI.Unshipped.txtMicrosoft.Identity.Web.Certificateless.csproj+Directory.Build.propsMicrosoft.Identity.Client.KeyAttestationvia a centrally-managed version variable.No new internal API and no
InternalsVisibleToentry — the extension point lives on theexisting public base type.
Tests + samples
tests/Microsoft.Identity.Web.Test/Certificates/WithClientCredentialsTests.cstests/DevApps/daemon-app/daemon-app-msi-mtls/tests/DevApps/daemon-app/daemon-app-fic-mtls/Microsoft.Identity.Web.slnDesign notes
The opt-in lives on the public abstract
ClientAssertionProviderBaserather than on theconcrete
ManagedIdentityClientAssertion. This:is ManagedIdentityClientAssertionnarrowing in the consumer.InternalsVisibleTo("Microsoft.Identity.Web.TokenAcquisition").OidcIdpSignedAssertionProvidera clean override path — tracked as follow-upin [Feature Request] Add token-binding (mTLS PoP) support to OidcIdpSignedAssertionProvider #3851.
Stateless by design:
GetSignedAssertionWithBindingAsyncreturns the assertion + certpair fresh each call. No shared mutable flags, no DI-singleton thread-safety races.
Tests
WithClientCredentialsTests.cs:WithBindingCertificateAsync_FicWithManagedIdentityAssertion_ReturnsBuilder— dispatch succeeds.WithBindingCertificateAsync_FicWithFileBasedAssertion_StillThrows—IDW10115regression guard.(
Microsoft.Identity.Web.Testnet462/472/net8/net9/net10 +Microsoft.Identity.Web.UI.Testnet8/net9.)Builds
Microsoft.Identity.Web.Certificateless(all TFMs): 0 warn / 0 err.Microsoft.Identity.Web.TokenAcquisition(all TFMs): 0 warn / 0 err.TreatWarningsAsErrors=trueenforced.Linked
SupportsTokenBindingtoOidcIdpSignedAssertionProvider).