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…
2
Answers
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.
To generate a SAS token with URI for a container using a user delegation key, you can follow the code below.
Code:
Output:
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.
Update:
To create a user delegation SAS, you need to have the
Storage Blob Data Contributor
role to your storage account.