Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func<string> cli
throw new ArgumentNullException(nameof(clientAssertionDelegate));
}

return WithClientAssertion(
return WithClientAssertionInternal(
(opts, ct) =>
Task.FromResult(new ClientSignedAssertion
{
Expand All @@ -251,7 +251,7 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func<Cancellatio
throw new ArgumentNullException(nameof(clientAssertionAsyncDelegate));
}

return WithClientAssertion(
return WithClientAssertionInternal(
async (opts, ct) =>
{
string jwt = await clientAssertionAsyncDelegate(ct).ConfigureAwait(false);
Expand All @@ -273,7 +273,7 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func<AssertionRe
throw new ArgumentNullException(nameof(clientAssertionAsyncDelegate));
}

return WithClientAssertion(
return WithClientAssertionInternal(
async (opts, _) =>
{
string jwt = await clientAssertionAsyncDelegate(opts).ConfigureAwait(false);
Expand All @@ -295,6 +295,18 @@ public ConfidentialClientApplicationBuilder WithClientAssertion(Func<AssertionRe
/// <exception cref="MsalClientException">Thrown if <paramref name="clientSignedAssertionProvider"/> is <see langword="null"/>.</exception>
public ConfidentialClientApplicationBuilder WithClientAssertion(Func<AssertionRequestOptions,
CancellationToken, Task<ClientSignedAssertion>> clientSignedAssertionProvider)
{
ValidateUseOfExperimentalFeature();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have telemetry that this does not break anyone?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mTLS POP is an internal only feature, so we should not be breaking anyone here.

Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem to be limited to MTLS though is it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that's a good point @neha-bhargava and @trwalke. I will let @bgavrilMS know about this.

return WithClientAssertionInternal(clientSignedAssertionProvider);
}

/// <summary>
/// Internal helper to set the client assertion provider.
/// </summary>
/// <param name="clientSignedAssertionProvider"></param>
/// <returns></returns>
internal ConfidentialClientApplicationBuilder WithClientAssertionInternal(
Func<AssertionRequestOptions, CancellationToken, Task<ClientSignedAssertion>> clientSignedAssertionProvider)
{
Config.ClientCredential = new ClientAssertionDelegateCredential(clientSignedAssertionProvider);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand All @@ -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)
Expand Down Expand Up @@ -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 }))
Expand All @@ -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) =>
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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();

Expand All @@ -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) =>
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading