DevOpsil

Azure Blob Storage 403 AuthorizationPermissionMismatch: Step-by-Step Fix Guide

Muhammad HassanMuhammad Hassan8 min read

Azure Blob Storage 403 AuthorizationPermissionMismatch: Step-by-Step Fix Guide

If you've ever stared at this error message in your logs, you know the frustration:

AuthorizationPermissionMismatch: This request is not authorized to perform this operation using this permission.

It's one of those errors that looks simple on the surface but has about a dozen different root causes. I've debugged this across dozens of Azure deployments, and the fix is almost never the same twice. Let's cut through the noise and get your storage access working.

What This Error Actually Means

Before jumping into fixes, let's understand what Azure is telling you. AuthorizationPermissionMismatch specifically means:

  • Azure authenticated you successfully (it knows who you are)
  • But the credentials you're using don't have the permissions required for the operation you're attempting

This is different from AuthorizationFailure (wrong credentials) or NoAuthenticationInformation (missing credentials entirely). You're authenticated — you just don't have the right access level.

The mismatch can happen at three layers:

  1. RBAC (Role-Based Access Control) — Azure AD assignments
  2. SAS Token permissions — Shared Access Signature scope issues
  3. Blob-level access policies — Container public access or stored access policies

Let's tackle each one.


Step 1: Identify Your Authentication Method

First, determine how you're authenticating. Run this quick diagnostic:

# Check what auth method your app is using
az storage blob list \
  --account-name <your-storage-account> \
  --container-name <your-container> \
  --auth-mode login \
  --output table

If that works, your problem is with the credentials your application is using (SAS token, connection string, or managed identity). If it fails, you have an RBAC issue at the Azure AD level.


Step 2: Fix RBAC Permission Issues

This is the most common cause when using Azure AD authentication or Managed Identity.

Check Current Role Assignments

# Get the storage account resource ID
STORAGE_ID=$(az storage account show \
  --name <your-storage-account> \
  --resource-group <your-resource-group> \
  --query id -o tsv)

# List current role assignments
az role assignment list \
  --scope $STORAGE_ID \
  --output table

Assign the Correct Role

Azure Storage has specific data-plane roles — don't confuse them with management-plane roles. Owner or Contributor on the storage account does NOT grant blob data access. You need these:

OperationRequired Role
Read blobsStorage Blob Data Reader
Read + Write blobsStorage Blob Data Contributor
Full control + manage accessStorage Blob Data Owner
Queue operationsStorage Queue Data Contributor
# Assign Storage Blob Data Contributor to a service principal
az role assignment create \
  --assignee <service-principal-id-or-object-id> \
  --role "Storage Blob Data Contributor" \
  --scope $STORAGE_ID

# Or scope it to a specific container (more secure)
az role assignment create \
  --assignee <service-principal-id-or-object-id> \
  --role "Storage Blob Data Contributor" \
  --scope "${STORAGE_ID}/blobServices/default/containers/<container-name>"

Pro tip: Always scope RBAC assignments to the smallest possible resource. Container-level scope is better than account-level scope.

For Managed Identity Specifically

If your app runs on an Azure VM, App Service, or Container Instance:

# Get the managed identity object ID
IDENTITY_ID=$(az webapp identity show \
  --name <your-app-name> \
  --resource-group <your-resource-group> \
  --query principalId -o tsv)

# Assign the role
az role assignment create \
  --assignee $IDENTITY_ID \
  --role "Storage Blob Data Contributor" \
  --scope $STORAGE_ID

Wait 2-5 minutes after assignment. RBAC propagation across Azure isn't instant — I've been burned by this many times.


Step 3: Fix SAS Token Permission Issues

SAS tokens are scoped at creation time and encode specific permissions. If the token was generated with read-only permissions but your code tries to write, you'll get AuthorizationPermissionMismatch.

Decode Your SAS Token

SAS tokens are just URL-encoded query strings. Decode yours to see exactly what permissions it has:

from urllib.parse import urlparse, parse_qs

sas_token = "sv=2021-06-08&ss=b&srt=co&sp=rl&se=2024-12-31T00:00:00Z&st=2024-01-01T00:00:00Z&spr=https&sig=..."

# Parse the token
params = parse_qs(sas_token)
print(f"Services: {params.get('ss', ['unknown'])}")      # b=blob, q=queue, t=table, f=file
print(f"Resources: {params.get('srt', ['unknown'])}")    # s=service, c=container, o=object
print(f"Permissions: {params.get('sp', ['unknown'])}")   # r=read, w=write, d=delete, l=list, a=add, c=create, u=update, p=process
print(f"Expiry: {params.get('se', ['unknown'])}")

The sp parameter tells you everything. Common permission strings:

  • r — Read only
  • rl — Read + List
  • racwdl — Full access (Read, Add, Create, Write, Delete, List)

Generate a Correct SAS Token

from azure.storage.blob import BlobServiceClient, generate_account_sas, ResourceTypes, AccountSasPermissions
from datetime import datetime, timedelta, timezone

connection_string = "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net"

# Generate SAS with proper permissions
sas_token = generate_account_sas(
    account_name="<your-storage-account>",
    account_key="<your-account-key>",
    resource_types=ResourceTypes(service=False, container=True, object=True),
    permission=AccountSasPermissions(
        read=True,
        write=True,
        delete=True,
        list=True,
        add=True,
        create=True
    ),
    expiry=datetime.now(timezone.utc) + timedelta(hours=24)
)

