Azure Blob storage 403 not authorized, decoded: permission mismatch, network block, shared key or SAS
Every Blob storage 403 carries a second code that says which of four things refused it: AuthorizationPermissionMismatch (the identity or SAS lacks a data permission), AuthorizationFailure (a firewall, private endpoint or network rule), KeyBasedAuthenticationNotPermitted (shared key is off), or a SAS that's wrong in a field. What each means, the role that fixes it, the waits, and the DNS check most people skip.
Data & storageFix a problem8 min read
"This request is not authorized to perform this operation" is the Blob storage 403 everyone quotes, and on its own it tells you almost nothing. The useful part is the error code beside it, which says whether the identity lacks a data permission, the network refused the request before any permission was checked, shared key access has been switched off, or a SAS token is missing a letter. This guide decodes the codes, then takes each cause in turn: the data-plane role an Owner still doesn't have, the firewall and private-endpoint rules that answer 403 instead of "connection refused", the account-key setting that breaks older tools, and the SAS fields that have to line up. For the Azure-wide version (Key Vault, Cosmos DB, Resource Manager), see 403 after assigning a role; this one is Blob only, and goes deeper.
Read the second code
| Error code and message | Which cause |
|---|---|
AuthorizationPermissionMismatch: "This request is not authorized to perform this operation using this permission" | The identity's role, or the SAS token's permissions, don't cover the operation |
AuthorizationFailure: "This request is not authorized to perform this operation" | The network: public access, IP rules, virtual network rules, a private endpoint, a network security perimeter |
AuthorizationServiceMismatch: "... using this service" | An account SAS made for a different service (its ss field) |
KeyBasedAuthenticationNotPermitted | The client used the account key, or a SAS signed with it, and the account disallows shared key |
| In the portal: "You do not have permissions to list the data using your user account with Microsoft Entra ID" | Your account lacks Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read, a data permission |
| In the portal, when just viewing containers | A control-plane read permission is missing; the Reader role covers it |
Resource logs for the account show which authorisation type each request used (OAuth, SAS, shared key) and the error code, which settles arguments about what the client actually sent.
Permission mismatch: the identity needs a data role
Azure roles have Actions for managing a resource and DataActions for using it. Owner, Contributor and Storage Account Contributor can create and configure a storage account and have no data actions at all; they can't list or read a blob through Entra ID. Only the data roles can:
| To do this | Assign |
|---|---|
| Read and list blobs | Storage Blob Data Reader |
| Read, write and delete blobs (overwriting needs delete too) | Storage Blob Data Contributor |
| Everything, including POSIX ACLs on Data Lake Storage | Storage Blob Data Owner |
Assign at the smallest scope that works, the container or the account, not the subscription; add an ABAC condition if only a path or a tag should be reachable. Then wait. Learn says a role assignment or removal takes up to 10 minutes to take effect, Storage's own pages say up to 30 minutes in some cases, and a role with data actions assigned at management-group scope can take several hours. Signing out and in, or refreshing the token, picks it up sooner; a managed identity that got the role through a group caches its token for around 24 hours.
Two traps:
- "But it works in the portal." The portal defaults to the account key when your role includes
Microsoft.Storage/storageAccounts/listKeys/action, which Contributor does. Switch the portal's authentication method to your Entra account, or test with the CLI using--auth-mode login, and you'll see what the application sees. - Databricks workspaces. Data roles assigned at subscription scope don't reach a storage account in a Databricks-managed resource group; assign at the resource group, account or container.
An encryption scope on the container adds its own 403s when the scope is disabled or its customer-managed key can't be reached, and a disabled storage account or subscription refuses everything; both show up in the resource logs as reasons rather than as permission problems.
Authorization failure: the network answered first
With AuthorizationFailure, the request never reached a permission check. Work down the list:
- Public network access on the account is disabled, and the client is on the public internet. Either it needs a private endpoint, or public access from selected networks.
- IP rules: your client's public address isn't in the allow list. Changes take up to five minutes.
- Virtual network rules: traffic from every subnet is blocked until a rule allows that subnet; a client in an unlisted subnet gets 403.
- Service endpoint policies on the subscription, resource group or account: up to 15 minutes to apply.
- A network security perimeter rule overrides the firewall; enable its diagnostic logs to see which rule refused the request.
- Trusted services: the portal, a hosted pipeline agent, Data Factory and your laptop are outside the network. Allow trusted Microsoft services on the account, add the client IP for the session, or run the agent inside the network.
With a private endpoint, the usual fault is DNS. From the client, resolve <account>.blob.core.windows.net: a private address (10.x, 172.16 to 172.31, 192.168) means the private endpoint is in use; a public address means the request is arriving at the public endpoint, where the firewall refuses it. The fix is linking the private DNS zone to the virtual network, or forwarding the privatelink.blob.core.windows.net zone from your own DNS. Two more: Data Lake Storage uses the dfs endpoint, which needs its own private endpoint beside the blob one, because some operations are redirected between them; and copying between accounts needs network access to both the source and the destination.
Shared key is off
Disallowing shared key authorization (allowSharedKeyAccess: false) is Microsoft's recommendation and a prerequisite for Conditional Access on storage. Once it's set, any request signed with an account key fails with KeyBasedAuthenticationNotPermitted, and so does every service SAS and account SAS, because those are signed with the key. What still works: Entra ID, and a user delegation SAS, which is signed with an Entra credential and can be revoked all at once by revoking the delegation key.
The clients that break are the ones you forgot: an old script with the connection string in it, a Logic App or Data Factory linked service on "account key", Storage Explorer with key usage enabled, a third party's integration. Resource logs list them by authorisation type before you flip the switch. If the logs still show shared key after you've disallowed it, that's trusted access from a resource in your tenant, which the setting allows.
SAS: permissions, service and time
A SAS that was wrong in one field fails the same way as a missing role. Check, in order:
sp, the permissions. The operation needs every permission it uses: overwriting a blob needs write and delete; listing needs list; copying needs read on the source.ss, the services (account SAS):bfor blobs,ffiles,qqueues,ttables. A token made for files is refused by Blob withAuthorizationServiceMismatch.- Start and expiry. A start time in the future (including clock skew) or an expiry in the past is a 403, not a 401. Keep service SAS lifetimes to an hour unless a stored access policy backs them, and set an account-level SAS expiration policy so long-lived tokens are logged.
- Protocol. A token issued for HTTPS only fails over HTTP.
- A stored access policy that was just created or changed takes time to propagate; a request in the first moments after can fail.
- User delegation SAS: the signing identity needs the right to generate the delegation key, and the key's own expiry caps every token made from it.
Prefer a user delegation SAS to a service SAS wherever the client is Blob; it's the one that survives shared key being turned off.
Prove it before you close the ticket
# What roles does the caller actually hold, and at what scope?
az role assignment list --assignee <object id> --all -o table
# The data plane as that identity, with Entra auth (never the key)
az storage blob list --account-name <account> --container-name <container> --auth-mode login -o table
# Is the private endpoint in use from here?
nslookup <account>.blob.core.windows.net
Run the blob listing as the identity that will do the work (az login --identity on a VM, or from the app's own logs), because your access proves nothing about the caller's. If the listing works with --auth-mode login and the application still gets 403, the application is using a key or a SAS, and the sections above say which.
Sources
- Troubleshoot 403 errors in Azure Blob Storage, Microsoft Learn (the error codes, the diagnostic checklist, SAS, private endpoints, encryption scopes)
- Troubleshoot Azure Blob Storage access issues in the Azure portal, Microsoft Learn
- Authorize access to blobs by using Microsoft Entra ID, Microsoft Learn (data roles, the listKeys loophole, propagation delays)
- Assign an Azure role for access to blob data, Microsoft Learn
- Troubleshoot Azure RBAC, Microsoft Learn (caching, management-group scope, managed identities in groups)
- Prevent Shared Key authorization for an Azure Storage account, Microsoft Learn (what each SAS type does when shared key is off; trusted access in the logs)
- Secure your Azure Storage account, Microsoft Learn (RBAC at the smallest scope, ABAC, user delegation SAS, SAS expiration policy)
- Use private endpoints for Azure Storage, Microsoft Learn (DNS changes)
Did this fix it?
Comments
No comments yet. Share what worked for you.
Sign in to comment. Comments show under a display name you choose, never your email.