How to create Azure Storage SAS token using DefaultAzureCredential class - azure

I want to create SAS token to download a blob stored in container in azure storage. I can easily generate SAS token using shared credential but this requires storage access key. How can I generate sas token using managed Identity.
credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
sasQueryParams, err := azblob.BlobSASSignatureValues{
Protocol: azblob.SASProtocolHTTPS,
ExpiryTime: time.Now().UTC().Add(4 * time.Hour),
ContainerName: containerName,
BlobName: blobName,
Permissions: azblob.BlobSASPermissions{Add: false,
Read: true, Write: false}.String(),
}.NewSASQueryParameters(credential)

How can I generate sas token using managed Identity?
You can generate it by using DefaultAzureCredential and the proper access to that blob in the storage container.
Connect to the storage account by using the Azure AD credentials of Default Azure Credential class.
Sample Code:
var strgAccName = _configuration.GetValue<string>("YourStorageAccountName");
var saUri = $"https://{strgAccName}.blob.core.windows.net";
var blobServiceClient = new BlobServiceClient(new Uri(saUri), new DefaultAzureCredential());
var blobContainerClient = blobServiceClient.GetBlobContainerClient(_configuration.GetValue<string>("YourContainerName"));
var blobClient = blobContainerClient.GetBlobClient("YourImage.jpg");
// We can issue the SAS token till a maximum of 7 days.
var userDelegationKey = blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow,
DateTimeOffset.UtcNow.AddHours(4));
var sasBuilder = new BlobSasBuilder()
{
BlobContainerName = blobClient.BlobContainerName,
BlobName = blobClient.Name,
Resource = "b", // b: blob, c: container
StartsOn = DateTimeOffset.UtcNow,
ExpiresOn = DateTimeOffset.UtcNow.AddHours(4),
};
sasBuilder.SetPermissions(BlobSasPermissions.Read);
var blobUriBuilder = new BlobUriBuilder(blobClient.Uri)
{
Sas = sasBuilder.ToSasQueryParameters(userDelegationKey,blobServiceClient.AccountName)
};
// Read this in any view like `blobUriBuilder.ToUri().ToString();`
}
And re-check the delegated access is there or not for that blob.
So, we don't use any access key and connection string for this.
Thanks to #Anupam Maiti for this Article, please refer this for step-by-step procedure.

Related

Azure blob read SAS token throws AuthorizationPermissionMismatch exception

I'm trying to generate a SAS token for a blob, so that any user with the token can read the blob. Below is the code I have. I get an exception when I try to read the blob. If I grant "Storage Blob Data Reader" access to the user, then it works. My understanding is that user with SAS token should be able to read the blob without granting specific permission. what am I missing here ?
BlobServiceClient blobServiceClient = new BlobServiceClient(new Uri("https://accountname.blob.core.windows.net/"), new DefaultAzureCredential());
UserDelegationKey key = await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow,
DateTimeOffset.UtcNow.AddDays(1));
BlobSasBuilder sasBuilder = new BlobSasBuilder()
{
BlobContainerName = "containerName",
BlobName = "file.json",
Resource = "b",
StartsOn = DateTimeOffset.UtcNow,
ExpiresOn = DateTimeOffset.UtcNow.AddHours(1)
};
sasBuilder.SetPermissions(BlobSasPermissions.Read);
string sasToken = sasBuilder.ToSasQueryParameters(key, "accountname").ToString();
UriBuilder fullUri = new UriBuilder()
{
Scheme = "https",
Host = string.Format("{0}.blob.core.windows.net", "accountname"),
Path = string.Format("{0}/{1}", "containerName", "file.json"),
Query = sasToken
};
var blobClient = new Azure.Storage.Blobs.BlobClient(fullUri.Uri);
using (var stream = await blobClient.OpenReadAsync()) // throws exception
{ }
Exception : Service request failed.
Status: 403 (This request is not authorized to perform this operation using this permission.)
ErrorCode: AuthorizationPermissionMismatch
I believe you are getting this error is because the user for which you are getting the user delegation key does not have permissions to access the data in the storage account.
Assigning Owner permission enables the user to manage the storage account itself, it does not give them permissions to manage the data.
Please try by assigning the user one of the data roles described here: https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory#azure-built-in-roles-for-blobs.
To learn more about RBAC roles to manage data, please see this link: https://learn.microsoft.com/en-us/azure/storage/blobs/assign-azure-role-data-access?tabs=portal.

