diff --git a/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.cs b/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.cs
index 04995d6614..14665d317f 100644
--- a/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.cs
@@ -56,6 +56,24 @@ internal static AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder Cr
.WithScopes(scopes);
}
+ ///
+ /// Applicable to first-party applications only, this method also allows to specify
+ /// if the x5c claim should be sent to Azure AD.
+ /// Sending the x5c enables application developers to achieve easy certificate roll-over in Azure AD:
+ /// this method will send the certificate chain to Azure AD along with the token request,
+ /// so that Azure AD can use it to validate the subject name based on a trusted issuer policy.
+ /// This saves the application admin from the need to explicitly manage the certificate rollover
+ /// (either via portal or PowerShell/CLI operation). For details see https://aka.ms/msal-net-sni
+ ///
+ /// true if the x5c should be sent. Otherwise false.
+ /// The default is false
+ /// The builder to chain the .With methods
+ public AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder WithSendX5C(bool withSendX5C)
+ {
+ Parameters.SendX5C = withSendX5C;
+ return this;
+ }
+
///
internal override Task ExecuteInternalAsync(CancellationToken cancellationToken)
{
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
index 5f282702bb..ef9185312d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-
\ No newline at end of file
+Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
+
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
index 5f282702bb..ef9185312d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-
\ No newline at end of file
+Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
+
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
index 5f282702bb..ef9185312d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-
\ No newline at end of file
+Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
+
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
index 5f282702bb..ef9185312d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-
\ No newline at end of file
+Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
+
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
index 5f282702bb..ef9185312d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-
\ No newline at end of file
+Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
+
diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
index 5f282702bb..ef9185312d 100644
--- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-
\ No newline at end of file
+Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder.WithSendX5C(bool withSendX5C) -> Microsoft.Identity.Client.AcquireTokenByUsernameAndPasswordConfidentialParameterBuilder
+
diff --git a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientCredentialWithCertTest.cs b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientCredentialWithCertTest.cs
index 67490087db..5e03718004 100644
--- a/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientCredentialWithCertTest.cs
+++ b/tests/Microsoft.Identity.Test.Unit/PublicApiTests/ClientCredentialWithCertTest.cs
@@ -819,6 +819,43 @@ public async Task RopcCcaSendsX5CAsync(bool sendX5C)
}
}
+ [DataTestMethod]
+ [DataRow(true)]
+ [DataRow(false)]
+ public async Task RopcCcaSendsX5CUsingRequestLevelAPIAsync(bool sendX5C)
+ {
+ using (var harness = CreateTestHarness())
+ {
+ var certificate = CertHelper.GetOrCreateTestCert();
+ var exportedCertificate = Convert.ToBase64String(certificate.Export(X509ContentType.Cert));
+
+ var app = ConfidentialClientApplicationBuilder
+ .Create(TestConstants.ClientId)
+ .WithHttpManager(harness.HttpManager)
+ .WithCertificate(certificate)
+ .Build();
+
+ harness.HttpManager.AddInstanceDiscoveryMockHandler();
+
+ harness.HttpManager.AddMockHandler(
+ CreateTokenResponseHttpHandlerWithX5CValidation(
+ clientCredentialFlow: false,
+ expectedX5C: sendX5C ? exportedCertificate : null));
+
+ var result = await (app as IByUsernameAndPassword)
+ .AcquireTokenByUsernamePassword(
+ TestConstants.s_scope,
+ TestConstants.Username,
+ TestConstants.DefaultPassword)
+ .WithSendX5C(sendX5C)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource);
+ }
+ }
+
[TestMethod]
public async Task EnsureCertificateSerialNumberIsAddedToCacheKeyTestAsync()
{