-
Notifications
You must be signed in to change notification settings - Fork 7
Open
Description
When using tus while developing on a localhost connecting to remote Azure resources, I experience the error shown in the screenshot. It seems like Azure might be rate limiting our DefaultAzureCredential requests, breaking the tus upload process with the exception Azure.Identity.AuthenticationFailedException: ManagedIdentityCredential authentication failed: Invalid argument.
From what I see, this can be resolved by giving the developer the option to select a lazy identity option and reuse the DefaultAzureCredential for the use case where we only care about refreshing the credential on server start.
// AzureBlobTusStoreAuthenticationMode.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Xtensible.TusDotNet.Azure
{
/// <summary>
/// Modes for how the library authenticates with the Azure storage account.
/// ConnectionString: Use a connection string with either a Shared Access Signature (SAS) or access key. See https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string.
/// SystemAssignedManagedIdentity: Authenticate using Azure system-assigned managed identity. See https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview.
/// SystemAssignedManagedIdentityLazy: Uses the <see cref="System.Lazy{DefaultAzureCredential}"/> to create a single instance of the credential. This is useful when the credential is expensive to create.
/// Thread safe using the <see cref="System.Threading.LazyThreadSafetyMode.ExecutionAndPublication"/> mode.
/// </summary>
public enum AzureBlobTusStoreAuthenticationMode
{
ConnectionString = 0,
SystemAssignedManagedIdentity = 1,
SystemAssignedManagedIdentityLazy = 2,
}
}
// AzureBlobClientFactory.cs:
using Azure.Core;
using Azure.Identity;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
namespace Xtensible.TusDotNet.Azure
{
internal static class AzureBlobClientFactory
{
private static readonly Lazy<DefaultAzureCredential> AzureCredential = new(() => new DefaultAzureCredential(), LazyThreadSafetyMode.ExecutionAndPublication);
public static BlobServiceClient CreateBlobServiceClient(AzureBlobTusStoreAuthenticationMode authenticationMode, string connectionString)
{
if (new [] {AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentity, AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentityLazy}.Contains(authenticationMode))
{
var connectionStringIsUri = Uri.TryCreate(connectionString, UriKind.Absolute, out var blobStorageUri);
if (!connectionStringIsUri)
{
throw new ArgumentException("connectionString must be a Azure Blob Storage URI when authentication mode is SystemAssignedManagedIdentity");
}
return new BlobServiceClient(blobStorageUri, authenticationMode == AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentityLazy
? AzureCredential.Value
: new DefaultAzureCredential());
}
else
{
return new BlobServiceClient(connectionString);
}
}
public static AppendBlobClient CreateAppendBlobClient(AzureBlobTusStoreAuthenticationMode authenticationMode, string connectionString, string containerName, string blobPath)
{
if (new [] {AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentity, AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentityLazy}.Contains(authenticationMode))
{
var connectionStringIsUri = Uri.TryCreate(connectionString, UriKind.Absolute, out var blobStorageUri);
if (!connectionStringIsUri)
{
throw new ArgumentException("connectionString must be a Azure Blob Storage URI when authentication mode is SystemAssignedManagedIdentity");
}
return new AppendBlobClient(new Uri(blobStorageUri, $"{containerName}/{blobPath}"), authenticationMode == AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentityLazy
? AzureCredential.Value
: new DefaultAzureCredential());
}
else
{
return new AppendBlobClient(connectionString, containerName, blobPath);
}
}
public static BlobContainerClient CreateBlobContainerClient(AzureBlobTusStoreAuthenticationMode authenticationMode, string connectionString, string containerName)
{
if (new [] {AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentity, AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentityLazy}.Contains(authenticationMode))
{
var connectionStringIsUri = Uri.TryCreate(connectionString, UriKind.Absolute, out var blobStorageUri);
if (!connectionStringIsUri)
{
throw new ArgumentException("connectionString must be a Azure Blob Storage URI when authentication mode is SystemAssignedManagedIdentity");
}
return new BlobContainerClient(new Uri(blobStorageUri, containerName), authenticationMode == AzureBlobTusStoreAuthenticationMode.SystemAssignedManagedIdentityLazy
? AzureCredential.Value
: new DefaultAzureCredential());
}
else
{
return new BlobContainerClient(connectionString, containerName);
}
}
}
}
Metadata
Metadata
Assignees
Labels
No labels