print(f"SAS Token: {sas_token}")

For container-scoped SAS (more granular):

from azure.storage.blob import BlobServiceClient, generate_container_sas, ContainerSasPermissions

sas_token = generate_container_sas(
    account_name="<your-storage-account>",
    container_name="<your-container>",
    account_key="<your-account-key>",
    permission=ContainerSasPermissions(read=True, write=True, list=True, delete=False),
    expiry=datetime.now(timezone.utc) + timedelta(hours=1)
)

Step 4: Check Container Access Policies and Network Rules

Public Access Settings

If your code assumes public access but the container is private:

# Check container public access level
az storage container show \
  --name <your-container> \
  --account-name <your-storage-account> \
  --auth-mode login \
  --query properties.publicAccess

# Set to blob-level public read if needed
az storage container set-permission \
  --name <your-container> \
  --account-name <your-storage-account> \
  --auth-mode login \
  --public-access blob

Note: As of 2023, Azure now disables public blob access at the account level by default on new storage accounts. Even if a container allows public access, the account-level setting overrides it.

# Check account-level public access setting
az storage account show \
  --name <your-storage-account> \
  --resource-group <your-resource-group> \
  --query allowBlobPublicAccess

# Enable if needed (consider security implications)
az storage account update \
  --name <your-storage-account> \
  --resource-group <your-resource-group> \
  --allow-blob-public-access true

Firewall and Network Rules

If the storage account has network restrictions, requests from disallowed IPs get a 403:

# List network rules
az storage account show \
  --name <your-storage-account> \
  --resource-group <your-resource-group> \
  --query networkRuleSet

# Add your IP if needed
az storage account network-rule add \
  --account-name <your-storage-account> \
  --resource-group <your-resource-group> \
  --ip-address <your-ip>

Step 5: Validate with a Quick Test Script

Once you've made changes, don't just redeploy your app and hope for the best. Test directly:

import os
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
from azure.storage.blob import BlobServiceClient

def test_blob_access(storage_account_name: str, container_name: str):
    account_url = f"https://{storage_account_name}.blob.core.windows.net"
    
    # Test with DefaultAzureCredential (picks up managed identity, env vars, etc.)
    credential = DefaultAzureCredential()
    client = BlobServiceClient(account_url=account_url, credential=credential)
    
    container_client = client.get_container_client(container_name)
    
    # Test list
    print("Testing LIST...")
    blobs = list(container_client.list_blobs())
    print(f"✓ List succeeded. Found {len(blobs)} blobs")
    
    # Test write
    print("Testing WRITE...")
    test_blob = container_client.get_blob_client("permission-test.txt")
    test_blob.upload_blob("test content", overwrite=True)
    print("✓ Write succeeded")
    
    # Test read
    print("Testing READ...")
    data = test_blob.download_blob().readall()
    print(f"✓ Read succeeded: {data}")
    
    # Cleanup
    test_blob.delete_blob()
    print("✓ Delete succeeded")

if __name__ == "__main__":
    test_blob_access(
        storage_account_name="<your-storage-account>",
        container_name="<your-container>"
    )

Quick Reference: Error Cause Lookup

ScenarioLikely CauseFix
Using Managed Identity, error on writeMissing Storage Blob Data ContributorAdd RBAC role assignment
SAS token works for read, fails on writeSAS generated with read-only permissionsRegenerate SAS with write permission
Works in local dev, fails in productionLocal uses your AD credentials, prod uses managed identityFix managed identity RBAC
Works for some containers, not othersRBAC scoped to wrong containerCheck role assignment scope
Fails from specific IP/regionStorage firewall rulesAdd IP or VNet to allowed list
Error after storage account migrationStale credentials or changed endpointsUpdate connection strings

One Thing I See Constantly

People assign Contributor role at the subscription or resource group level and wonder why they can't access blob data. Management plane ≠ Data plane in Azure Storage.

Contributor lets you create, delete, and configure the storage account itself. It does absolutely nothing for reading or writing blobs, queues, or tables. You need the dedicated data-plane roles (Storage Blob Data Reader/Contributor/Owner) for that.

Azure made this distinction intentional — it's actually good security design. But it trips up everyone at least once.


Final Checklist

Before closing the ticket, verify:

  • RBAC role assignments are at the correct scope (account vs. container)
  • You're using data-plane roles, not management-plane roles
  • SAS token permissions match the operations being performed
  • SAS token hasn't expired
  • Account-level allowBlobPublicAccess matches your intent
  • Storage account firewall rules allow the client IP/VNet
  • Waited for RBAC propagation (2-5 minutes minimum)
  • Tested with the diagnostic script above

The AuthorizationPermissionMismatch error is annoying, but it's at least honest — Azure tells you exactly what category of problem you're dealing with. Follow this guide top to bottom, and you'll find the culprit.

Share:

Was this article helpful?

Muhammad Hassan
Muhammad Hassan

Network & Traffic Engineer

Packets don't lie. I design and troubleshoot the network layer that everything else depends on — Nginx, Envoy, HAProxy, DNS, CDNs, and everything in between. If it touches a socket, it's my problem.

Related Articles

Discussion