skip to Main Content

I am trying to create a method which can return an Azure SAS URI for a specific container. The code runs fine and does produce what seems like a valid URI, but when you try to use it you get the following error:

<?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:bdfae000-901e-00e2-093a-87e256000000
        Time:2024-04-05T09:19:45.7818447Z
    </Message>
    <AuthenticationErrorDetail>
        Signature did not match. String to sign used was rl
        2024-04-06T09:19:24Z
        /blob/tmfootballdev/$root
        b066bc3e-48c7-42d6-a041-9b900744166f
        eb272a1e-e767-4041-9363-f8736579f898
        2024-04-05T09:04:15Z
        2024-04-06T09:19:15Z
        b
        2023-11-03
        https
        2023-11-03
        c
    </AuthenticationErrorDetail>
</Error>

(I removed some empty lines and changed the indentation to make it more readable).

While it is quite clear that the signature isn’t matching what it expects, it’s less clear why that is the case. I create the string as a User Delegation SAS, using the following code:

public async Task<Uri> GetContainerSas(BlobContainerClient containerClient)
{
    // Construct the blob endpoint from the account name.
    string endpoint = $"https://{_accountName}.blob.core.windows.net";

    // Create a blob service client object using DefaultAzureCredential
    BlobServiceClient blobServiceClient = new(
        new Uri(endpoint),
        new DefaultAzureCredential());

    // Get a user delegation key for the Blob service that's valid for 1 day
    UserDelegationKey userDelegationKey =
        await blobServiceClient.GetUserDelegationKeyAsync(
            DateTimeOffset.UtcNow.AddMinutes(-15),
            DateTimeOffset.UtcNow.AddDays(1));

    // Create a SAS token for the blob resource that's also valid for 1 day
    BlobSasBuilder sasBuilder = new()
    {
        BlobContainerName = containerClient.Name,
        Resource = "c",
        ExpiresOn = DateTimeOffset.UtcNow.AddDays(1),
        Protocol = SasProtocol.Https
    };

    // Specify the necessary permissions
    sasBuilder.SetPermissions(
        BlobSasPermissions.List | BlobSasPermissions.Read);


    // Add the SAS token to the blob URI
    BlobUriBuilder uriBuilder = new(containerClient.Uri)
    {
        // Specify the user delegation key
        Sas = sasBuilder.ToSasQueryParameters(
            userDelegationKey,
            containerClient.AccountName)
    };

    return uriBuilder.ToUri();
}

(The code is based on this article, but modified to work on the entire container, not just a single blob).

An example output of a SAS URI, which doesn’t work is this (signature censored):

https://<account>.blob.core.windows.net/test
?skoid=b066bc3e-48c7-42d6-a041-9b900744166f
&sktid=eb272a1e-e767-4041-9363-f8736579f898
&skt=2024-04-05T09%3A04%3A15Z
&ske=2024-04-06T09%3A19%3A15Z
&sks=b
&skv=2023-11-03
&sv=2023-11-03
&spr=https
&se=2024-04-06T09%3A19%3A24Z
&sr=c
&sp=rl
&sig=xxxxxx

Which is quite similar to this token, generated from the Azure Portal, which does work:

https://<account>.blob.core.windows.net/test
?sp=rl
&st=2024-04-05T08:52:05Z&se=2024-04-05T16:52:05Z
&spr=https
&sv=2022-11-02
&sr=c
&sig=xxxxxx

What am I missing?

UPDATE

It may be related to insufficient access rights (see screenshot) to generate User Delegation keys. The one I tested with was generated as an Account key. I will verify when I fix the RBAC. I still don’t get why I should be able to actually retrieve a URI if I don’t have sufficient access. I would expect an exception. If someone knows why, feel free to share…

Screenshot from Azure Portal

2

Answers


  1. Chosen as BEST ANSWER

    In order to obtain a UserDelegationKey, the account you connect with must be registered as a Storage Blob Delegator under Access Control (IAM) in Azure. This requires Role Based Access Control (RBAC) to be enabled.

    If you don't have this, rather than getting an exception, you get an invalid SAS, which can lead you to think that there is something wrong with the way you obtain - or use - the SAS. Even if your code is correct.

    Settings from Azure Access Control (IAM)


  2. Trying to create a method which can return an Azure SAS URI for a specific container.

    To generate a SAS token with URI for a container using a user delegation key, you can follow the code below.

    Code:

    using Azure.Identity;
    using Azure.Storage.Blobs;
    using Azure.Storage.Sas;
    using System;
    
    
    class program
    {
        public static void Main(string[] args)
        {
            var Container = "test";
            var storage = "venkat123";
            var url = @"https://" + storage + ".blob.core.windows.net";
            var blobServiceClient = new BlobServiceClient(
                new Uri(url),
                new DefaultAzureCredential());
    
            BlobContainerClient ContainerClient = blobServiceClient.GetBlobContainerClient(Container);
     
            var userDelegationKey = blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(1));
            var sasBuilder = new BlobSasBuilder()
            {
                BlobContainerName = Container,
                Resource = "c",
                StartsOn = DateTimeOffset.UtcNow,
                ExpiresOn = DateTimeOffset.UtcNow.AddDays(1),
            };
            sasBuilder.SetPermissions(BlobSasPermissions.Read | BlobSasPermissions.Write);
            var blobUriBuilder = new BlobUriBuilder(ContainerClient.Uri)
            {
                Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, blobServiceClient.AccountName)
            };
            var sourceUri = blobUriBuilder.ToUri();
            Console.WriteLine(sourceUri);
        }
    }
    

    Output:

    https://venkat123.blob.core.windows.net/test?skoid=6e19aaxxxx9-4a85-85f4-b00099a8da5c&sktid=72f988bfxxxx1af-91ab-2xxxb47&skt=2024-04-05T10%3A05%3A57Z&ske=2024-04-06T10%3A05%3A57Z&sks=b&skv=2023-11-03&sv=2023-11-03&st=2024-04-05T10%3A06%3A12Z&se=2024-04-06T10%3A06%3A12Z&sr=c&sp=rw&sig=xxxx
    

    Postman:
    To test, I’m using the above container URL with a stored file (test.jpg) to access for validation.

    It executed successfully in my environment.

    enter image description here

    Update:

    To create a user delegation SAS, you need to have the Storage Blob Data Contributor role to your storage account.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search