Monday, June 23, 2025

SAS Azure

 

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:

  1. Azure AD Authentication: Your web application authenticates with Azure AD using one of several credential types
  2. User Delegation Key Acquisition: The authenticated app requests a special cryptographic key from Azure Storage
  3. SAS Token Generation: Your app signs the SAS parameters using this key
  4. 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 secret
  • CertificateCredential: Uses application ID and certificate
  • Requires manual credential management and rotation

Development/Testing

  • DefaultAzureCredential: Automatically discovers available authentication methods
  • AzureCliCredential: Uses Azure CLI authentication context
  • InteractiveBrowserCredential: 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:

  1. Use managed identity if your web app runs on Azure
  2. Assign "Storage Blob Delegator" role to your app's identity
  3. Use DefaultAzureCredential in your code for automatic credential discovery
  4. Implement short-lived SAS tokens with minimal required permissions
  5. 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.

Saturday, June 21, 2025

azure blob storage

Azure File Sharing Architecture Report

Executive Summary

This document evaluates different approaches for securely sharing CSV files with external partner organizations through our multi-tenant web application. The key requirement is to share files that auto-expire after 30 days while preventing unauthorized access through email forwarding.

Business Context

  • Current Setup: Multi-tenant web application with Microsoft Graph API permissions from partner organizations
  • Requirement: Share CSV files with external users from partner organizations
  • Security Concern: Prevent unauthorized access if emails containing file links are forwarded
  • Compliance: Files must automatically expire/delete after 30 days
  • Architecture: Cross-tenant scenario (our storage account in our tenant, external users in their own tenants)

Technical Approaches Evaluated

1. SAS (Shared Access Signature) Tokens - ❌ Not Recommended

Description: Generate time-limited URLs that provide direct access to Azure Storage files.

Types of SAS:

  • Regular SAS: Created using storage account keys
  • User Delegation SAS: Created using Azure AD credentials (more secure)
  • Account SAS: Grants access to entire storage account
  • Service SAS: Scoped to specific service (Blob, Queue, Table, File)

Implementation:

1. Generate SAS token with 30-day expiry
2. Email SAS URL directly to users
3. Users access files directly from Azure Storage

Advantages:

  • Simple implementation
  • Direct access to Azure Storage (good performance)
  • Built-in expiry mechanism

Critical Disadvantages:

  • Forwarding Risk: Anyone who receives forwarded email can access files
  • No identity verification: Bearer token approach
  • Limited audit trail: Difficult to track who actually accessed files
  • No granular permission control: Access is binary (have URL = access)

Verdict: ❌ Rejected due to security concerns

2. Azure AD Authentication + Web App Gateway - ✅ Recommended

Description: External users authenticate through our multi-tenant web app, which then serves files directly from storage.

Implementation:

1. User clicks link in email → Redirected to our web app
2. User authenticates using their organizational credentials (Azure AD)
3. Our app validates user is from authorized organization
4. Our app reads file from storage using managed identity
5. Our app serves file directly to authenticated user

Authentication Flow:

  • External Users: OAuth 2.0 through multi-tenant Azure AD
  • App to Storage: Managed Identity with Storage Blob Data Reader role
  • File Lifecycle: Azure Blob Lifecycle Management (30-day auto-deletion)

Advantages:

  • No forwarding risk: Users must authenticate to access files
  • Identity-based security: Leverages existing organizational credentials
  • Full audit trail: Complete logging of who accessed what
  • Granular control: Can restrict access by user/organization
  • Leverages existing infrastructure: Uses current multi-tenant app setup
  • Microsoft's recommended approach: Follows security best practices

Disadvantages:

  • Uses app bandwidth for file serving
  • Slightly more complex implementation
  • App becomes bottleneck for large files

Performance Considerations:

  • Suitable for typical CSV file sizes
  • May impact app performance under heavy concurrent usage

3. Hybrid Approach: Azure AD + Dynamic SAS Generation - ✅ Alternative Option

Description: Users authenticate through web app, which then generates short-lived SAS tokens for direct storage access.

Implementation:

