diff --git a/src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs b/src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs
index d299cc64db1..8f448f4a89a 100644
--- a/src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs
+++ b/src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs
@@ -7,7 +7,6 @@
#pragma warning disable ASPIREAZURE001
using Aspire.Hosting.ApplicationModel;
-using Aspire.Hosting.Azure.Provisioning.Internal;
using Aspire.Hosting.Pipelines;
using Microsoft.Extensions.DependencyInjection;
diff --git a/src/Aspire.Hosting.Azure/ITokenCredentialProvider.cs b/src/Aspire.Hosting.Azure/ITokenCredentialProvider.cs
new file mode 100644
index 00000000000..589bcbad747
--- /dev/null
+++ b/src/Aspire.Hosting.Azure/ITokenCredentialProvider.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Azure.Core;
+
+namespace Aspire.Hosting.Azure;
+
+///
+/// Provides access to the that Aspire uses
+/// to authenticate against Azure when provisioning resources and calling Azure APIs.
+///
+///
+///
+/// This service is registered as a singleton when Azure provisioning is enabled
+/// (e.g., by AddAzureProvisioning).
+///
+///
+/// Integrations and app host code can resolve this service from
+/// to obtain a instance configured by Aspire's
+/// Azure provisioning options (for example, the configured tenant id and credential source).
+/// The concrete credential type returned by is an implementation
+/// detail and may change between releases; callers should treat the value as an opaque
+/// .
+///
+///
+public interface ITokenCredentialProvider
+{
+ ///
+ /// Gets the to use for Azure authentication.
+ ///
+ TokenCredential TokenCredential { get; }
+}
diff --git a/src/Aspire.Hosting.Azure/Provisioning/Internal/IProvisioningServices.cs b/src/Aspire.Hosting.Azure/Provisioning/Internal/IProvisioningServices.cs
index d04839558ee..36e1527f1cf 100644
--- a/src/Aspire.Hosting.Azure/Provisioning/Internal/IProvisioningServices.cs
+++ b/src/Aspire.Hosting.Azure/Provisioning/Internal/IProvisioningServices.cs
@@ -244,14 +244,3 @@ internal interface IUserPrincipalProvider
///
Task GetUserPrincipalAsync(CancellationToken cancellationToken = default);
}
-
-///
-/// Provides access to Azure token credentials.
-///
-internal interface ITokenCredentialProvider
-{
- ///
- /// Gets the token credential for Azure authentication.
- ///
- TokenCredential TokenCredential { get; }
-}
diff --git a/src/Aspire.Hosting.Foundry/PromptAgent/AzurePromptAgentResource.cs b/src/Aspire.Hosting.Foundry/PromptAgent/AzurePromptAgentResource.cs
index 0d78fae9685..9ce681ee63a 100644
--- a/src/Aspire.Hosting.Foundry/PromptAgent/AzurePromptAgentResource.cs
+++ b/src/Aspire.Hosting.Foundry/PromptAgent/AzurePromptAgentResource.cs
@@ -6,7 +6,6 @@
using System.Globalization;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
-using Aspire.Hosting.Azure.Provisioning.Internal;
using Aspire.Hosting.Pipelines;
using Aspire.Hosting.Publishing;
using Azure.AI.Projects;
diff --git a/tests/Aspire.Hosting.Azure.Tests/TokenCredentialProviderTests.cs b/tests/Aspire.Hosting.Azure.Tests/TokenCredentialProviderTests.cs
new file mode 100644
index 00000000000..f0de536fe46
--- /dev/null
+++ b/tests/Aspire.Hosting.Azure.Tests/TokenCredentialProviderTests.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Aspire.Hosting.Utils;
+using Azure.Core;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Aspire.Hosting.Azure.Tests;
+
+public class TokenCredentialProviderTests
+{
+ [Fact]
+ public void AddAzureProvisioning_RegistersITokenCredentialProvider()
+ {
+ using var builder = TestDistributedApplicationBuilder.Create();
+ builder.AddAzureProvisioning();
+
+ using var app = builder.Build();
+
+ var provider = app.Services.GetRequiredService();
+
+ Assert.NotNull(provider);
+ Assert.NotNull(provider.TokenCredential);
+ }
+
+ [Fact]
+ public void AddAzureProvisioning_RegistersITokenCredentialProviderAsSingleton()
+ {
+ using var builder = TestDistributedApplicationBuilder.Create();
+ builder.AddAzureProvisioning();
+
+ using var app = builder.Build();
+
+ var first = app.Services.GetRequiredService();
+ var second = app.Services.GetRequiredService();
+
+ Assert.Same(first, second);
+ Assert.Same(first.TokenCredential, second.TokenCredential);
+ }
+
+ [Fact]
+ public void AddingAzureResource_RegistersITokenCredentialProvider()
+ {
+ // AddAzureProvisioning is invoked indirectly when an Azure resource is added.
+ using var builder = TestDistributedApplicationBuilder.Create();
+ builder.AddAzureInfrastructure("infra", _ => { });
+
+ using var app = builder.Build();
+
+ var provider = app.Services.GetRequiredService();
+
+ Assert.NotNull(provider.TokenCredential);
+ }
+
+ [Fact]
+ public void ITokenCredentialProvider_CanBeReplacedWithCustomImplementation()
+ {
+ // External callers should be able to plug in their own credential by
+ // replacing the registered service with their own implementation.
+ var customCredential = new TestTokenCredential();
+ var customProvider = new CustomTokenCredentialProvider(customCredential);
+
+ using var builder = TestDistributedApplicationBuilder.Create();
+ builder.AddAzureProvisioning();
+ builder.Services.AddSingleton(customProvider);
+
+ using var app = builder.Build();
+
+ var resolved = app.Services.GetRequiredService();
+
+ Assert.Same(customProvider, resolved);
+ Assert.Same(customCredential, resolved.TokenCredential);
+ }
+
+ private sealed class CustomTokenCredentialProvider(TokenCredential credential) : ITokenCredentialProvider
+ {
+ public TokenCredential TokenCredential { get; } = credential;
+ }
+}