How to generate SAS token in azure JS SDK, from app client, without using account key

How to pre-sign Url in azure using javascript SDK with App registrations - Application (client) ID, client secret (value), Tenant_id and also account name, container name, blob name.
I am not able to generate Container Level SAS token for giving temporary access to my files.
const account = "accountName";
const containerName = "containerName";
const blobName = "blob";
const credential = new ClientSecretCredential(
"AZURE_TENANT_ID",
"AZURE_CLIENT_ID",
"AZURE_CLIENT_SECRET"
);
const blobServiceClient = new BlobServiceClient(
`https://${account}.blob.core.windows.net`,
credential
);
const containerClient = blobServiceClient.getContainerClient(containerName);
let blobs = containerClient.listBlobsFlat({includeMetadata: true}); // works ok
await credential.getToken(); // how to generate sas token for my container to sign url ?
I do not want to use the account key, and it seems generateBlobSASQueryParameters function works with account key.
I have succeeded with the following steps.
Have to add Storage Blob Delegator and Storage Blob Data Reader roles to my application client from storage container IAM, and add the following code to upper one.
const userDelegationKey = await blobServiceClient.getUserDelegationKey(new Date(), new Date(new Date().valueOf() + 86400));
const containerSAS = generateBlobSASQueryParameters({
containerName,
permissions: ContainerSASPermissions.parse("r"),
startsOn: new Date(),
expiresOn: new Date(new Date().valueOf() + 86400),
version: "2018-11-09"
},
userDelegationKey,
account
).toString();
console.log(`${containerClient.getBlockBlobClient(blobName).url}?${containerSAS}`);
A user delegation SAS offers superior security to a SAS that is signed with the storage account key. Microsoft recommends using a user delegation SAS when possible
https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-user-delegation-sas-create-dotnet
The link has a .NET sample, but you could translate to javascript.
There are also some test cases here demonstrating the usage: get a user delegation key from BlobServiceClient, then pass the user delegation key to generateBlobSASQueryParameters()
https://github.com/Azure/azure-sdk-for-js/blob/d2730549e078571df008e929f19c07aaf8f9efd9/sdk/storage/storage-blob/test/node/sas.spec.ts#L992-L1004

Scope for Accessing Storage Account using Managed Identity

I'm using managed identity to access azure database in this manner.The Azure App Registration is used for getting the token and the token is passed to the connection.In the same manner,how do i connect to a storage account and write to a container? What will be the scope in this case?
AuthenticationResult authenticationResult = null;
var _app = ConfidentialClientApplicationBuilder.Create(Environment.GetEnvironmentVariable("ClientId"))
.WithAuthority(string.Format(Environment.GetEnvironmentVariable("AADInstance"), Environment.GetEnvironmentVariable("Tenant")))
.WithClientSecret(Environment.GetEnvironmentVariable("ClientSecret")).Build();
authenticationResult = _app.AcquireTokenForClient(new string[] { "https://database.windows.net/.default" }).ExecuteAsync().Result;
using (SqlConnection conn = new SqlConnection(Environment.GetEnvironmentVariable("DBConnection")))
{
conn.AccessToken = authenticationResult.AccessToken;
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT * FROM mytable", conn))
{
var result = cmd.ExecuteScalar();
Console.WriteLine(result);
}
}
Azure Storage uses this scope:
https://storage.azure.com/.default
That said, with the new Azure Storage SDK and Azure.Identity, you don't actually need to know this.
You can use them like this:
var credential = new ClientSecretCredential(tenantId: "", clientId: "", clientSecret: "");
var blobUrl = "https://accountname.blob.core.windows.net";
var service = new BlobServiceClient(new Uri(blobUrl), credential);
var container = service.GetBlobContainerClient("container");
var blob = container.GetBlobClient("file.txt");
// TODO: Write the file
For Azure Storage, the scope will be https://storage.azure.com/.default.
Please see this link for more details: https://learn.microsoft.com/en-us/azure/storage/common/storage-auth-aad-app?tabs=dotnet#azure-storage-resource-id.

