From f3ed3501d374d859b4ddbc7ac82283c3911c3ca1 Mon Sep 17 00:00:00 2001 From: Hongyang Du Date: Tue, 29 Aug 2023 16:13:45 +0800 Subject: [PATCH 1/5] init --- .../Authentication/AdalAuthenticator.cs | 337 ------------------ .../Authentication/AppCredentials.cs | 18 +- .../CertificateAppCredentials.cs | 54 --- .../ConstantHttpClientFactory.cs | 4 +- .../IJwtTokenProviderFactory.cs | 24 -- .../Authentication/JwtTokenProviderFactory.cs | 30 -- .../ManagedIdentityAppCredentials.cs | 23 -- .../ManagedIdentityAuthenticator.cs | 14 - ...IdentityServiceClientCredentialsFactory.cs | 13 - .../Authentication/MicrosoftAppCredentials.cs | 18 - .../Authentication/MsalAppCredentials.cs | 7 - .../Microsoft.Bot.Connector.csproj | 1 - .../MockAppCredentials.cs | 24 -- .../Authentication/AppCredentialsTests.cs | 75 ---- .../JwtTokenProviderFactoryTests.cs | 47 --- .../ManagedIdentityAppCredentialsTests.cs | 76 ---- .../ManagedIdentityAuthenticatorTests.cs | 178 --------- ...ityServiceClientCredentialsFactoryTests.cs | 99 ----- .../Mocks/MockAppCredentials.cs | 7 +- 19 files changed, 6 insertions(+), 1043 deletions(-) delete mode 100644 libraries/Microsoft.Bot.Connector/Authentication/AdalAuthenticator.cs delete mode 100644 libraries/Microsoft.Bot.Connector/Authentication/IJwtTokenProviderFactory.cs delete mode 100644 libraries/Microsoft.Bot.Connector/Authentication/JwtTokenProviderFactory.cs delete mode 100644 tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs delete mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/AppCredentialsTests.cs delete mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/JwtTokenProviderFactoryTests.cs delete mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAppCredentialsTests.cs delete mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs delete mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityServiceClientCredentialsFactoryTests.cs diff --git a/libraries/Microsoft.Bot.Connector/Authentication/AdalAuthenticator.cs b/libraries/Microsoft.Bot.Connector/Authentication/AdalAuthenticator.cs deleted file mode 100644 index 1c8b4cd0a1..0000000000 --- a/libraries/Microsoft.Bot.Connector/Authentication/AdalAuthenticator.cs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Diagnostics; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.IdentityModel.Clients.ActiveDirectory; - -namespace Microsoft.Bot.Connector.Authentication -{ - /// - /// An authentication class that implements , used to authenticate requests against Azure. - /// - public class AdalAuthenticator : IAuthenticator - { - private const string MsalTemporarilyUnavailable = "temporarily_unavailable"; - - // When TargetFramework moves to netstandard2.1, use HttpStatusCode.TooManyRequests - // https://docs.microsoft.com/en-us/dotnet/api/system.net.httpstatuscode?view=netstandard-2.1#System_Net_HttpStatusCode_TooManyRequests - private const int HttpTooManyRequests = 429; - - // Semaphore to control concurrency while refreshing tokens from ADAL. - // Whenever a token expires, we want only one request to retrieve a token. - // Cached requests take less than 0.1 millisecond to resolve, so the semaphore doesn't hurt performance under load tests - // unless we have more than 10,000 requests per second, but in that case other things would break first. - private static SemaphoreSlim tokenRefreshSemaphore = new SemaphoreSlim(1, 1); - private static readonly TimeSpan SemaphoreTimeout = TimeSpan.FromSeconds(10); - - // Depending on the responses we get from the service, we update a shared retry policy with the RetryAfter header - // from the HTTP 429 we receive. - // When everything seems to be OK, this retry policy will be empty. - // The reason for this is that if a request gets throttled, even if we wait to retry that, another thread will try again right away. - // With the shared retry policy, if a request gets throttled, we know that other threads have to wait as well. - // This variable is guarded by the authContextSemaphore semaphore. Don't modify it outside of the semaphore scope. - private static volatile RetryParams currentRetryPolicy; - - // Our ADAL context. Acquires tokens and manages token caching for us. - private AuthenticationContext authContext; - - private readonly ClientCredential clientCredential; - private readonly ClientAssertionCertificate clientCertificate; - private readonly bool clientCertSendX5c; - private readonly OAuthConfiguration authConfig; - private readonly ILogger logger; - - /// - /// Initializes a new instance of the class. - /// - /// The client credential to use for token acquisition. - /// A configuration object for OAuth client credential authentication. - /// A customized instance of the HttpClient class. - public AdalAuthenticator(ClientCredential clientCredential, OAuthConfiguration configurationOAuth, HttpClient customHttpClient = null) - : this(clientCredential, configurationOAuth, customHttpClient, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The client credential to use for token acquisition. - /// A configuration object for OAuth client credential authentication. - /// A customized instance of the HttpClient class. - /// The type used to perform logging. - public AdalAuthenticator(ClientCredential clientCredential, OAuthConfiguration configurationOAuth, HttpClient customHttpClient = null, ILogger logger = null) - { - this.authConfig = configurationOAuth ?? throw new ArgumentNullException(nameof(configurationOAuth)); - this.clientCredential = clientCredential ?? throw new ArgumentNullException(nameof(clientCredential)); - this.logger = logger; - - Initialize(configurationOAuth, customHttpClient); - } - - /// - /// Initializes a new instance of the class. - /// - /// A client credential that includes a X509Certificate. - /// A configuration object for OAuth client credential authentication. - /// A customized instance of the HttpClient class. - /// The type used to perform logging. - public AdalAuthenticator(ClientAssertionCertificate clientCertificate, OAuthConfiguration configurationOAuth, HttpClient customHttpClient = null, ILogger logger = null) - : this(clientCertificate, false, configurationOAuth, customHttpClient, logger) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A client credential that includes a X509Certificate. - /// Enables easy certificates roll-over in Azure AD. Setting it to true sends the public certficate to Azure AD along with token requests. - /// A configuration object for OAuth client credential authentication. - /// A customized instance of the HttpClient class. - /// The type used to perform logging. - public AdalAuthenticator(ClientAssertionCertificate clientCertificate, bool sendX5c, OAuthConfiguration configurationOAuth, HttpClient customHttpClient = null, ILogger logger = null) - { - this.authConfig = configurationOAuth ?? throw new ArgumentNullException(nameof(configurationOAuth)); - this.clientCertificate = clientCertificate ?? throw new ArgumentNullException(nameof(clientCertificate)); - this.logger = logger; - this.clientCertSendX5c = sendX5c; - - Initialize(configurationOAuth, customHttpClient); - } - - /// - /// Performs the call to acquire a security token. - /// - /// Forces a token refresh by clearing the token cache and acquiring it again. - /// A object. - public async Task GetTokenAsync(bool forceRefresh = false) - { - var watch = Stopwatch.StartNew(); - - var result = await Retry.Run( - task: () => AcquireTokenAsync(forceRefresh), - retryExceptionHandler: (ex, ct) => HandleAdalException(ex, ct)).ConfigureAwait(false); - - watch.Stop(); - logger?.LogInformation($"GetTokenAsync: Acquired token using ADAL in {watch.ElapsedMilliseconds}."); - - return result; - } - - async Task IAuthenticator.GetTokenAsync(bool forceRefresh) - { - var result = await GetTokenAsync(forceRefresh).ConfigureAwait(false); - return new AuthenticatorResult() - { - AccessToken = result.AccessToken, - ExpiresOn = result.ExpiresOn - }; - } - - private static RetryParams HandleAdalException(Exception ex, int currentRetryCount) - { - if (IsAdalServiceUnavailable(ex)) - { - return ComputeAdalRetry(ex); - } - else if (ex is ThrottleException) - { - // This is an exception that we threw, with knowledge that - // one of our threads is trying to acquire a token from the server - // Use the retry parameters recommended in the exception - ThrottleException throttlException = (ThrottleException)ex; - return throttlException.RetryParams ?? RetryParams.DefaultBackOff(currentRetryCount); - } - else if (IsAdalServiceInvalidRequest(ex)) - { - return RetryParams.StopRetrying; - } - else - { - // We end up here is the exception is not an ADAL exception. An example, is under high traffic - // where we could have a timeout waiting to acquire a token, waiting on the semaphore. - // If we hit a timeout, we want to retry a reasonable number of times. - return RetryParams.DefaultBackOff(currentRetryCount); - } - } - - private static bool IsAdalServiceUnavailable(Exception ex) - { - AdalServiceException adalServiceException = ex as AdalServiceException; - if (adalServiceException == null) - { - return false; - } - - // When the Service Token Server (STS) is too busy because of “too many requests”, - // it returns an HTTP error 429 - return adalServiceException.ErrorCode == MsalTemporarilyUnavailable || adalServiceException.StatusCode == HttpTooManyRequests; - } - - /// - /// Determine whether exception represents an invalid request from AAD. - /// - /// Exception. - /// True if the exception represents an invalid request. - private static bool IsAdalServiceInvalidRequest(Exception ex) - { - if (ex is AdalServiceException adal) - { - // ErrorCode is "invalid_request" - // but HTTP StatusCode covers more non-retryable conditions - if (adal.StatusCode == (int)HttpStatusCode.BadRequest) - { - return true; - } - } - - return false; - } - - private static RetryParams ComputeAdalRetry(Exception ex) - { - if (ex is AdalServiceException) - { - AdalServiceException adalServiceException = (AdalServiceException)ex; - - // When the Service Token Server (STS) is too busy because of “too many requests”, - // it returns an HTTP error 429 with a hint about when you can try again (Retry-After response field) as a delay in seconds - if (adalServiceException.ErrorCode == MsalTemporarilyUnavailable || adalServiceException.StatusCode == HttpTooManyRequests) - { - RetryConditionHeaderValue retryAfter = adalServiceException.Headers.RetryAfter; - - // Depending on the service, the recommended retry time may be in retryAfter.Delta or retryAfter.Date. Check both. - if (retryAfter != null && retryAfter.Delta.HasValue) - { - return new RetryParams(retryAfter.Delta.Value); - } - else if (retryAfter != null && retryAfter.Date.HasValue) - { - return new RetryParams(retryAfter.Date.Value.Offset); - } - - // We got a 429 but didn't get a specific back-off time. Use the default - return RetryParams.DefaultBackOff(0); - } - } - - return RetryParams.DefaultBackOff(0); - } - - private void ReleaseSemaphore() - { - try - { - tokenRefreshSemaphore.Release(); - } - catch (SemaphoreFullException) - { - // We should not be hitting this after switching to SemaphoreSlim, but if we do hit it everything will keep working. - // Logging to have clear knowledge of whether this is happening. - logger?.LogWarning("Attempted to release a full semaphore."); - } - - // Any exception other than SemaphoreFullException should be thrown right away - } - - private void Initialize(OAuthConfiguration configurationOAuth, HttpClient customHttpClient) - { - if (customHttpClient != null) - { - var httpClientFactory = new ConstantHttpClientFactory(customHttpClient); - this.authContext = new AuthenticationContext(configurationOAuth.Authority, configurationOAuth.ValidateAuthority, new TokenCache(), httpClientFactory); - } - else - { - this.authContext = new AuthenticationContext(configurationOAuth.Authority, configurationOAuth.ValidateAuthority); - } - } - - private async Task AcquireTokenAsync(bool forceRefresh = false) - { - bool acquired = false; - - if (forceRefresh) - { - authContext.TokenCache.Clear(); - } - - try - { - // The ADAL client team recommends limiting concurrency of calls. When the Token is in cache there is never - // contention on this semaphore, but when tokens expire there is some. However, after measuring performance - // with and without the semaphore (and different configs for the semaphore), not limiting concurrency actually - // results in higher response times overall. Without the use of this semaphore calls to AcquireTokenAsync can take up - // to 5 seconds under high concurrency scenarios. -#pragma warning disable VSTHRD103 // Call async methods when in an async method - acquired = tokenRefreshSemaphore.Wait(SemaphoreTimeout); -#pragma warning restore VSTHRD103 // Call async methods when in an async method - - // If we are allowed to enter the semaphore, acquire the token. - if (acquired) - { - // Acquire token async using MSAL.NET - // https://github.com/AzureAD/azure-activedirectory-library-for-dotnet - // Given that this is a ClientCredential scenario, it will use the cache without the - // need to call AcquireTokenSilentAsync (which is only for user credentials). - // Scenario details: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Client-credential-flows#it-uses-the-application-token-cache - AuthenticationResult authResult = null; - - // Password based auth - if (clientCredential != null) - { - authResult = await authContext.AcquireTokenAsync(authConfig.Scope, this.clientCredential).ConfigureAwait(false); - } - - // Certificate based auth - else if (clientCertificate != null) - { - authResult = await authContext.AcquireTokenAsync(authConfig.Scope, clientCertificate, sendX5c: this.clientCertSendX5c).ConfigureAwait(false); - } - - // This means we acquired a valid token successfully. We can make our retry policy null. - // Note that the retry policy is set under the semaphore so no additional synchronization is needed. - if (currentRetryPolicy != null) - { - currentRetryPolicy = null; - } - - return authResult; - } - else - { - // If the token is taken, it means that one thread is trying to acquire a token from the server. - // If we already received information about how much to throttle, it will be in the currentRetryPolicy. - // Use that to inform our next delay before trying. - throw new ThrottleException() { RetryParams = currentRetryPolicy }; - } - } - catch (Exception ex) - { - // If we are getting throttled, we set the retry policy according to the RetryAfter headers - // that we receive from the auth server. - // Note that the retry policy is set under the semaphore so no additional synchronization is needed. - if (IsAdalServiceUnavailable(ex)) - { - currentRetryPolicy = ComputeAdalRetry(ex); - } - - throw; - } - finally - { - // Always release the semaphore if we acquired it. - if (acquired) - { - ReleaseSemaphore(); - } - } - } - } -} diff --git a/libraries/Microsoft.Bot.Connector/Authentication/AppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/AppCredentials.cs index 012bb644d6..bc2e335099 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/AppCredentials.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/AppCredentials.cs @@ -213,27 +213,11 @@ public async Task GetTokenAsync(bool forceRefresh = false) return token.AccessToken; } - /// - /// Builds the lazy to be used for token acquisition. - /// - /// A lazy . - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected abstract Lazy BuildAuthenticator(); - /// /// Builds the lazy to be used for token acquisition. /// /// A lazy . - protected virtual Lazy BuildIAuthenticator() - { - return new Lazy( - () => - { - var lazyAuthenticator = BuildAuthenticator(); - return lazyAuthenticator.Value; - }, - LazyThreadSafetyMode.ExecutionAndPublication); - } + protected abstract Lazy BuildIAuthenticator(); private bool ShouldSetToken() { diff --git a/libraries/Microsoft.Bot.Connector/Authentication/CertificateAppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/CertificateAppCredentials.cs index e46938e10d..c6b6c86295 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/CertificateAppCredentials.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/CertificateAppCredentials.cs @@ -6,7 +6,6 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using Microsoft.Extensions.Logging; -using Microsoft.IdentityModel.Clients.ActiveDirectory; namespace Microsoft.Bot.Connector.Authentication { @@ -15,7 +14,6 @@ namespace Microsoft.Bot.Connector.Authentication /// public class CertificateAppCredentials : AppCredentials { - private readonly ClientAssertionCertificate adalClientCertificate; private readonly X509Certificate2 clientCertificate; private readonly bool sendX5c; @@ -81,58 +79,6 @@ public CertificateAppCredentials(X509Certificate2 clientCertificate, bool sendX5 MicrosoftAppId = appId; } - /// - /// Initializes a new instance of the class. - /// - /// Client certificate to be presented for authentication. - /// Optional. The oauth token tenant. - /// Optional to be used when acquiring tokens. - /// Optional to gather telemetry data while acquiring and managing credentials. - public CertificateAppCredentials(ClientAssertionCertificate clientCertificate, string channelAuthTenant = null, HttpClient customHttpClient = null, ILogger logger = null) - : this(clientCertificate, false, channelAuthTenant, customHttpClient, logger) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Client certificate to be presented for authentication. - /// This parameter, if true, enables application developers to achieve easy certificates roll-over in Azure AD: setting this parameter to 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. The oauth token tenant. - /// Optional to be used when acquiring tokens. - /// Optional to gather telemetry data while acquiring and managing credentials. - public CertificateAppCredentials(ClientAssertionCertificate clientCertificate, bool sendX5c, string channelAuthTenant = null, HttpClient customHttpClient = null, ILogger logger = null) - : base(channelAuthTenant, customHttpClient, logger) - { - if (clientCertificate == null) - { - throw new ArgumentNullException(nameof(clientCertificate)); - } - - this.sendX5c = sendX5c; - this.clientCertificate = clientCertificate.Certificate; - MicrosoftAppId = clientCertificate.ClientId; - adalClientCertificate = clientCertificate; - } - - /// - /// Builds the lazy to be used for token acquisition. - /// - /// A lazy . - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() - { - return new Lazy( - () => - new AdalAuthenticator( - adalClientCertificate, - sendX5c, - new OAuthConfiguration() { Authority = OAuthEndpoint, ValidateAuthority = ValidateAuthority, Scope = OAuthScope }, - CustomHttpClient, - Logger), - LazyThreadSafetyMode.ExecutionAndPublication); - } - /// protected override Lazy BuildIAuthenticator() { diff --git a/libraries/Microsoft.Bot.Connector/Authentication/ConstantHttpClientFactory.cs b/libraries/Microsoft.Bot.Connector/Authentication/ConstantHttpClientFactory.cs index e9d9e8d64f..b13880adc5 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/ConstantHttpClientFactory.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/ConstantHttpClientFactory.cs @@ -8,9 +8,9 @@ namespace Microsoft.Bot.Connector.Authentication { /// - /// HttpClientFactory that always returns the same HttpClient instance for ADAL AcquireTokenAsync calls. + /// HttpClientFactory that always returns the same HttpClient instance for AcquireTokenAsync calls. /// - internal class ConstantHttpClientFactory : IdentityModel.Clients.ActiveDirectory.IHttpClientFactory, IMsalHttpClientFactory + internal class ConstantHttpClientFactory : IMsalHttpClientFactory { private readonly HttpClient httpClient; diff --git a/libraries/Microsoft.Bot.Connector/Authentication/IJwtTokenProviderFactory.cs b/libraries/Microsoft.Bot.Connector/Authentication/IJwtTokenProviderFactory.cs deleted file mode 100644 index cebc8ee3bc..0000000000 --- a/libraries/Microsoft.Bot.Connector/Authentication/IJwtTokenProviderFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Azure.Services.AppAuthentication; - -namespace Microsoft.Bot.Connector.Authentication -{ - /// - /// A factory that can create OAuth token providers for generating JWT auth tokens. - /// - [Obsolete("This class is deprecated.", false)] - public interface IJwtTokenProviderFactory - { - /// - /// Creates a new instance of the class. - /// - /// Client id for the managed identity to be used for acquiring tokens. - /// A customized instance of the HttpClient class. - /// A new instance of the class. - AzureServiceTokenProvider CreateAzureServiceTokenProvider(string appId, HttpClient customHttpClient = null); - } -} diff --git a/libraries/Microsoft.Bot.Connector/Authentication/JwtTokenProviderFactory.cs b/libraries/Microsoft.Bot.Connector/Authentication/JwtTokenProviderFactory.cs deleted file mode 100644 index 42172ea7a1..0000000000 --- a/libraries/Microsoft.Bot.Connector/Authentication/JwtTokenProviderFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Azure.Services.AppAuthentication; - -namespace Microsoft.Bot.Connector.Authentication -{ - /// - [Obsolete("This class is deprecated.", false)] - public class JwtTokenProviderFactory : IJwtTokenProviderFactory - { - /// - public AzureServiceTokenProvider CreateAzureServiceTokenProvider(string appId, HttpClient customHttpClient = null) - { - if (string.IsNullOrWhiteSpace(appId)) - { - throw new ArgumentNullException(nameof(appId)); - } - - // https://docs.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=dotnet - // "RunAs=App;AppId=" for user-assigned managed identities - var connectionString = $"RunAs=App;AppId={appId}"; - return customHttpClient == null - ? new AzureServiceTokenProvider(connectionString) - : new AzureServiceTokenProvider(connectionString, httpClientFactory: new ConstantHttpClientFactory(customHttpClient)); - } - } -} diff --git a/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAppCredentials.cs index 7a6fccbb74..c624766732 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAppCredentials.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAppCredentials.cs @@ -13,21 +13,6 @@ namespace Microsoft.Bot.Connector.Authentication /// public class ManagedIdentityAppCredentials : AppCredentials { - /// - /// Initializes a new instance of the class. - /// Managed Identity for AAD credentials auth and caching. - /// - /// Client ID for the managed identity assigned to the bot. - /// The scope for the token. - /// The JWT token provider factory to use. - /// Optional to be used when acquiring tokens. - /// Optional to gather telemetry data while acquiring and managing credentials. - [Obsolete("This method is deprecated, the IJwtTokenProviderFactory argument is now redundant. Use the overload without this argument.", false)] - public ManagedIdentityAppCredentials(string appId, string oAuthScope, IJwtTokenProviderFactory tokenProviderFactory, HttpClient customHttpClient = null, ILogger logger = null) - : this(appId, oAuthScope, customHttpClient, logger) - { - } - /// /// Initializes a new instance of the class. /// Managed Identity for AAD credentials auth and caching. @@ -47,14 +32,6 @@ public ManagedIdentityAppCredentials(string appId, string oAuthScope, HttpClient MicrosoftAppId = appId; } - /// - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() - { - // Should not be called, legacy - throw new NotImplementedException(); - } - /// protected override Lazy BuildIAuthenticator() { diff --git a/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAuthenticator.cs b/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAuthenticator.cs index b16078809a..14ced8afba 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAuthenticator.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityAuthenticator.cs @@ -21,20 +21,6 @@ public class ManagedIdentityAuthenticator : IAuthenticator private readonly ILogger _logger; private readonly IConfidentialClientApplication _clientApplication; - /// - /// Initializes a new instance of the class. - /// - /// Client id for the managed identity to be used for acquiring tokens. - /// Resource for which to acquire the token. - /// The JWT token provider factory to use. - /// A customized instance of the HttpClient class. - /// The type used to perform logging. - [Obsolete("This method is deprecated, the IJwtTokenProviderFactory argument is now redundant. Use the overload without this argument.", false)] - public ManagedIdentityAuthenticator(string appId, string resource, IJwtTokenProviderFactory tokenProviderFactory, HttpClient customHttpClient = null, ILogger logger = null) - : this(appId, resource, customHttpClient, logger) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityServiceClientCredentialsFactory.cs b/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityServiceClientCredentialsFactory.cs index 5c6e40b416..70bcc21caa 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityServiceClientCredentialsFactory.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/ManagedIdentityServiceClientCredentialsFactory.cs @@ -19,19 +19,6 @@ public class ManagedIdentityServiceClientCredentialsFactory : ServiceClientCrede private readonly HttpClient _httpClient; private readonly ILogger _logger; - /// - /// Initializes a new instance of the class. - /// - /// Client ID for the managed identity assigned to the bot. - /// The JWT token provider factory to use. - /// A custom httpClient to use. - /// A logger instance to use. - [Obsolete("This method is deprecated, the IJwtTokenProviderFactory argument is now redundant. Use the overload without this argument.", false)] - public ManagedIdentityServiceClientCredentialsFactory(string appId, IJwtTokenProviderFactory tokenProviderFactory, HttpClient httpClient = null, ILogger logger = null) - : this(appId, httpClient, logger) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/libraries/Microsoft.Bot.Connector/Authentication/MicrosoftAppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/MicrosoftAppCredentials.cs index eb6b712212..add80a0471 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/MicrosoftAppCredentials.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/MicrosoftAppCredentials.cs @@ -5,7 +5,6 @@ using System.Net.Http; using System.Threading; using Microsoft.Extensions.Logging; -using Microsoft.IdentityModel.Clients.ActiveDirectory; namespace Microsoft.Bot.Connector.Authentication { @@ -134,23 +133,6 @@ public MicrosoftAppCredentials(string appId, string password, string channelAuth /// public string MicrosoftAppPassword { get; set; } - /// - /// Builds the lazy to be used for token acquisition. - /// - /// A lazy . - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() - { - return new Lazy( - () => - new AdalAuthenticator( - new ClientCredential(MicrosoftAppId, MicrosoftAppPassword), - new OAuthConfiguration() { Authority = OAuthEndpoint, ValidateAuthority = ValidateAuthority, Scope = OAuthScope }, - this.CustomHttpClient, - this.Logger), - LazyThreadSafetyMode.ExecutionAndPublication); - } - /// protected override Lazy BuildIAuthenticator() { diff --git a/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs index 0bdb1bc47f..63524e8194 100644 --- a/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs +++ b/libraries/Microsoft.Bot.Connector/Authentication/MsalAppCredentials.cs @@ -120,13 +120,6 @@ async Task IAuthenticator.GetTokenAsync(bool forceRefresh) return result; } - /// - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() - { - throw new NotImplementedException(); - } - /// protected override Lazy BuildIAuthenticator() { diff --git a/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj b/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj index 244dea2297..66e3628af5 100644 --- a/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj +++ b/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj @@ -26,7 +26,6 @@ - diff --git a/tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs b/tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs deleted file mode 100644 index 2b84860c38..0000000000 --- a/tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Logging; - -namespace Microsoft.Bot.Builder.Tests -{ - public class MockAppCredentials : AppCredentials - { - public MockAppCredentials(string channelAuthTenant = null, HttpClient customHttpClient = null, ILogger logger = null) - : base(channelAuthTenant, customHttpClient, logger) - { - } - - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() - { - return new Lazy(); - } - } -} diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/AppCredentialsTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/AppCredentialsTests.cs deleted file mode 100644 index 159f992dc5..0000000000 --- a/tests/Microsoft.Bot.Connector.Tests/Authentication/AppCredentialsTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace Microsoft.Bot.Connector.Tests.Authentication -{ - public class AppCredentialsTests - { - [Fact] - public void ConstructorTests() - { - var shouldDefaultToChannelScope = new TestAppCredentials("irrelevant", null, null); - Assert.Equal(AuthenticationConstants.ToChannelFromBotOAuthScope, shouldDefaultToChannelScope.OAuthScope); - - var shouldDefaultToCustomScope = new TestAppCredentials("irrelevant", null, null, "customScope"); - Assert.Equal("customScope", shouldDefaultToCustomScope.OAuthScope); - } - - [Fact] - public async Task ProcessHttpRequestShouldNotSendTokenForAnonymous() - { - var sut = new TestAppCredentials("irrelevant", null, null) - { - MicrosoftAppId = null - }; - - // AppId is null. - await sut.ProcessHttpRequestAsync(new HttpRequestMessage(), CancellationToken.None); - Assert.Null(sut.Request.Headers.Authorization); - - // AppId is anonymous skill. - sut.MicrosoftAppId = AuthenticationConstants.AnonymousSkillAppId; - Assert.Null(sut.Request.Headers.Authorization); - } - - private class TestAppCredentials : AppCredentials - { - public TestAppCredentials(string channelAuthTenant = null, HttpClient customHttpClient = null, ILogger logger = null) - : base(channelAuthTenant, customHttpClient, logger) - { - } - - public TestAppCredentials(string channelAuthTenant = null, HttpClient customHttpClient = null, ILogger logger = null, string oAuthScope = null) - : base(channelAuthTenant, customHttpClient, logger, oAuthScope) - { - } - - /// - /// Gets the request sent to . - /// - public HttpRequestMessage Request { get; private set; } - - public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - // This override calls the base and captures the modified request so we can assert the Auth header. - await base.ProcessHttpRequestAsync(request, cancellationToken); - Request = request; - } - - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() - { - return new Mock>().Object; - } - } - } -} diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/JwtTokenProviderFactoryTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/JwtTokenProviderFactoryTests.cs deleted file mode 100644 index b59ce06275..0000000000 --- a/tests/Microsoft.Bot.Connector.Tests/Authentication/JwtTokenProviderFactoryTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Bot.Connector.Authentication; -using Xunit; - -namespace Microsoft.Bot.Connector.Tests.Authentication -{ - public class JwtTokenProviderFactoryTests - { - private const string TestAppId = "foo"; - - [Fact] - public void CanCreateAzureServiceTokenProvider() - { - var sut = new JwtTokenProviderFactory(); - var tokenProvider = sut.CreateAzureServiceTokenProvider(TestAppId); - Assert.NotNull(tokenProvider); - } - - [Fact] - public void CanCreateAzureServiceTokenProviderWithCustomHttpClient() - { - using (var customHttpClient = new HttpClient()) - { - var sut = new JwtTokenProviderFactory(); - var tokenProvider = sut.CreateAzureServiceTokenProvider(TestAppId, customHttpClient); - Assert.NotNull(tokenProvider); - } - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void CannotCreateAzureServiceTokenProviderWithoutAppId(string appId) - { - Assert.Throws(() => - { - var sut = new JwtTokenProviderFactory(); - _ = sut.CreateAzureServiceTokenProvider(appId); - }); - } - } -} diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAppCredentialsTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAppCredentialsTests.cs deleted file mode 100644 index f0fa8e1c00..0000000000 --- a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAppCredentialsTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace Microsoft.Bot.Connector.Tests.Authentication -{ - public class ManagedIdentityAppCredentialsTests - { - private const string TestAppId = "foo"; - private const string TestAudience = "bar"; - - [Fact] - public void ConstructorTests() - { - var tokenProviderFactory = new Mock(); - - var sut1 = new ManagedIdentityAppCredentials(TestAppId, TestAudience, tokenProviderFactory.Object); - Assert.Equal(TestAppId, sut1.MicrosoftAppId); - Assert.Equal(TestAudience, sut1.OAuthScope); - - using (var customHttpClient = new HttpClient()) - { - var sut2 = new ManagedIdentityAppCredentials(TestAppId, TestAudience, tokenProviderFactory.Object, customHttpClient); - Assert.Equal(TestAppId, sut2.MicrosoftAppId); - Assert.Equal(TestAudience, sut2.OAuthScope); - - var logger = new Mock().Object; - var sut3 = new ManagedIdentityAppCredentials(TestAppId, TestAudience, tokenProviderFactory.Object, null, logger); - Assert.Equal(TestAppId, sut3.MicrosoftAppId); - Assert.Equal(TestAudience, sut3.OAuthScope); - - var sut4 = new ManagedIdentityAppCredentials(TestAppId, TestAudience, tokenProviderFactory.Object, customHttpClient, logger); - Assert.Equal(TestAppId, sut4.MicrosoftAppId); - Assert.Equal(TestAudience, sut4.OAuthScope); - } - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void CanCreateCredentialsWithoutAudience(string audience) - { - var tokenProviderFactory = new Mock(); - - var sut = new ManagedIdentityAppCredentials(TestAppId, audience, tokenProviderFactory.Object); - Assert.Equal(TestAppId, sut.MicrosoftAppId); - Assert.Equal(AuthenticationConstants.ToChannelFromBotOAuthScope, sut.OAuthScope); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void CannotCreateCredentialsWithoutAppId(string appId) - { - Assert.Throws(() => - { - var tokenProviderFactory = new Mock(); - _ = new ManagedIdentityAppCredentials(appId, TestAudience, tokenProviderFactory.Object); - }); - } - - [Fact] - public void CanCreateCredentialsWithoutTokenProviderFactory() - { - _ = new ManagedIdentityAppCredentials(TestAppId, TestAudience, tokenProviderFactory: null); - } - } -} diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs deleted file mode 100644 index 4ae1dd6238..0000000000 --- a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.Services.AppAuthentication; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Logging; -using Moq; -using Moq.Protected; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace Microsoft.Bot.Connector.Tests.Authentication -{ - public class ManagedIdentityAuthenticatorTests - { - private const string TestAppId = "foo"; - private const string TestAudience = "bar"; - private const string TestConnectionString = "RunAs=App;AppId=foo"; - private const string TestAzureAdInstance = "https://login.microsoftonline.com/"; - - [Fact] - public void ConstructorTests() - { - var callsToCreateTokenProvider = 0; - - var tokenProvider = new Mock(TestConnectionString, TestAzureAdInstance); - - var tokenProviderFactory = new Mock(); - tokenProviderFactory - .Setup(f => f.CreateAzureServiceTokenProvider(It.IsAny(), It.IsAny())) - .Returns((appId, customHttpClient) => - { - callsToCreateTokenProvider++; - Assert.Equal(TestAppId, appId); - - return tokenProvider.Object; - }); - - _ = new ManagedIdentityAuthenticator(TestAppId, TestAudience, tokenProviderFactory.Object); - - using (var customHttpClient = new HttpClient()) - { - _ = new ManagedIdentityAuthenticator(TestAppId, TestAudience, tokenProviderFactory.Object, customHttpClient); - - var logger = new Mock(); - _ = new ManagedIdentityAuthenticator(TestAppId, TestAudience, tokenProviderFactory.Object, null, logger.Object); - - _ = new ManagedIdentityAuthenticator(TestAppId, TestAudience, tokenProviderFactory.Object, customHttpClient, logger.Object); - } - - Assert.Equal(0, callsToCreateTokenProvider); - } - - [Fact] - public void CanGetJwtToken() - { - var response = new HttpResponseMessage(HttpStatusCode.OK); - var expiresOn = DateTimeOffset.Now.ToUnixTimeSeconds() + 10000; - var json = new JObject - { - { "expires_on", expiresOn }, - { "access_token", "at_secret" } - }; - response.Content = new StringContent(json.ToString()); - - var mockHttpMessageHandler = new Mock(); - mockHttpMessageHandler.Protected() - .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(response); - var httpClient = new HttpClient(mockHttpMessageHandler.Object); - - var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); - var token = sut.GetTokenAsync().GetAwaiter().GetResult(); - - Assert.Equal("at_secret", token.AccessToken); - Assert.Equal(expiresOn, token.ExpiresOn.ToUnixTimeSeconds()); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void CanGetJwtTokenWithForceRefresh(bool forceRefreshInput) - { - var response = new HttpResponseMessage(HttpStatusCode.OK); - var expiresOn = DateTimeOffset.Now.ToUnixTimeSeconds() + 10000; - var json = new JObject - { - { "expires_on", expiresOn }, - { "access_token", "at_secret" } - }; - response.Content = new StringContent(json.ToString()); - - var mockHttpMessageHandler = new Mock(); - mockHttpMessageHandler.Protected() - .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(response); - var httpClient = new HttpClient(mockHttpMessageHandler.Object); - - var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); - var token = sut.GetTokenAsync(forceRefreshInput).GetAwaiter().GetResult(); - - Assert.Equal("at_secret", token.AccessToken); - Assert.Equal(expiresOn, token.ExpiresOn.ToUnixTimeSeconds()); - } - - [Fact] - public void DefaultRetryOnException() - { - var maxRetries = 10; - var callsToAcquireToken = 0; - var mockHttpMessageHandler = new Mock(); - mockHttpMessageHandler.Protected() - .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(() => - { - callsToAcquireToken++; - throw new Exception(); - }); - var httpClient = new HttpClient(mockHttpMessageHandler.Object); - - var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); - - try - { - _ = sut.GetTokenAsync().GetAwaiter().GetResult(); - } - catch (AggregateException e) - { - Assert.Equal(maxRetries + 1, e.InnerExceptions.Count); - } - finally - { - Assert.Equal(maxRetries + 1, callsToAcquireToken); - } - } - - [Fact] - public void CanRetryAndAcquireToken() - { - var callsToAcquireToken = 0; - var response = new HttpResponseMessage(HttpStatusCode.OK); - var expiresOn = DateTimeOffset.Now.ToUnixTimeSeconds() + 10000; - var json = new JObject - { - { "expires_on", expiresOn }, - { "access_token", "at_secret" } - }; - response.Content = new StringContent(json.ToString()); - - var mockHttpMessageHandler = new Mock(); - mockHttpMessageHandler.Protected() - .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(() => - { - callsToAcquireToken++; - if (callsToAcquireToken == 1) - { - throw new Exception(); - } - - return response; - }); - var httpClient = new HttpClient(mockHttpMessageHandler.Object); - - var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); - var token = sut.GetTokenAsync().GetAwaiter().GetResult(); - - Assert.Equal("at_secret", token.AccessToken); - Assert.Equal(expiresOn, token.ExpiresOn.ToUnixTimeSeconds()); - Assert.Equal(2, callsToAcquireToken); - } - } -} diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityServiceClientCredentialsFactoryTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityServiceClientCredentialsFactoryTests.cs deleted file mode 100644 index 1dde2e930e..0000000000 --- a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityServiceClientCredentialsFactoryTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using System.Threading; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace Microsoft.Bot.Connector.Tests.Authentication -{ - public class ManagedIdentityServiceClientCredentialsFactoryTests - { - private const string TestAppId = "foo"; - private const string TestAudience = "bar"; - - [Fact] - public void ConstructorTests() - { - var tokenProviderFactory = new Mock(); - - _ = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object); - - using (var customHttpClient = new HttpClient()) - { - _ = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object, customHttpClient); - - var logger = new Mock(); - _ = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object, null, logger.Object); - - _ = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object, customHttpClient, logger.Object); - } - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void CannotCreateCredentialsFactoryWithoutAppId(string appId) - { - Assert.Throws(() => - { - var tokenProviderFactory = new Mock(); - _ = new ManagedIdentityServiceClientCredentialsFactory(appId, tokenProviderFactory.Object); - }); - } - - [Fact] - public void CanCreateCredentialsFactoryWithoutTokenProviderFactory() - { - _ = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory: null); - } - - [Fact] - public void IsValidAppIdTest() - { - var tokenProviderFactory = new Mock(); - var sut = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object); - - Assert.True(sut.IsValidAppIdAsync(TestAppId, CancellationToken.None).GetAwaiter().GetResult()); - Assert.False(sut.IsValidAppIdAsync("InvalidAppId", CancellationToken.None).GetAwaiter().GetResult()); - } - - [Fact] - public void IsAuthenticationDisabledTest() - { - var tokenProviderFactory = new Mock(); - var sut = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object); - - Assert.False(sut.IsAuthenticationDisabledAsync(CancellationToken.None).GetAwaiter().GetResult()); - } - - [Fact] - public void CanCreateCredentials() - { - var tokenProviderFactory = new Mock(); - var sut = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object); - - var credentials = sut.CreateCredentialsAsync( - TestAppId, TestAudience, "https://login.microsoftonline.com", true, CancellationToken.None); - Assert.NotNull(credentials); - } - - [Fact] - public void CannotCreateCredentialsWithInvalidAppId() - { - var tokenProviderFactory = new Mock(); - var sut = new ManagedIdentityServiceClientCredentialsFactory(TestAppId, tokenProviderFactory.Object); - - Assert.Throws(() => - { - _ = sut.CreateCredentialsAsync( - "InvalidAppId", TestAudience, "https://login.microsoftonline.com", true, CancellationToken.None); - }); - } - } -} diff --git a/tests/integration/Microsoft.Bot.Builder.Integration.AspNet.Core.Tests/Mocks/MockAppCredentials.cs b/tests/integration/Microsoft.Bot.Builder.Integration.AspNet.Core.Tests/Mocks/MockAppCredentials.cs index ad3749743e..0933c66094 100644 --- a/tests/integration/Microsoft.Bot.Builder.Integration.AspNet.Core.Tests/Mocks/MockAppCredentials.cs +++ b/tests/integration/Microsoft.Bot.Builder.Integration.AspNet.Core.Tests/Mocks/MockAppCredentials.cs @@ -14,11 +14,10 @@ public MockAppCredentials(string channelAuthTenant = null, HttpClient customHttp : base(channelAuthTenant, customHttpClient, logger) { } - - [Obsolete("This method is deprecated. Use BuildIAuthenticator instead.", false)] - protected override Lazy BuildAuthenticator() + + protected override Lazy BuildIAuthenticator() { - return new Lazy(); + throw new NotImplementedException(); } } } From 14242b0178603a02c448994608e65d1d8111f3a0 Mon Sep 17 00:00:00 2001 From: Hongyang Du Date: Wed, 30 Aug 2023 09:03:02 +0800 Subject: [PATCH 2/5] update --- .../Microsoft.Bot.Connector.csproj | 1 + .../MockAppCredentials.cs.cs | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs.cs diff --git a/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj b/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj index 66e3628af5..5fc5f2f36b 100644 --- a/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj +++ b/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj @@ -26,6 +26,7 @@ + diff --git a/tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs.cs b/tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs.cs new file mode 100644 index 0000000000..82980905b2 --- /dev/null +++ b/tests/Microsoft.Bot.Builder.Tests/MockAppCredentials.cs.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Net.Http; +using Microsoft.Bot.Connector.Authentication; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Bot.Builder.Tests +{ + public class MockAppCredentials : AppCredentials + { + public MockAppCredentials(string channelAuthTenant = null, HttpClient customHttpClient = null, ILogger logger = null) + : base(channelAuthTenant, customHttpClient, logger) + { + } + + protected override Lazy BuildIAuthenticator() + { + throw new NotImplementedException(); + } + } +} From 7893d4109b0ce1616232fda98a13cdd4398af97d Mon Sep 17 00:00:00 2001 From: Hongyang Du Date: Wed, 30 Aug 2023 09:13:33 +0800 Subject: [PATCH 3/5] revert one test --- .../ManagedIdentityAuthenticatorTests.cs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs new file mode 100644 index 0000000000..14314411bf --- /dev/null +++ b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Bot.Connector.Authentication; +using Moq; +using Moq.Protected; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Microsoft.Bot.Connector.Tests.Authentication +{ + public class ManagedIdentityAuthenticatorTests + { + private const string TestAppId = "foo"; + private const string TestAudience = "bar"; + + [Fact] + public void CanGetJwtToken() + { + var response = new HttpResponseMessage(HttpStatusCode.OK); + var expiresOn = DateTimeOffset.Now.ToUnixTimeSeconds() + 10000; + var json = new JObject + { + { "expires_on", expiresOn }, + { "access_token", "at_secret" } + }; + response.Content = new StringContent(json.ToString()); + + var mockHttpMessageHandler = new Mock(); + mockHttpMessageHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(response); + var httpClient = new HttpClient(mockHttpMessageHandler.Object); + + var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); + var token = sut.GetTokenAsync().GetAwaiter().GetResult(); + + Assert.Equal("at_secret", token.AccessToken); + Assert.Equal(expiresOn, token.ExpiresOn.ToUnixTimeSeconds()); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanGetJwtTokenWithForceRefresh(bool forceRefreshInput) + { + var response = new HttpResponseMessage(HttpStatusCode.OK); + var expiresOn = DateTimeOffset.Now.ToUnixTimeSeconds() + 10000; + var json = new JObject + { + { "expires_on", expiresOn }, + { "access_token", "at_secret" } + }; + response.Content = new StringContent(json.ToString()); + + var mockHttpMessageHandler = new Mock(); + mockHttpMessageHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(response); + var httpClient = new HttpClient(mockHttpMessageHandler.Object); + + var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); + var token = sut.GetTokenAsync(forceRefreshInput).GetAwaiter().GetResult(); + + Assert.Equal("at_secret", token.AccessToken); + Assert.Equal(expiresOn, token.ExpiresOn.ToUnixTimeSeconds()); + } + + [Fact] + public void DefaultRetryOnException() + { + var maxRetries = 10; + var callsToAcquireToken = 0; + var mockHttpMessageHandler = new Mock(); + mockHttpMessageHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(() => + { + callsToAcquireToken++; + throw new Exception(); + }); + var httpClient = new HttpClient(mockHttpMessageHandler.Object); + + var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); + + try + { + _ = sut.GetTokenAsync().GetAwaiter().GetResult(); + } + catch (AggregateException e) + { + Assert.Equal(maxRetries + 1, e.InnerExceptions.Count); + } + finally + { + Assert.Equal(maxRetries + 1, callsToAcquireToken); + } + } + + [Fact] + public void CanRetryAndAcquireToken() + { + var callsToAcquireToken = 0; + var response = new HttpResponseMessage(HttpStatusCode.OK); + var expiresOn = DateTimeOffset.Now.ToUnixTimeSeconds() + 10000; + var json = new JObject + { + { "expires_on", expiresOn }, + { "access_token", "at_secret" } + }; + response.Content = new StringContent(json.ToString()); + + var mockHttpMessageHandler = new Mock(); + mockHttpMessageHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(() => + { + callsToAcquireToken++; + if (callsToAcquireToken == 1) + { + throw new Exception(); + } + + return response; + }); + var httpClient = new HttpClient(mockHttpMessageHandler.Object); + + var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, httpClient); + var token = sut.GetTokenAsync().GetAwaiter().GetResult(); + + Assert.Equal("at_secret", token.AccessToken); + Assert.Equal(expiresOn, token.ExpiresOn.ToUnixTimeSeconds()); + Assert.Equal(2, callsToAcquireToken); + } + } +} From 2eb1d241f45cf209150f95d742b8b1176a811c3d Mon Sep 17 00:00:00 2001 From: Hongyang Du Date: Wed, 30 Aug 2023 09:14:50 +0800 Subject: [PATCH 4/5] revert one test --- .../Authentication/ManagedIdentityAuthenticatorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs index 14314411bf..7ccab5f4e6 100644 --- a/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs +++ b/tests/Microsoft.Bot.Connector.Tests/Authentication/ManagedIdentityAuthenticatorTests.cs @@ -79,7 +79,7 @@ public void DefaultRetryOnException() var mockHttpMessageHandler = new Mock(); mockHttpMessageHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(() => + .ReturnsAsync(() => { callsToAcquireToken++; throw new Exception(); @@ -118,7 +118,7 @@ public void CanRetryAndAcquireToken() var mockHttpMessageHandler = new Mock(); mockHttpMessageHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(() => + .ReturnsAsync(() => { callsToAcquireToken++; if (callsToAcquireToken == 1) From bf11267718d41e181d533149497c35da6797d75e Mon Sep 17 00:00:00 2001 From: Hongyang Du Date: Tue, 5 Sep 2023 10:50:59 +0800 Subject: [PATCH 5/5] add compatbaseline --- ApiCompatBaseline.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ApiCompatBaseline.txt b/ApiCompatBaseline.txt index 0895775d4c..dd119bc045 100644 --- a/ApiCompatBaseline.txt +++ b/ApiCompatBaseline.txt @@ -1,4 +1,11 @@ # These types are no longer supported in Microsoft.Bot.Builder.Azure: TypesMustExist : Type 'Microsoft.Bot.Builder.Azure.CosmosDbCustomClientOptions' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Microsoft.Bot.Builder.Azure.CosmosDbStorage' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Microsoft.Bot.Builder.Azure.CosmosDbStorageOptions' does not exist in the implementation but it does exist in the contract. \ No newline at end of file +TypesMustExist : Type 'Microsoft.Bot.Builder.Azure.CosmosDbStorageOptions' does not exist in the implementation but it does exist in the contract. + +TypesMustExist : Type 'Microsoft.Bot.Connector.Authentication.AdalAuthenticator' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'Microsoft.Bot.Connector.Authentication.AppCredentials.BuildAuthenticator()' does not exist in the implementation but it does exist in the contract. +CannotAddAbstractMembers : Member 'Microsoft.Bot.Connector.Authentication.AppCredentials.BuildIAuthenticator()' is abstract in the implementation but is missing in the contract. +MembersMustExist : Member 'Microsoft.Bot.Connector.Authentication.CertificateAppCredentials..ctor(Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate, System.String, System.Net.Http.HttpClient, Microsoft.Extensions.Logging.ILogger)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'Microsoft.Bot.Connector.Authentication.CertificateAppCredentials.BuildAuthenticator()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'Microsoft.Bot.Connector.Authentication.MicrosoftAppCredentials.BuildAuthenticator()' does not exist in the implementation but it does exist in the contract. \ No newline at end of file