Using Cloud-Agnostic Temporary Access to Objects: Abstracting AWS Pre-Signed URLs and Azure SAS in C# Projects

Ercan Erdoğan
4 min readOct 8, 2024

--

Using Cloud-Agnostic Temporary Access to Objects

This post continues the journey we started in our previous article. If you haven’t read the first story yet, you can check it out here : Seamless Cloud Storage Integration: Abstracting AWS S3 and Azure Blob Storage in C#

In many scenarios, especially in web or mobile applications, you might need to provide temporary access to objects in your cloud storage. Thankfully, both AWS and Azure have solutions for this. AWS provides pre-signed URLs for S3, while Azure offers Shared Access Signatures (SAS) for Blob Storage.

Let’s walk through how to generate these temporary URLs and use them to securely upload and download files.

Extending the Interface for Temporary URLs

Since this post is a continuation of the previous one, we are now extending our interface to include a temporary access feature for both providers. By adding and enhancing this functionality, we adhere to SOLID’s Open/Closed Principle, allowing our system to be easily extended without modifying existing code.

This will allow us to create pre-signed URLs for AWS and SAS tokens for Azure.

public interface IBlobStorageService
{
Task UploadObjectAsync(string containerName, string blobName, Stream data);
Task<Stream> GetObjectAsync(string containerName, string blobName);
Task DeleteObjectAsync(string containerName, string blobName);
Task<string> GetTemporaryUrlAsync(string containerName, string blobName, TimeSpan expiry);
}

Generating Pre-Signed URLs for AWS S3

AWS pre-signed URLs allow you to give temporary access to S3 objects without sharing your credentials. Here’s how to implement it:

using Amazon.S3;
using Amazon.S3.Model;
using System;

public class AwsS3StorageService : IBlobStorageService
{
private readonly IAmazonS3 _s3Client;

public AwsS3StorageService(string awsAccessKey, string awsSecretKey, RegionEndpoint region)
{
_s3Client = new AmazonS3Client(awsAccessKey, awsSecretKey, region);
}

public async Task<string> GetTemporaryUrlAsync(string bucketName, string objectKey, TimeSpan expiry)
{
var request = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = objectKey,
Expires = DateTime.UtcNow.Add(expiry)
};
return _s3Client.GetPreSignedURL(request);
}
}

Generating SAS Tokens for Azure Blob Storage

Similarly, Azure’s SAS tokens provide time-limited access to Blob Storage. Here’s how you can generate a SAS token:

using Azure.Storage;
using Azure.Storage.Blobs;
using Azure.Storage.Sas;
using System;

public class AzureBlobStorageService : IBlobStorageService
{
private readonly BlobServiceClient _blobServiceClient;

public AzureBlobStorageService(string connectionString)
{
_blobServiceClient = new BlobServiceClient(connectionString);
}

public async Task<string> GetTemporaryUrlAsync(string containerName, string blobName, TimeSpan expiry)
{
var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
var blobClient = containerClient.GetBlobClient(blobName);

var sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerName,
BlobName = blobName,
Resource = "b",
ExpiresOn = DateTimeOffset.UtcNow.Add(expiry)
};

sasBuilder.SetPermissions(BlobContainerSasPermissions.Read | BlobContainerSasPermissions.Write);

var sasUri = blobClient.GenerateSasUri(sasBuilder);
return sasUri.ToString();
}
}

Uploading and Downloading with Temporary URLs

Once you have your pre-signed URL (AWS) or SAS token (Azure), you can use them to upload and download files securely. Below are examples for both.

Let’s write an example for both.

Upload Examples

AWS S3 Upload using Pre-Signed URL:

public async Task UploadFileToS3Async(string preSignedUrl, string filePath)
{
using (var client = new HttpClient())
{
var fileContent = new ByteArrayContent(File.ReadAllBytes(filePath));
var response = await client.PutAsync(preSignedUrl, fileContent);
response.EnsureSuccessStatusCode();
}
}

Azure Blob Upload using SAS:

public async Task UploadFileToAzureAsync(string sasUrl, string filePath)
{
using (var client = new HttpClient())
{
var fileContent = new ByteArrayContent(File.ReadAllBytes(filePath));
var response = await client.PutAsync(sasUrl, fileContent);
response.EnsureSuccessStatusCode();
}
}

Download Examples

AWS S3 Download using Pre-Signed URL:

public async Task DownloadFileFromS3Async(string preSignedUrl, string downloadPath)
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(preSignedUrl);
response.EnsureSuccessStatusCode();

var fileStream = await response.Content.ReadAsStreamAsync();
using (var file = new FileStream(downloadPath, FileMode.Create, FileAccess.Write))
{
await fileStream.CopyToAsync(file);
}
}
}

Azure Blob Download using SAS:

public async Task DownloadFileFromAzureAsync(string sasUrl, string downloadPath)
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(sasUrl);
response.EnsureSuccessStatusCode();

var fileStream = await response.Content.ReadAsStreamAsync();
using (var file = new FileStream(downloadPath, FileMode.Create, FileAccess.Write))
{
await fileStream.CopyToAsync(file);
}
}
}

Advantages and Trade-offs of Using Temporary Access URLs

Temporary access URLs (pre-signed URLs and SAS tokens) provide a secure, time-bound way to grant external access to files in your cloud storage. However, it’s important to consider the benefits and potential limitations when using them.

Advantages

  1. Security: Pre-signed URLs and SAS tokens grant limited, temporary access without exposing your actual credentials. This adds an extra layer of security, as users only have access to specific files for a limited time.
  2. Granularity of Control: You can fine-tune the permissions (read, write, delete) for each URL or token, ensuring users can only perform the actions you want them to.
  3. Simplified Client-Side Access: Clients can upload or download files directly from cloud storage without needing your backend to handle the file transfer. This reduces server load and enhances performance.
  4. No Need for Permanent Credentials: Since the access is temporary, you don’t need to store long-term credentials on the client, reducing the risk of data breaches.

Trade-offs

  1. Time-limited Access: Once the URL or token expires, the client will no longer have access to the file. This means you need to handle the expiration period carefully to avoid disrupting users.
  2. URL Exposure Risk: If a pre-signed URL or SAS token is shared or intercepted, someone with the URL can access the file until it expires. Make sure to use HTTPS to protect the URL in transit and set appropriate permissions.
  3. Complexity: While these mechanisms simplify access for clients, they add some complexity in terms of managing the generation, expiration, and renewal of these URLs or tokens. Implementing logic to refresh expired URLs might be necessary.
  4. Limited Control after Generation: Once you generate a pre-signed URL or SAS token, you cannot revoke or alter it. If you realize that you’ve granted incorrect permissions, you can’t take them back until the URL or token expires.

Conclusion

Pre-signed URLs and SAS tokens are powerful tools that simplify the file access process while maintaining security. However, like any security mechanism, they come with trade-offs that you must manage carefully. In scenarios where you need to allow temporary access to cloud storage, these features offer a secure, flexible solution, especially when you couple them with an abstracted service that works across multiple providers.

Beyond the cloud…

--

--