diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index ccd69b9600..60b48241fc 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -7,7 +7,7 @@ - + @@ -34,6 +34,7 @@ + @@ -46,7 +47,7 @@ - + diff --git a/src/Service/Azure.DataApiBuilder.Service.csproj b/src/Service/Azure.DataApiBuilder.Service.csproj index 5cf762ca57..d0478b83ed 100644 --- a/src/Service/Azure.DataApiBuilder.Service.csproj +++ b/src/Service/Azure.DataApiBuilder.Service.csproj @@ -64,6 +64,7 @@ + diff --git a/src/Service/Startup.cs b/src/Service/Startup.cs index 333bf57234..39fc0e29b0 100644 --- a/src/Service/Startup.cs +++ b/src/Service/Startup.cs @@ -3,6 +3,8 @@ using System; using System.IO.Abstractions; +using System.Linq; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -435,7 +437,7 @@ public void ConfigureServices(IServiceCollection services) else { // NOTE: this is done to reuse the same connection multiplexer for both the cache and backplane - Task connectionMultiplexerTask = ConnectionMultiplexer.ConnectAsync(level2CacheOptions.ConnectionString); + Task connectionMultiplexerTask = CreateConnectionMultiplexerAsync(level2CacheOptions.ConnectionString); fusionCacheBuilder .WithSerializer(new FusionCacheSystemTextJsonSerializer()) @@ -470,6 +472,33 @@ public void ConfigureServices(IServiceCollection services) services.AddControllers(); } + /// + /// Creates a ConnectionMultiplexer for Redis with support for Azure Entra authentication. + /// + /// The Redis connection string. + /// A task that represents the asynchronous operation. The task result contains the connected IConnectionMultiplexer. + private static async Task CreateConnectionMultiplexerAsync(string connectionString) + { + ConfigurationOptions options = ConfigurationOptions.Parse(connectionString); + + // Determine if an endpoint is localhost/loopback + static bool IsLocalhostEndpoint(EndPoint ep) => ep switch + { + DnsEndPoint dns => string.Equals(dns.Host, "localhost", StringComparison.OrdinalIgnoreCase), + IPEndPoint ip => IPAddress.IsLoopback(ip.Address), + _ => false, + }; + + // If no password is provided, and the endpoint (or at least one of them) is non-localhost, + // attempt to use Entra authentication. + if (string.IsNullOrEmpty(options.Password) && !options.EndPoints.Any(IsLocalhostEndpoint)) + { + options = await options.ConfigureForAzureWithTokenCredentialAsync(new DefaultAzureCredential()); + } + + return await ConnectionMultiplexer.ConnectAsync(options); + } + /// /// Configure GraphQL services within the service collection of the /// request pipeline.