Getting Error while Creating SAS Token for Azure Storage Blob with MSI

I'm trying to create a SAS token for a storage blob. I use a StorageCredentials which was created with MSI (Managed Service Identity) to create the CloudBlobClient. When creating the SAS I'm getting "Cannot create Shared Access Signature unless Account Key credentials are used". Is there support to SAS with MSI?
var container = blobClient.GetContainerReference(containerName);
var blockBlob = container.GetBlockBlobReference(snapname);
var sas = string.Concat(blockBlob.Uri.ToString(), blockBlob.GetSharedAccessSignature(sasConstraints));
This is how I create the StorageCredentials:
tokenCallback = CreateMsiCallback();
var initToken = await tokenCallback(audience);
return new StorageCredentials(
new TokenCredential(initToken, async (state, token) =>
{
var accessToken = await _tokenCallback(audience);
return new NewTokenAndFrequency(accessToken, TimeSpan.FromMinutes(1));
}, null, TimeSpan.FromMinutes(1))
);
To create the token callback I use HttpClient
public Func<string, Task<string>> CreateMsiCallback()
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, certChain, policyErrors) =>
{
if (policyErrors == SslPolicyErrors.None)
{
return true;
}
return 0 == string.Compare(cert.GetCertHashString(), FabricThumbprint, StringComparison.OrdinalIgnoreCase);
}
};
var client = new HttpClient(handler)
{
DefaultRequestHeaders =
{
{"secret", FabricAuthenticationCode }
}
};
return async (resource) =>
{
var requestUri = $"{FabricMsiEndpoint}?api-version={FabricApiVersion}&resource={HttpUtility.UrlEncode(resource)}";
var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);
var response = await client.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
var tokenResponseString = await response.Content.ReadAsStringAsync();
var tokenResponseObject =
JsonConvert.DeserializeObject<ManagedIdentityTokenResponse>(tokenResponseString);
return tokenResponseObject.AccessToken;
};
}
}
Based on this Github issue, you will need to assign Storage data roles to your MSI in order to generate SAS token. From this thread:
The error is because your oauth account don't have permission to
generateUserDelegationKey. To get SAS with Oauth storage context
(New-AzStorageContext -UseConnectedAuth), we need first generate
UserDelegationKey from server , then use the key to generate the SAS
token.
Please check have you assigned correct roles to the Oauth login user
(with Connect-AzAccount). like at least one of the following 4 roles
on the specific storage account:
Storage Blob Data Owner
Storage Blob Data Contributor
Storage Blob Data Reader
Storage Blob Delegator

can i specify a queue name when generate shared access signatures (SAS) for my azure storage account

here is the docs that describe how to Constructing a Service SAS.
the document says, you can specify a table name , so that the sas can only access that specific table.
Can i do the same thing with queue, so the sas can only access that specific queue?
Can i do the same thing with queue, so the sas can only access that
specific queue?
Sure you can! Take a look at the code below:
static void GenerateSasForQueue()
{
var cred = new StorageCredentials(accountName, accountKey);
var account = new CloudStorageAccount(cred, true);
var client = account.CreateCloudQueueClient();
var queue = client.GetQueueReference("queue-name");
var sasPolicy = new SharedAccessQueuePolicy()
{
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-15),
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(2),
Permissions = SharedAccessQueuePermissions.Add | SharedAccessQueuePermissions.Read |
SharedAccessQueuePermissions.Update | SharedAccessQueuePermissions.ProcessMessages
};
var sasToken = queue.GetSharedAccessSignature(sasPolicy);
var sasUrl = string.Format("{0}{1}", queue.Uri.AbsoluteUri, sasToken);
}
This code will generate a SAS Token on the queue named queue-name in your storage account with all permissions valid for 2 hours from the date of SAS creation.

Resources