using OrasProject.Oras.Registry.Remote.Auth;
using Azure.Identity;
using Azure.Core;
using Azure.Containers.ContainerRegistry;
namespace OrasProject.Oras.Examples.Azure;
// This is an example implementation of AzureCredentialProvider, which
// can be used to authenticate with Azure Container Registry. It uses
// DefaultAzureCredential with the fully qualified type name. It implements
// the ICredentialProvider interface.
public class AzureCredentialProvider(string host) : ICredentialProvider
{
public string Host { get; } = host;
private string _aadToken { get; set; } = string.Empty;
private Credential _credential { get; set; } = new Credential();
private DateTimeOffset _tokenExpiry { get; set; } = DateTimeOffset.MinValue;
private readonly TokenCredential _aadCredential = new DefaultAzureCredential();
private ContainerRegistryClient _acrClient { get; set; } = new ContainerRegistryClient(new Uri($"https://{host}"));
private async Task<string> GetAadTokenAsync(CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(_aadToken) || DateTimeOffset.UtcNow >= _tokenExpiry)
{
string[] scopes = ["https://containerregistry.azure.net"];
var token = await _aadCredential.GetTokenAsync(new TokenRequestContext(scopes), cancellationToken).ConfigureAwait(false);
_aadToken = token.Token;
_tokenExpiry = token.ExpiresOn;
}
return _aadToken;
}
// Implement the ICredentialProvider interface.
public async Task<Credential> ResolveCredentialAsync(string hostname, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(hostname))
{
throw new ArgumentException("Hostname cannot be null or empty.", nameof(hostname));
}
if (hostname != Host)
{
throw new ArgumentException($"Hostname '{hostname}' does not match the expected host '{Host}'.", nameof(hostname));
}
if (!_credential.IsEmpty() && DateTimeOffset.UtcNow < _tokenExpiry)
{
return _credential;
}
var aadToken = await GetAadTokenAsync(cancellationToken).ConfigureAwait(false);
var response = await _acrClient.ExchangeAadAccessTokenForAcrRefreshTokenAsync(hostname, null, null, aadToken, cancellationToken);
_credential = new Credential(RefreshToken: response.Value.RefreshToken);
return _credential;
}
}
using OrasProject.Oras.Registry.Remote.Auth;
using OrasProject.Oras.Registry.Remote;
using OrasProject.Oras.Registry;
namespace OrasProject.Oras.Examples.Azure;
public static class AzureContainerRegistryAuthentication
{
// This example demonstrates how to use the ICredentialProvider interface to
// authenticate with Azure Container Registry, and perform a copy operation
// between two ACR repositories.
// For production use: Implement proper exception handling, cancellation, and dependency injection.
public static async Task AuthenticateWithAzureContainerRegistryAsync(CancellationToken cancellationToken = default)
{
using var httpClient = new HttpClient();
// Create a repository instance for the source repository.
var srcCredentialProvider = new AzureCredentialProvider("mysourceregistry.azurecr.io");
var srcRepository = new Repository(new RepositoryOptions
{
Reference = Reference.Parse("mysourceregistry.azurecr.io/myimage"),
Client = new Client(httpClient, credentialProvider: srcCredentialProvider),
});
// Create a repository instance for the destination repository.
var dstCredentialProvider = new AzureCredentialProvider("mydestinationregistry.azurecr.io");
var dstRepository = new Repository(new RepositoryOptions
{
Reference = Reference.Parse("mydestinationregistry.azurecr.io/myimage"),
Client = new Client(httpClient, credentialProvider: dstCredentialProvider),
});
// Copy the artifact tagged by v1.1.0 from the source repository to the destination
// repository, and tag it with v1.
var srcReference = "v1.1.0";
var dstReference = "v1";
await srcRepository.CopyAsync(srcReference, dstRepository, dstReference, cancellationToken);
}
}