1. User clicks link in email → Redirected to our web app
2. User authenticates using organizational credentials
3. Our app validates permissions for specific file
4. Our app generates short-lived SAS token (15-30 minutes)
5. User downloads directly from Azure Storage using SAS token

Advantages:

  • No forwarding risk: Authentication required before SAS generation
  • Better performance: Direct downloads from Azure Storage
  • Scalability: App only handles authentication, not file serving
  • Cost efficiency: Lower bandwidth costs for app
  • Identity-based security: Maintains authentication requirements

Disadvantages:

  • More complex implementation (SAS generation logic)
  • Slightly larger attack surface (SAS tokens exist, even if short-lived)

Alternative Approaches Considered and Rejected

Azure AD B2B (Guest Users)

Why Rejected: Requires manual invitation process and guest user management overhead for each partner organization user.

Direct Azure AD Authentication to Storage

Why Rejected: Complex cross-tenant configuration required since our storage is in our tenant while external users are in their own tenants.

Shared Key Authorization

Why Rejected: Requires sharing sensitive storage account keys; not suitable for external user scenarios.

Technical Architecture Details

Storage Configuration

  • Service: Azure Blob Storage
  • Lifecycle Management: Automatic deletion after 30 days
  • Access Control: Role-Based Access Control (RBAC)
  • Required Role: Storage Blob Data Reader (for app's managed identity)

Authentication Infrastructure

  • Identity Provider: Microsoft Entra ID (formerly Azure AD)
  • App Registration: Multi-tenant configuration
  • Token Type: OAuth 2.0 access tokens
  • Cross-tenant Support: Built-in multi-tenant capabilities

Security Features

  • Encryption: HTTPS for all communications
  • Audit Logging: Full request/response logging
  • Identity Verification: Organizational credential requirements
  • Permission Scoping: Granular access control per user/organization

Recommendations

Primary Recommendation: Azure AD + Web App Gateway

Rationale:

  • Addresses all security requirements
  • Leverages existing multi-tenant infrastructure
  • Provides complete audit trail
  • Follows Microsoft security best practices
  • Suitable for typical CSV file sharing scenarios

Implementation Priority: High

Secondary Option: Hybrid Approach

When to Consider:

  • Large file sizes (>10MB)
  • High concurrent usage expected
  • Performance optimization is critical

Implementation Priority: Medium (consider for future optimization)

Implementation Roadmap

Phase 1: Core Implementation (2-3 weeks)

  1. Configure Azure Blob Lifecycle Management (30-day deletion)
  2. Set up managed identity for web app
  3. Implement authentication endpoint for external users
  4. Create file serving endpoint with permission validation
  5. Update email templates with web app links

Phase 2: Optimization (1-2 weeks)

  1. Add comprehensive logging and monitoring
  2. Implement file access analytics
  3. Add user-friendly error handling
  4. Performance testing and optimization

Phase 3: Future Enhancements (Optional)

  1. Consider hybrid approach if performance issues arise
  2. Add file preview capabilities
  3. Implement bulk download features

Risk Assessment

Security Risks: Low

  • Authentication required for all access
  • No direct storage URLs in emails
  • Complete audit trail
  • Automatic file expiry

Performance Risks: Low-Medium

  • App bandwidth usage for file serving
  • Potential bottleneck under high load
  • Mitigated by typical CSV file sizes

Implementation Risks: Low

  • Leverages existing authentication infrastructure
  • Well-documented Azure services
  • Clear migration path from current setup

Cost Implications

Storage Costs

  • Standard Azure Blob Storage pricing
  • Lifecycle management included
  • Minimal cost impact

Compute Costs

  • Slight increase in app compute usage for file serving
  • Offset by improved security and compliance

Development Costs

  • Estimated 3-4 weeks development time
  • Leverages existing team Azure expertise

Conclusion

The Azure AD Authentication + Web App Gateway approach provides the optimal balance of security, functionality, and implementation simplicity for our use case. It eliminates the email forwarding security risk while leveraging our existing multi-tenant infrastructure and following Microsoft's recommended security practices.

The hybrid approach should be considered as a future optimization if performance requirements change or file sizes significantly increase.