diff --git a/libraries/Microsoft.Bot.Connector/Authentication/FederatedAppCredentials.cs b/libraries/Microsoft.Bot.Connector/Authentication/FederatedAppCredentials.cs
new file mode 100644
index 0000000000..628ca236b1
--- /dev/null
+++ b/libraries/Microsoft.Bot.Connector/Authentication/FederatedAppCredentials.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Net.Http;
+using System.Threading;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.Bot.Connector.Authentication
+{
+ ///
+ /// Federated Credentials auth implementation.
+ ///
+ public class FederatedAppCredentials : AppCredentials
+ {
+ private readonly string _clientId;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// App ID for the Application.
+ /// Client ID for the managed identity assigned to the bot.
+ /// Optional. The token tenant.
+ /// Optional. The scope for the token.
+ /// Optional to be used when acquiring tokens.
+ /// Optional to gather telemetry data while acquiring and managing credentials.
+ public FederatedAppCredentials(string appId, string clientId, string channelAuthTenant = null, string oAuthScope = null, HttpClient customHttpClient = null, ILogger logger = null)
+ : base(channelAuthTenant, customHttpClient, logger, oAuthScope)
+ {
+ if (string.IsNullOrWhiteSpace(appId))
+ {
+ throw new ArgumentNullException(nameof(appId));
+ }
+
+ MicrosoftAppId = appId;
+ _clientId = clientId;
+ }
+
+ ///
+ protected override Lazy BuildIAuthenticator()
+ {
+ return new Lazy(
+ () => new FederatedAuthenticator(MicrosoftAppId, _clientId, OAuthEndpoint, OAuthScope, CustomHttpClient, Logger),
+ LazyThreadSafetyMode.ExecutionAndPublication);
+ }
+ }
+}
diff --git a/libraries/Microsoft.Bot.Connector/Authentication/FederatedAuthenticator.cs b/libraries/Microsoft.Bot.Connector/Authentication/FederatedAuthenticator.cs
new file mode 100644
index 0000000000..a3b87888cb
--- /dev/null
+++ b/libraries/Microsoft.Bot.Connector/Authentication/FederatedAuthenticator.cs
@@ -0,0 +1,134 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Web;
+
+namespace Microsoft.Bot.Connector.Authentication
+{
+ ///
+ /// Abstraction to acquire tokens from a Federated Credentials Application.
+ ///
+ internal class FederatedAuthenticator : IAuthenticator
+ {
+ private readonly string _authority;
+ private readonly string _scope;
+ private readonly string _clientId;
+ private readonly ILogger _logger;
+ private readonly IConfidentialClientApplication _clientApplication;
+ private readonly ManagedIdentityClientAssertion _managedIdentityClientAssertion;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// App id for the Application.
+ /// Client id for the managed identity to be used for acquiring tokens.
+ /// Resource for which to acquire the token.
+ /// Login endpoint for request.
+ /// A customized instance of the HttpClient class.
+ /// The type used to perform logging.
+ public FederatedAuthenticator(string appId, string clientId, string authority, string scope, HttpClient customHttpClient = null, ILogger logger = null)
+ {
+ if (string.IsNullOrWhiteSpace(appId))
+ {
+ throw new ArgumentNullException(nameof(appId));
+ }
+
+ if (string.IsNullOrWhiteSpace(scope))
+ {
+ throw new ArgumentNullException(nameof(scope));
+ }
+
+ _authority = authority;
+ _scope = scope;
+ _clientId = clientId;
+ _logger = logger ?? NullLogger.Instance;
+ _clientApplication = CreateClientApplication(appId, customHttpClient);
+ _managedIdentityClientAssertion = new ManagedIdentityClientAssertion(_clientId);
+ }
+
+ ///
+ public async Task GetTokenAsync(bool forceRefresh = false)
+ {
+ var watch = Stopwatch.StartNew();
+
+ var result = await Retry
+ .Run(() => AcquireTokenAsync(forceRefresh), HandleTokenProviderException)
+ .ConfigureAwait(false);
+
+ watch.Stop();
+ _logger.LogInformation($"GetTokenAsync: Acquired token using MSI in {watch.ElapsedMilliseconds}.");
+
+ return result;
+ }
+
+ private async Task AcquireTokenAsync(bool forceRefresh)
+ {
+ const string scopePostFix = "/.default";
+ var scope = _scope;
+
+ if (!scope.EndsWith(scopePostFix, StringComparison.OrdinalIgnoreCase))
+ {
+ scope = $"{scope}{scopePostFix}";
+ }
+
+ _logger.LogDebug($"AcquireTokenAsync: authority={_authority}, scope={scope}");
+
+ var authResult = await _clientApplication
+ .AcquireTokenForClient(new[] { scope })
+ .WithAuthority(_authority, true)
+ .WithForceRefresh(forceRefresh)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+ return new AuthenticatorResult
+ {
+ AccessToken = authResult.AccessToken,
+ ExpiresOn = authResult.ExpiresOn
+ };
+ }
+
+ private RetryParams HandleTokenProviderException(Exception e, int retryCount)
+ {
+ _logger.LogError(e, "Exception when trying to acquire token using Federated Credentials!");
+
+ if (e is MsalServiceException exception)
+ {
+ // stop retrying for all except for throttling response
+ if (exception.StatusCode != 429)
+ {
+ return RetryParams.StopRetrying;
+ }
+ }
+
+ return RetryParams.DefaultBackOff(retryCount);
+ }
+
+ private IConfidentialClientApplication CreateClientApplication(string appId, HttpClient customHttpClient = null)
+ {
+ _logger.LogDebug($"CreateClientApplication for appId={appId}");
+
+ var clientBuilder = ConfidentialClientApplicationBuilder
+ .Create(appId)
+ .WithClientAssertion((AssertionRequestOptions options) => FetchExternalTokenAsync())
+ .WithCacheOptions(CacheOptions.EnableSharedCacheOptions); // for more cache options see https://learn.microsoft.com/entra/msal/dotnet/how-to/token-cache-serialization?tabs=msal
+
+ if (customHttpClient != null)
+ {
+ clientBuilder.WithHttpClientFactory(new ConstantHttpClientFactory(customHttpClient));
+ }
+
+ return clientBuilder.Build();
+ }
+
+ private async Task FetchExternalTokenAsync()
+ {
+ return await _managedIdentityClientAssertion.GetSignedAssertionAsync(default).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/libraries/Microsoft.Bot.Connector/Authentication/FederatedServiceClientCredentialsFactory.cs b/libraries/Microsoft.Bot.Connector/Authentication/FederatedServiceClientCredentialsFactory.cs
new file mode 100644
index 0000000000..66514607c4
--- /dev/null
+++ b/libraries/Microsoft.Bot.Connector/Authentication/FederatedServiceClientCredentialsFactory.cs
@@ -0,0 +1,89 @@
+// 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.Extensions.Logging;
+using Microsoft.Rest;
+
+namespace Microsoft.Bot.Connector.Authentication
+{
+ ///
+ /// A Federated Credentials implementation of the interface.
+ ///
+ public class FederatedServiceClientCredentialsFactory : ServiceClientCredentialsFactory
+ {
+ private readonly string _appId;
+ private readonly string _clientId;
+ private readonly string _tenantId;
+ private readonly HttpClient _httpClient;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Microsoft application Id.
+ /// Managed Identity Client Id.
+ /// The app tenant.
+ /// A custom httpClient to use.
+ /// A logger instance to use.
+ /// This enables authentication App Registration + Federated Credentials.
+ public FederatedServiceClientCredentialsFactory(
+ string appId,
+ string clientId,
+ string tenantId = null,
+ HttpClient httpClient = null,
+ ILogger logger = null)
+ : base()
+ {
+ if (string.IsNullOrWhiteSpace(appId))
+ {
+ throw new ArgumentNullException(nameof(appId));
+ }
+
+ if (string.IsNullOrWhiteSpace(clientId))
+ {
+ throw new ArgumentNullException(nameof(clientId));
+ }
+
+ _appId = appId;
+ _clientId = clientId;
+ _tenantId = tenantId;
+ _httpClient = httpClient;
+ _logger = logger;
+ }
+
+ ///
+ public override Task IsValidAppIdAsync(string appId, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(appId == _appId);
+ }
+
+ ///
+ public override Task IsAuthenticationDisabledAsync(CancellationToken cancellationToken)
+ {
+ // Auth is always enabled for Certificate.
+ return Task.FromResult(false);
+ }
+
+ ///
+ public override Task CreateCredentialsAsync(
+ string appId, string audience, string loginEndpoint, bool validateAuthority, CancellationToken cancellationToken)
+ {
+ if (appId != _appId)
+ {
+ throw new InvalidOperationException("Invalid App ID.");
+ }
+
+ return Task.FromResult(new FederatedAppCredentials(
+ _appId,
+ _clientId,
+ channelAuthTenant: _tenantId,
+ oAuthScope: audience,
+ customHttpClient: _httpClient,
+ logger: _logger));
+ }
+ }
+}
diff --git a/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj b/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj
index 6c1583444a..67627e6767 100644
--- a/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj
+++ b/libraries/Microsoft.Bot.Connector/Microsoft.Bot.Connector.csproj
@@ -29,8 +29,9 @@
-
+
+