From 617394d6ff834b0804cceabff2ed9d6e06342cd4 Mon Sep 17 00:00:00 2001
From: Cecilia Avila <44245136+ceciliaavila@users.noreply.github.com>
Date: Thu, 12 Oct 2023 11:04:05 -0300
Subject: [PATCH] Add sendx5c parameter to Certificate factory class (#6699)
* Add sendX5c parameter in certificate auth factory
* Add unit tests
* Remove commented property.
* Revert changes in CertificateServiceClientCredentialsFactory
---
.../Authentication/MsalAppCredentials.cs | 27 +++++
...salServiceClientCredentialsFactoryTests.cs | 98 +++++++++++++++++++
2 files changed, 125 insertions(+)
create mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/MsalServiceClientCredentialsFactoryTests.cs
diff --git a/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs
index 63524e8194..aaa048d5f6 100644
--- a/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs
+++ b/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs
@@ -106,6 +106,33 @@ public MsalAppCredentials(string appId, X509Certificate2 certificate, string aut
.Build();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Microsoft application id.
+ /// The certificate to use for authentication.
+ /// If true will send the public certificate 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.
+ /// Optional switch for whether to validate the authority.
+ /// Optional authority.
+ /// Optional custom scope.
+ /// Optional .
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2234:Pass system uri objects instead of strings", Justification = "Using string overload for legacy compatibility.")]
+ public MsalAppCredentials(string appId, X509Certificate2 certificate, bool sendX5c, string authority = null, string scope = null, bool validateAuthority = true, ILogger logger = null)
+ : this(
+ clientApplication: null,
+ appId: appId,
+ authority: authority,
+ scope: scope,
+ validateAuthority: validateAuthority,
+ logger: logger)
+ {
+ _clientApplication = ConfidentialClientApplicationBuilder.Create(appId)
+ .WithAuthority(authority ?? OAuthEndpoint, validateAuthority)
+ .WithCertificate(certificate, sendX5c)
+ .Build();
+ }
+
async Task IAuthenticator.GetTokenAsync(bool forceRefresh)
{
var watch = Stopwatch.StartNew();
diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/MsalServiceClientCredentialsFactoryTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/MsalServiceClientCredentialsFactoryTests.cs
new file mode 100644
index 0000000000..bd89bffb04
--- /dev/null
+++ b/tests/Microsoft.Bot.Connector.Tests/Authentication/MsalServiceClientCredentialsFactoryTests.cs
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Bot.Connector.Authentication;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Identity.Client;
+using Moq;
+using Xunit;
+
+namespace Microsoft.Bot.Connector.Tests.Authentication
+{
+ public class MsalServiceClientCredentialsFactoryTests
+ {
+ private const string TestAppId = nameof(TestAppId);
+ private const string TestTenantId = nameof(TestTenantId);
+ private const string TestAudience = nameof(TestAudience);
+ private const string LoginEndpoint = "https://login.microsoftonline.com";
+ private const string LoginEndpointGov = "https://login.microsoftonline.us/MicrosoftServices.onmicrosoft.us";
+ private readonly Mock logger = new Mock();
+ private readonly Mock configuration = new Mock();
+ private readonly Mock clientApplication = new Mock();
+
+ [Fact]
+ public void ConstructorTests()
+ {
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+
+ Assert.NotNull(factory);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public async Task ShouldReturnEmptyCredentialsWithoutAppId(string appId)
+ {
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+ var credentials = await factory.CreateCredentialsAsync(appId, TestAudience, LoginEndpoint, true, CancellationToken.None);
+
+ Assert.Equal(MsalAppCredentials.Empty, credentials);
+ }
+
+ [Fact]
+ public void ShouldThrowIfAppIdDoesNotMatch()
+ {
+ configuration.Setup(x => x.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey).Value).Returns(TestAppId);
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+
+ Assert.ThrowsAsync(() => factory.CreateCredentialsAsync(
+ "InvalidAppId", TestAudience, LoginEndpoint, true, CancellationToken.None));
+ }
+
+ [Fact]
+ public void ShouldCreateCredentials()
+ {
+ configuration.Setup(x => x.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey).Value).Returns(TestAppId);
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+ var credentials = factory.CreateCredentialsAsync(TestAppId, TestAudience, LoginEndpoint, true, CancellationToken.None).GetAwaiter().GetResult();
+
+ Assert.NotNull(credentials);
+ Assert.IsType(credentials);
+ }
+
+ [Fact]
+ public void ShouldCreateCredentialsForGoverment()
+ {
+ configuration.Setup(x => x.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey).Value).Returns(TestAppId);
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+ var credentials = factory.CreateCredentialsAsync(TestAppId, TestAudience, LoginEndpointGov, true, CancellationToken.None).GetAwaiter().GetResult();
+
+ Assert.NotNull(credentials);
+ Assert.IsType(credentials);
+ }
+
+ [Fact]
+ public void IsValidAppIdTest()
+ {
+ configuration.Setup(x => x.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey).Value).Returns(TestAppId);
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+
+ Assert.True(factory.IsValidAppIdAsync(TestAppId, CancellationToken.None).GetAwaiter().GetResult());
+ Assert.False(factory.IsValidAppIdAsync("InvalidAppId", CancellationToken.None).GetAwaiter().GetResult());
+ }
+
+ [Fact]
+ public void IsAuthenticationDisabledTest()
+ {
+ configuration.Setup(x => x.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey).Value).Returns(string.Empty);
+ var factory = new MsalServiceClientCredentialsFactory(configuration.Object, clientApplication.Object, logger.Object);
+
+ Assert.True(factory.IsAuthenticationDisabledAsync(CancellationToken.None).GetAwaiter().GetResult());
+ }
+ }
+}