diff --git a/src/client/Microsoft.Identity.Client/AppConfig/ConfidentialClientApplicationBuilder.cs b/src/client/Microsoft.Identity.Client/AppConfig/ConfidentialClientApplicationBuilder.cs index 54234eacfd..2f1463e56d 100644 --- a/src/client/Microsoft.Identity.Client/AppConfig/ConfidentialClientApplicationBuilder.cs +++ b/src/client/Microsoft.Identity.Client/AppConfig/ConfidentialClientApplicationBuilder.cs @@ -228,7 +228,7 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func cli throw new ArgumentNullException(nameof(clientAssertionDelegate)); } - return WithClientAssertion( + return WithClientAssertionInternal( (opts, ct) => Task.FromResult(new ClientSignedAssertion { @@ -251,7 +251,7 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func { string jwt = await clientAssertionAsyncDelegate(ct).ConfigureAwait(false); @@ -273,7 +273,7 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func { string jwt = await clientAssertionAsyncDelegate(opts).ConfigureAwait(false); @@ -295,6 +295,18 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(FuncThrown if is . public ConfidentialClientApplicationBuilder WithClientAssertion(Func> clientSignedAssertionProvider) + { + ValidateUseOfExperimentalFeature(); + return WithClientAssertionInternal(clientSignedAssertionProvider); + } + + /// + /// Internal helper to set the client assertion provider. + /// + /// + /// + internal ConfidentialClientApplicationBuilder WithClientAssertionInternal( + Func> clientSignedAssertionProvider) { Config.ClientCredential = new ClientAssertionDelegateCredential(clientSignedAssertionProvider); return this; diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientAssertionTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientAssertionTests.cs index 5f253a624d..70db27353d 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientAssertionTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientAssertionTests.cs @@ -345,6 +345,7 @@ public async Task ClientAssertion_BearerAsync() var handler = http.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithHttpManager(http) .WithClientAssertion(BearerDelegate()) @@ -373,6 +374,7 @@ public async Task ClientAssertion_WithPoPDelegate_No_Mtls_Api_SendsBearer_Async( http.AddInstanceDiscoveryMockHandler(); var handler = http.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithHttpManager(http) .WithClientAssertion(PopDelegate()) @@ -399,6 +401,7 @@ public async Task ClientAssertion_ReceivesClientCapabilitiesAsync() bool checkedCaps = false; var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithClientCapabilities(TestConstants.ClientCapabilities) .WithHttpManager(http) @@ -427,6 +430,7 @@ public async Task ClientAssertion_ReceivesClientCapabilitiesAsync() public async Task ClientAssertion_EmptyJwt_ThrowsAsync() { var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithClientAssertion((o, c) => Task.FromResult(new ClientSignedAssertion { Assertion = string.Empty })) @@ -443,6 +447,7 @@ public async Task ClientAssertion_CancellationTokenPropagatesAsync() using var cts = new CancellationTokenSource(); var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithClientAssertion((o, ct) => { @@ -481,6 +486,7 @@ public async Task WithMtlsPop_AfterPoPDelegate_Works() var cert = CertHelper.GetOrCreateTestCert(); var app = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientAssertion(PopDelegate()) .WithAuthority($"https://login.microsoftonline.com/123456-1234-2345-1234561234") .WithAzureRegion(ConfidentialClientApplication.AttemptRegionDiscovery) @@ -559,6 +565,7 @@ public async Task PoP_CachedTokenWithDifferentCertificate_IsBypassedAsync() // ─────────── Build the app ─────────── var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithClientAssertion(popDelegate) .WithAuthority($"https://login.microsoftonline.com/123456-1234-2345-1234561234") @@ -591,7 +598,8 @@ public async Task PoP_CachedTokenWithDifferentCertificate_IsBypassedAsync() public async Task WithMtlsPop_AfterBearerDelegate_Throws() { var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) - .WithClientSecret(TestConstants.ClientSecret) + .WithExperimentalFeatures(true) + .WithClientSecret(TestConstants.ClientSecret) .WithClientAssertion(BearerDelegate()) .BuildConcrete(); @@ -614,6 +622,7 @@ public async Task ClientAssertion_NotCalledWhenTokenFromCacheAsync() http.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); // first call => network var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithHttpManager(http) .WithClientAssertion((o, c) => @@ -644,6 +653,7 @@ public async Task WithMtlsPop_AfterPoPDelegate_NoRegion_ThrowsAsync() // Arrange – CCA with PoP delegate (returns JWT + cert) but **no AzureRegion configured** var cert = CertHelper.GetOrCreateTestCert(); var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientAssertion(PopDelegate()) .WithHttpManager(http) .BuildConcrete(); diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs index b2ab2b6bfb..bbcc835155 100644 --- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ConfidentialClientApplicationTests.cs @@ -898,6 +898,7 @@ public async Task AcquireTokenForClient_EmptyAssertion_ThrowsArgumentExceptionAs { // Build a CCA whose assertion‑delegate returns NO JWT (error case) var cca = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) + .WithExperimentalFeatures(true) .WithClientSecret(TestConstants.ClientSecret) .WithClientAssertion( (opts, ct) => Task.FromResult(new ClientSignedAssertion