Azure AD credentials for User Delegation SAS: A complete implementation guide
User Delegation SAS tokens represent the most secure method for granting temporary access to Azure Storage, using Azure AD credentials instead of storage account keys. This guide clarifies exactly what credentials are involved and how to implement them properly in web applications.
Understanding the credential confusion
The key insight is that User Delegation SAS requires two separate identity concepts: the creating identity (your web app's Azure AD credentials) and the using identity (whoever uses the SAS token). Many developers confuse these roles, particularly when they can login to Azure Storage Explorer interactively but struggle with programmatic access.
When you login to Azure Storage Explorer, you're using personal Azure login credentials (interactive authentication). However, programmatic SAS creation in web applications requires different credential types designed for automated scenarios.
What Azure AD credentials are actually used for programmatic SAS creation
The authentication flow
User Delegation SAS creation follows a specific four-phase process:
- Azure AD Authentication: Your web application authenticates with Azure AD using one of several credential types
- User Delegation Key Acquisition: The authenticated app requests a special cryptographic key from Azure Storage
- SAS Token Generation: Your app signs the SAS parameters using this key
- SAS Token Usage: External users/applications can access storage using the SAS token (no Azure AD required)
Supported credential types for programmatic access
The Azure SDK uses the TokenCredential
interface with these implementations:
Managed Identity (Recommended for production)
ManagedIdentityCredential
: Uses Azure's built-in identity service- No secrets to manage, automatic credential rotation
- Only works on Azure resources (App Service, Functions, VMs, etc.)
Service Principal (For external or cross-platform scenarios)
ClientSecretCredential
: Uses application ID and secretCertificateCredential
: Uses application ID and certificate- Requires manual credential management and rotation
Development/Testing
DefaultAzureCredential
: Automatically discovers available authentication methodsAzureCliCredential
: Uses Azure CLI authentication contextInteractiveBrowserCredential
: For interactive scenarios
Critical difference: Creator vs User identities
Creating Identity (Your Web App): Using Identity (SAS Token Holder):
├─ Must be Azure AD authenticated ├─ No Azure AD authentication needed
├─ Needs RBAC permissions ├─ No RBAC permissions required
├─ Identity embedded in SAS token ├─ Identity not tracked by Azure
├─ Audit trail of SAS creation ├─ Only usage logged anonymously
└─ Limited to 7-day SAS validity └─ Access controlled by SAS permissions
Setting up proper Azure AD identity for your web app
Step 1: Choose the right credential type
For Azure-hosted web apps (App Service, Functions, Container Apps):
# Enable system-assigned managed identity
az webapp identity assign --name mywebapp --resource-group myResourceGroup
# Get the principal ID for role assignment
PRINCIPAL_ID=$(az webapp identity show --name mywebapp --resource-group myResourceGroup --query principalId --output tsv)
For external or multi-platform apps:
# Create service principal
az ad sp create-for-rbac --name "mywebapp-sp" --role "Storage Blob Delegator" \
--scopes "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Storage/storageAccounts/{storage-account}"
Step 2: Assign required permissions
The creating identity needs this specific action:
Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey
Built-in roles that include this permission:
- Storage Blob Delegator (minimum required)
- Storage Blob Data Contributor (if you also need data access)
- Storage Blob Data Owner (for full access)
# Assign the minimum required role
az role assignment create \
--role "Storage Blob Delegator" \
--assignee $PRINCIPAL_ID \
--scope "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Storage/storageAccounts/{storage-account}"
Implementation examples
C#/.NET with Managed Identity
using Azure.Identity;
using Azure.Storage.Blobs;
using Azure.Storage.Sas;
public class UserDelegationSasService
{
private readonly BlobServiceClient _blobServiceClient;
private readonly string _storageAccountName;
public UserDelegationSasService(string storageAccountName)
{
_storageAccountName = storageAccountName;
var endpoint = $"https://{storageAccountName}.blob.core.windows.net";
// DefaultAzureCredential automatically discovers managed identity
_blobServiceClient = new BlobServiceClient(new Uri(endpoint), new DefaultAzureCredential());
}
public async Task<string> CreateUserDelegationSasAsync(
string containerName,
string blobName,
TimeSpan validity)
{
// Step 1: Get user delegation key using Azure AD credentials
var userDelegationKey = await _blobServiceClient.GetUserDelegationKeyAsync(
DateTimeOffset.UtcNow,
DateTimeOffset.UtcNow.Add(validity));
// Step 2: Create SAS builder with desired permissions
var sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerName,
BlobName = blobName,
Resource = "b", // "b" for blob, "c" for container
StartsOn = DateTimeOffset.UtcNow.AddMinutes(-5), // Clock skew tolerance
ExpiresOn = DateTimeOffset.UtcNow.Add(validity)
};
// Step 3: Set specific permissions (principle of least privilege)
sasBuilder.SetPermissions(BlobSasPermissions.Read | BlobSasPermissions.Write);
// Step 4: Generate signed SAS token
var sasToken = sasBuilder.ToSasQueryParameters(
userDelegationKey,
_storageAccountName);
return sasToken.ToString();
}
}
// ASP.NET Core integration
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<UserDelegationSasService>(provider =>
{
var configuration = provider.GetRequiredService<IConfiguration>();
var storageAccountName = configuration["Azure:StorageAccountName"];
return new UserDelegationSasService(storageAccountName);
});
}
JavaScript/Node.js with Service Principal
const { BlobServiceClient, generateBlobSASQueryParameters, BlobSASPermissions } = require('@azure/storage-blob');
const { ClientSecretCredential } = require('@azure/identity');
class UserDelegationSasService {
constructor(storageAccountName, tenantId, clientId, clientSecret) {
this.storageAccountName = storageAccountName;
// Service principal authentication
const credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
this.blobServiceClient = new BlobServiceClient(
`https://${storageAccountName}.blob.core.windows.net`,
credential
);
}
async createUserDelegationSas(containerName, blobName, validityHours = 1) {
const now = new Date();
const startTime = new Date(now.getTime() - 5 * 60 * 1000); // 5 minutes ago
const expiryTime = new Date(now.getTime() + validityHours * 60 * 60 * 1000);
// Get user delegation key
const userDelegationKey = await this.blobServiceClient.getUserDelegationKey(
startTime,
expiryTime
);
// Generate SAS with specific permissions
const sasQueryParams = generateBlobSASQueryParameters({
containerName,
blobName,
permissions: BlobSASPermissions.parse('rw'), // read, write
startsOn: startTime,
expiresOn: expiryTime
}, userDelegationKey, this.storageAccountName);
return sasQueryParams.toString();
}
}
Storage account access vs Azure AD authentication relationship
User Delegation SAS operates under a dual permission model:
Level 1: Azure AD RBAC Permissions (Creation Time)
- Required for generating User Delegation Keys
- Evaluated when your app calls
generateUserDelegationKey
- Does not grant direct data access, only SAS creation capability
- Scoped at subscription, resource group, storage account, or container level
Level 2: SAS Token Permissions (Usage Time)
- Specified in the SAS token itself (read, write, delete, list, etc.)
- Independent of the creator's RBAC permissions
- Can be more restrictive than creator's permissions
- Evaluated for each storage operation using the SAS
Permission interaction examples
Scenario 1: High privilege creator, limited SAS
Creator RBAC: Storage Blob Data Owner (full access)
SAS Permissions: Read only
Result: SAS users can only read, despite creator having full access
Scenario 2: Creator with delegation permission only
Creator RBAC: Storage Blob Delegator (SAS creation only)
SAS Permissions: Read/Write
Result: SAS creation succeeds, tokens work for read/write operations
Multi-tenant web application considerations
Tenant isolation strategies
Container-per-tenant approach:
public async Task<string> CreateTenantSasAsync(string tenantId, string fileName, TimeSpan validity)
{
// Use tenant-specific container
var containerName = $"tenant-{tenantId}";
// Ensure container exists with proper access controls
var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
await containerClient.CreateIfNotExistsAsync();
return await CreateUserDelegationSasAsync(containerName, fileName, validity);
}
Path-based isolation:
public async Task<string> CreateTenantSasAsync(string tenantId, string fileName, TimeSpan validity)
{
// Use tenant-specific path within shared container
var blobName = $"tenants/{tenantId}/{fileName}";
return await CreateUserDelegationSasAsync("shared-container", blobName, validity);
}
Azure AD configuration for multi-tenancy
For B2B scenarios (business tenants):
var options = new DefaultAzureCredentialOptions
{
TenantId = guestTenantId // Target tenant for B2B scenario
};
var credential = new DefaultAzureCredential(options);
Best practices for credential management
Production deployment checklist
✅ Security best practices:
- Use managed identities for Azure-hosted applications
- Implement certificate-based authentication for service principals
- Never hardcode credentials in source code
- Store secrets in Azure Key Vault when required
- Enable comprehensive audit logging
✅ Operational best practices:
- Implement short-lived SAS tokens (1-24 hours typical)
- Cache user delegation keys (reuse for up to 7 days)
- Set up proper error handling for authentication failures
- Monitor SAS token usage patterns
- Implement token revocation procedures
✅ Multi-tenant considerations:
- Ensure proper tenant isolation in storage structure
- Validate tenant context before SAS generation
- Implement tenant-specific monitoring and alerting
- Consider data residency requirements per tenant
Common security pitfalls to avoid
❌ Authentication mistakes:
- Using personal login credentials in production
- Overly permissive application permissions
- Weak session management practices
- Misconfigured redirect URIs
❌ SAS token security issues:
- Overly broad SAS permissions
- Extended validity periods without revocation
- Distributing SAS tokens over HTTP
- Lack of usage monitoring
❌ Credential management errors:
- Hardcoded credentials in source code
- Shared credentials across applications
- Missing credential rotation procedures
- Insufficient credential protection
Monitoring and compliance
Essential monitoring setup
Azure Storage logs to track:
- User delegation key generation events
- SAS token usage patterns
- Failed authentication attempts
- Unusual access patterns
Key alerts to configure:
- Unusual SAS token usage patterns
- Failed authentication attempts
- Access from unexpected locations
- High-volume data access
Compliance considerations
Regulatory requirements:
- GDPR: Data encryption, access logging, audit trails
- HIPAA: Access controls, audit logging, encryption
- SOC 2: Security controls, monitoring, incident response
Azure Policy enforcement:
- Disable shared key access on storage accounts
- Require encryption in transit and at rest
- Enforce network access restrictions
- Mandate audit logging
Conclusion
User Delegation SAS with Azure AD credentials provides enterprise-grade security for storage access delegation. The key is understanding that your web application's Azure AD identity (managed identity or service principal) creates the SAS tokens, while the actual users of those tokens don't need Azure AD authentication.
For immediate implementation:
- Use managed identity if your web app runs on Azure
- Assign "Storage Blob Delegator" role to your app's identity
- Use
DefaultAzureCredential
in your code for automatic credential discovery - Implement short-lived SAS tokens with minimal required permissions
- Set up comprehensive monitoring and audit logging
This approach eliminates the security risks of storage account keys while providing flexible, auditable access control for your multi-tenant web application.
No comments:
Post a Comment