azure.net-coreazure-blob-storageshared-access-signatures

How to use SAS Token to connect to Azure BlobStorage from C#?


tl;dr: I'm trying to connect to an Azure BlobStorage from C# using a SAS, but cannot seem to get past a StorageException saying the "Server failed to authenticate the request." I suspect something is wrong with my connection string.


I am using libraries Microsoft.Azure.Storage.Blob 11.2.3.0 and Microsoft.Azure.Storage.Common 11.2.3.0 to connect to an Azure BlobStorage from a .NET Core 3.1 application.

My C# code to establish this connection starts as follows:

var storageAccount = CloudStorageAccount.Parse(connectionString);
var blobClient = storageAccount.CreateCloudBlobClient();

When using a connection string that includes the fields AccountName, AccountKey, EndpointSuffix, and DefaultEndpointsProtocol (this is https)1, this works flawlessly. (I can connect and do things like enumerate containers and blobs, and also create containers and blobs and upload data.)


Now, I'm supposed to connect to that same BlobStorage with a SAS. For that purpose, I have been given the following bits of information (here anonymized to some kind of "placeholders" because I am going to refer to them in the following):

I can successfully establish a connection to said BlobStorage container from Microsoft Azure Storage Explorer by using the <Blob-SAS-URL> as described above.2

However, I have so far been unable to successfully use the SAS from my C# code.

I have tried various ways to write my connection string, such as:

According to the docs, this should work. In there, a SAS-based connection string is outlined to contain up to five fields - BlobEndpoint, QueueEndpoint, TableEndpoint, FileEndpoint, and SharedAccessSignature - where only (any) one of the four endpoints must be specified beside the SAS.

I have also tried creating/populating the CloudStorageAccount instance differently than by means of a (possibly misformatted) connection string, such as:

var storageAccount = new CloudStorageAccount(
    new StorageCredentials("<Blob-SAS-Token>"),
    "<StorageAccount>", "<EndpointSuffix>", true);

Unfortunately, no matter what I try, as soon as I attempt to retrieve any data from the BlobStorage this way, a Microsoft.Azure.Storage.StorageException is thrown, stating

Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.

UTC time on my local machine exactly matches current UTC time I can find on online sources. As I can connect with my SAS information from another application on the very same machine, the issue is most probably not related to any aspect of the environment (such as a missing entry for my IP address in some blocking list or similar), anyway. Rather than that, I suspect I am not providing an adequate set of information out of what I have been given for establishing the connection.

What do I have to change to make this connection work?


1: I am going to write this post with some sort of "placeholders" for the various identifiers and credentials so as to not divulge any confidential information. I hope this is not too confusing. On the other hand, it should actually allow for easily adapting the information to whichever sample endpoint is available by a couple of find-and-replace operations.

2: To do so, I right-click the Storage Accounts tree node, then Connect to Azure Storage... > Blob Container > SAS URL (Shared Access Signature


Solution

  • Connection string-based solution below.

    Based on Swetha's comment, I was able to connect using the following code:

    var creds = new StorageCredentials("<Blob-SAS-Token>");
    var blobContainer = new CloudBlobContainer(new Uri("<DefaultEndpointsProtocol>://<StorageAccount>.blob.<EndpointSuffix>/<Container>"), creds);
    

    I can then retrieve and upload data from/to blobContainer.

    Unfortunately, this means the code for connecting to the BlobStorage looks differently than with a connection string. Therefore, I will need to provide, test, and maintain two different code paths, depending on which kind of connection configuration has been supplied (and there is no guarantee a third authentication method will not require yet another code path - something I have now asked about in a separate question).


    UPDATE: Based upon an answer to my question about storing connection data, I have now learned that it is actually possible to connect with a SAS using a connection string. The following formats have proven valid:

    Then, an important point is that (at least if the SAS is container-specific like the one I got - I'm not sure this is always the case) I can work in the container, but not on the container (or anything outside of it). That is:

    Once sticking to these restrictions, it is possible to use CloudStorageAccount.Parse(connectionString) again.


    1: I have created a separate question about how to safely check for a container's existence.