7

I've been struggling with this for about a day now. I am testing direct to Azure Blob storage upload and getting the dreaded CORS issue. "XMLHttpRequest cannot load https://tempodevelop.blob.core.windows.net/tmp/a4d8e867-f13e-343f-c6d3-a603…Ym0PlrBn%2BU/UzUs7QUhQw%3D&sv=2014-02-14&se=2016-10-12T17%3A59%3A26.638531. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403."

Things I have already tried:

  1. set the CORS to all hosts: enter image description here
  2. tried hosting my app locally and on heroku
  3. made sure that I could upload a file using a different tool (Azure Storage Explorer)
  4. configured my AccessPolicy to 'rwdl' and I am definitely getting an access signature (verified in unit tests).

The code as a whole is available here: https://github.com/mikebz/azureupload

But the relevant parts are here, front end upload:

<script>

    /* 
     * not a true GUID, see here: http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
     */ 
    function guid() {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
        }
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
            s4() + '-' + s4() + s4() + s4();
    }

    function startUpload() {   
        var fileName = guid();

        jQuery.getJSON("/formfileupload/signature/" + fileName , function(data) {
                console.log("got a signature: " + data.bloburl);
                uploadFile(data.bloburl, data.signature);
            })
            .fail(function(jqxhr, textStatus, error) {
                console.log( "error: " + textStatus + " - " + error );
            })
    }
    
    function uploadFile(bloburl, signature) {        
        var xhr = new XMLHttpRequest();
        fileData = document.getElementById('fileToUpload').files[0];
        xhr.open("PUT", bloburl + "?" + signature);
        xhr.setRequestHeader('x-ms-blob-type', 'BlockBlob');
        xhr.setRequestHeader('x-ms-blob-content-type', fileData.type);
        result = xhr.send(fileData);
    }
</script>

The signature generation code in python is here:

def generate_access_signature(self, filename):
    """
    calls the Azure Web service to generate a temporary access signature.
    """
    blob_service = BlobService(
        account_name=self.account_name, 
        account_key=self.account_key
    )

    expire_at = datetime.utcnow()
    expire_at = expire_at + timedelta(seconds = 30)
    access_policy = AccessPolicy(permission="rwdl", expiry=expire_at.isoformat())

    sas_token = blob_service.generate_shared_access_signature( 
        container_name="tmp",
        blob_name = filename, 
        shared_access_policy=SharedAccessPolicy(access_policy)
    )
    return sas_token
mikebz
  • 3,277
  • 8
  • 37
  • 50

2 Answers2

9

According to the error message [The response had HTTP status code 403], it may be the CORS is not enabled for the service or no CORS rules matches the preflight request. Detail Please refer to the Cross-Origin Resource Sharing (CORS) Support for the Azure Storage Services. Or it may be the SAS signature incorrect. Please have a try to troubleshoot

  1. try to check the CORS setting on the Azure Portal under the Blob Service. As there are other services like table, queue, file. Expected

  2. Also Azure explore tools you can use to generate the SAS token enter image description here

Get the SAS and try to debug it with the generated SAS demo code enter image description here

Tom Sun - MSFT
  • 24,161
  • 3
  • 30
  • 47
  • Thanks Tom! I followed your steps. Even with a manually generated signature the result is the same. I am wondering if there is a bug there where maybe this system is not conducive to uploading new data? Not sure... – mikebz Oct 14 '16 at 17:10
  • For my experience ,It has no relationship with the system. Do you have double check the CROS setting under the Blob servie in the Azure Portal? Please just replace the blob url and signature with your generated,and have a try with my script again. And try to debug with the browse like Chrome or Edge. If it can't be resolved,could you mind supplying the CROS setting and browse troubleshot snapshot like me supplied? – Tom Sun - MSFT Oct 17 '16 at 02:01
  • is this secure to allow CORS for all origins? – Josue Martinez Jul 15 '17 at 01:52
1

Thanks to Tom and Microsoft's support the issue has been resolved.
Solution part #1 - make sure you use the Azure Storage Library for Python version 0.33 or later.

Here is my requirements file:

azure-common==1.1.4
azure-nspkg==1.0.0
azure-storage==0.33.0
cffi==1.8.3
cryptography==1.5.2
dj-database-url==0.4.1
Django==1.10.2
enum34==1.1.6
futures==3.0.5
gunicorn==19.6.0
idna==2.1
ipaddress==1.0.17
pep8==1.7.0
psycopg2==2.6.2
pyasn1==0.1.9
pycparser==2.16
python-dateutil==2.5.3
requests==2.11.1
six==1.10.0
whitenoise==3.2.2

Second issue is generating the signature. The code that generates the right signature is here:

from azure.storage.blob import BlockBlobService, ContainerPermissions
from datetime import datetime, timedelta


class AzureUtils:

    def __init__(self, account_name, account_key):
        if account_name is None:
            raise ValueError("account_name should not be None")
        if account_key is None:
            raise ValueError("account_key should not be None")
        self.account_name = account_name
        self.account_key = account_key

    def generate_access_signature(self, filename):
        """
        calls the Azure Web service to generate a temporary access signature.
        """
        block_blob_service = BlockBlobService(
            account_name=self.account_name,
            account_key=self.account_key
        )

        expire_at = datetime.utcnow()
        expire_at = expire_at + timedelta(seconds=30)

        permissions = ContainerPermissions.READ | ContainerPermissions.WRITE | ContainerPermissions.DELETE | ContainerPermissions.LIST

        sas_token = block_blob_service.generate_container_shared_access_signature(
                "tmp",
                permission=permissions,
                expiry=expire_at
        )

        return sas_token

The solution can also be retrieved here: https://github.com/mikebz/azureupload

mikebz
  • 3,277
  • 8
  • 37
  • 50