Search code examples
c#azure-blob-storage

C# BlobClient StartCopyFromUriAsync using bearer token authentication


I have a C# application which recieves an Azure BLOB container URI and bearer token, and needs to execute a copy of contents from one file to another file, both within the container. I understand that I cannot generate a user delegation SAS since the bearer token is only guaranteed to have container level access (Storage Blob Data Contributor).

When creating a BlobContainerClient with a TokenCredential containing the bearer token and trying to execute the copy:

                var sourceClient = m_blobContainerClient.GetBlobClient(source);
                var targetClient = m_blobContainerClient.GetBlobClient(target);
                return targetClient.StartCopyFromUriAsync(sourceClient.Uri);

I am getting a 404 (the source definitely exists - I am verifying this). Is what I'm trying to do even possible? Is there any solution for copy files without having a SAS URI for the source file?


Solution

  • I do agree with @Gaurav Mantri that it does not depend whether the destination blob is created or not, but it depends the clients.

    Bearer Token Method:

    using Azure.Core;
    using Azure.Storage.Blobs;
    using Microsoft.Identity.Client;
    
    
    class Program
    {
        static async Task Main(string[] args)
        {
            string ri_tnt_id = "932rithwikaf6d";
            string ri_clnt_id = "828arithwik3efa57";
            string clnt_sct = "DyHrithwikiggcjD";
    
            string rith_StrgName = "storagename";
            string con_nme = "rithcon";
            string ri_src_blb = "hello.txt";
            string ri_target_blb = "rithtest.txt";
    
            string blobServiceUri = $"https://{rith_StrgName}.blob.core.windows.net";
            string ri_tkn = await RithGetTkn(ri_tnt_id, ri_clnt_id, clnt_sct);
            var cred = new BearerTokenCredential(ri_tkn);
            var ri_bsc = new BlobServiceClient(new Uri(blobServiceUri), cred);
            var ri_bcc = ri_bsc.GetBlobContainerClient(con_nme);
            var sbc = ri_bcc.GetBlobClient(ri_src_blb);
            var tbc = ri_bcc.GetBlobClient(ri_target_blb);
            await RithCpyBlB(sbc, tbc);
    
            Console.WriteLine("Hello Rithwik, Blob is Copied!");
        }
        static async Task RithCpyBlB(BlobClient sbc, BlobClient tbc)
        {
            using var ri_data = new MemoryStream();
            await sbc.DownloadToAsync(ri_data);
            ri_data.Position = 0;
            await tbc.UploadAsync(ri_data, overwrite: true);
        }
    
    
    static async Task<string> RithGetTkn(string ri_tnt_id, string ri_clnt_id, string clnt_sct)
        {
            var app = ConfidentialClientApplicationBuilder.Create(ri_clnt_id)
                .WithClientSecret(clnt_sct)
                .WithAuthority($"https://login.microsoftonline.com/{ri_tnt_id}")
                .Build();
    
            string[] scopes = new[] { "https://storage.azure.com/.default" };
            var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
            return result.AccessToken;
        }
    }
    
    public class BearerTokenCredential : TokenCredential
    {
        private readonly string ritoken;
    
        public BearerTokenCredential(string accessToken)
        {
            ritoken = accessToken;
        }
    
        public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
        {
            return new AccessToken(ritoken, DateTimeOffset.MaxValue);
        }
    
        public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
        {
            return new ValueTask<AccessToken>(new AccessToken(ritoken, DateTimeOffset.MaxValue));
        }
    }
    

    Output:

    enter image description here

    enter image description here

    Default credential method as an alternative to your code which works for me:

    using Azure.Identity;
    using Azure.Storage.Blobs;
    
    
    class Program
    {
        static async Task Main(string[] args)
        {
            string ri_tnt_id = "932rithwikaf6d";
            string ri_clnt_id = "828arithwik3efa57";
            string clnt_sct = "DyHrithwikiggcjD";
    
    
            string rith_StrgName = "stoargename";
            string con_nme = "rithcon";
            string ri_src_blb = "hello.txt";
            string ri_target_blb = "test.txt";
    
            string blobServiceUri = $"https://{rith_StrgName}.blob.core.windows.net";
            var cred = new ClientSecretCredential(ri_tnt_id, ri_clnt_id, clnt_sct);
            var ri_bsc = new BlobServiceClient(new Uri(blobServiceUri), cred);
            var ri_bcc = ri_bsc.GetBlobContainerClient(con_nme);
            var sbc = ri_bcc.GetBlobClient(ri_src_blb);
            var tbc = ri_bcc.GetBlobClient(ri_target_blb);
            await RithCpyBlB(sbc, tbc);
    
            Console.WriteLine("Hello Rithwik, Blob is Copied!");
        }
    
        static async Task RithCpyBlB(BlobClient sbc, BlobClient tbc)
        {
            using var ri_data = new MemoryStream();
            await sbc.DownloadToAsync(ri_data);
            ri_data.Position = 0; 
            await tbc.UploadAsync(ri_data, overwrite: true);
        }
    }
    

    Output:

    enter image description here

    enter image description here

    Given Storage Blob Data Contributor Role.