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
Bogdan Gavril (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 Bogdan Gavril (@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 |
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).