Put Blob from URL AuthenticationFailed. Using Azure Storage SDK and all required headers.

Collin Brittain 36 Reputation points
2021-02-21T16:13:32.213+00:00

Hello,

I am testing out using the Put Blob from URL operation to copy a photo from my Google Photos account to Azure Blobs. I am getting stuck at authorization of the request. I downloaded the storage-dotnet-rest-api-with-auth repo to give me an example of how to interact with the API, and the provided List operation works fine. However, when I make my own request using the Put Blob from URL operation, I cannot authenticate. I believe I am providing all of the required headers, and it should be in correct canonicalized form because I am using the provided AzureStorageAuthenticationHelper.GetAuthorizationHeader() method.

The request URL:

   string containerName = "<containername>";  
   string blobName = "photo1.jpg";  
   String uri = string.Format("https://{0}.blob.core.windows.net/{1}/{2}", storageAccountName, containerName, blobName);  

My header code:

   DateTime now = DateTime.UtcNow;  
   httpRequestMessage.Headers.Add("x-ms-date", now.ToString("R", CultureInfo.InvariantCulture));  
   httpRequestMessage.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");  
   httpRequestMessage.Headers.Add("x-ms-version", "2020-04-08");  
   httpRequestMessage.Headers.Add("x-ms-copy-source", "<url>");  
   httpRequestMessage.Headers.Add("x-ms-blob-type", "BlockBlob");  
   // If you need any additional headers, add them here before creating  
   //   the authorization header.   
     
   // Add the authorization header.  
   httpRequestMessage.Headers.Authorization = AzureStorageAuthenticationHelper.GetAuthorizationHeader(  
       storageAccountName, storageAccountKey, now, httpRequestMessage);  

The resulting request:

   x-ms-date: Sun, 21 Feb 2021 16:00:49 GMT  
   x-ms-version: 2020-04-08  
   x-ms-copy-source: <url>  
   x-ms-blob-type: BlockBlob  
   Authorization: SharedKey <accountname>:<key>  
     
   Content-Type: image/jpeg  
   Content-Length: 0  
     
   https://myaccount.blob.core.windows.net/mycontainer/photo1.jpg  

The response:

   <?xml version="1.0" encoding="utf-8"?>  
   <Error>  
       <Code>AuthenticationFailed</Code>  
       <Message>  
            Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.  
   RequestId:aea0e156-a01e-0037-3a6a-082086000000  
   Time:2021-02-21T16:00:48.6817608Z  
       </Message>  
       <AuthenticationErrorDetail>  
           The MAC signature found in the HTTP request '<signature>' is not the same as any computed signature. Server used following string to sign:   
   'PUT  
     
     
     
     
   image/jpeg  
     
     
     
     
     
     
   x-ms-blob-type:BlockBlob  
   x-ms-copy-source:<url>  
   x-ms-date:Sun, 21 Feb 2021 16:00:49 GMT  
   x-ms-version:2020-04-08  
   /myaccount/mycontainer/photo1.jpg'.  
       </AuthenticationErrorDetail>  
   </Error>  

I have done a lot of searching and can't find a resolution to this problem. Most related issues are people strugging to get the canonicalized string correct but they aren't using the microsoft-provided method like I am which takes care of that.

What am I missing?

EDIT: If this is not the right place to post this question please redirect me.

Azure Blob Storage
Azure Blob Storage
An Azure service that stores unstructured data in the cloud as blobs.
2,415 questions
{count} votes

Accepted answer
  1. deherman-MSFT 33,141 Reputation points Microsoft Employee
    2021-02-22T22:13:23.14+00:00

    @Collin Brittain
    I confirmed with the product team that AzureStorageAuthenticationHelper is out of date and for a different/older .NET SDK.

    We highly recommend you use our official Azure Storage .NET SDK instead - Azure.Storage.Blobs. There is also Azure.Storage.DataMovement which already has the copy from a file from url ability you are trying to accomplish.

    Here is our sample for how to download a blob using Azure.Storage.Blobs

    Hope this helps. Let us know if you have further questions or issues and we will be happy to assist. Also Stack Overflow is another great resource that is actively monitored and responded to.

    -------------------------------

    Please don’t forget to "Accept the answer" and “up-vote” wherever the information provided helps you, this can be beneficial to other community members.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. deherman-MSFT 33,141 Reputation points Microsoft Employee
    2021-02-24T19:39:00.05+00:00

    @Collin Brittain
    I was able to gather some sample code that you might find helpful. Here’s a sample using the v12 Storage SDK library to copy more than one file to Azure blobs using the SyncCopyFromUrl API:

        // A list of tasks that are currently executing  
        List<Task<Response<BlobCopyInfo>>> runningTasks = new List<Task<Response<BlobCopyInfo>>>();  
        int maxWorkers = 4;  
    
        // List all the blobs  
        string sourceDirectoryName = "a/";  
        int prefixSize = sourceDirectoryName.Length-1;  
        string destDirectoryName = "c/";  
        //List<string> names = new List<string>();  
        await foreach (BlobItem blob in container.GetBlobsAsync(prefix: sourceDirectoryName))  
        {  
            BlobClient destBlob = container.GetBlobClient(destDirectoryName + blob.Name.Substring(prefixSize));  
            Task<Response<BlobCopyInfo>> task = destBlob.SyncCopyFromUriAsync(container.GetBlobClient(blob.Name).Uri);  
            runningTasks.Add(task);  
    
            // Check if any of our tasks are still busy  
            if (runningTasks.Count >= maxWorkers)  
            {  
                await Task.WhenAny(runningTasks).ConfigureAwait(false);  
    
                // Clear any completed blocks from the task list  
                for (int i = 0; i < runningTasks.Count; i++)  
                {  
                    Task runningTask = runningTasks[i];  
                    if (!runningTask.IsCompleted)  
                    {  
                        continue;  
                    }  
    
                    await runningTask.ConfigureAwait(false);  
                    runningTasks.RemoveAt(i);  
                    i--;  
                }  
            }  
        }  
        // Wait for all our tasks  
        await Task.WhenAll(runningTasks).ConfigureAwait(false);  
    
        foreach (Task<Response<BlobCopyInfo>> task in runningTasks)  
        {  
            Assert.AreEqual(task.Result.Value.CopyStatus, CopyStatus.Success);  
        }  
    

    Also here is a snippet of authenticating with the SDK:

        string accountName = StorageAccountName;  
        string accountKey = StorageAccountKey;  
        BlobUriBuilder uriBuilder = new BlobUriBuilder()  
        {  
            AccountName = StorageAccountName,  
            BlobContainerName = containerName,  
            BlobName = blobName  
        };  
        Uri serviceUri = uriBuilder.ToUri();  
    
        // Create a SharedKeyCredential that we can use to authenticate  
        StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);  
    
        // Create a client that can authenticate with a connection string  
        BlobServiceClient service = new BlobServiceClient(serviceUri, credential);