2
votes

What I am trying to do: I have a clientside (Browser, not node.js) JS App that uploads files to Blob Storage with the @azure/storage-blob package. To do so, it fetches a SAS Token from an Azure Function for a specific Blob. The Blob is created by the Function on each request and a SAS Token is returned to the client. Blob and SAS generation works and I can download it when the Blob Url with SAS is opened in a Browser.

Now what does not work is when I try to connect with the BLOB SAS (not Storage Account SAS or Connection String) to the Storage Account. The code below works when used with SAS from the whole Storage Account, but I do not want to give that much of permissions. I do not understand why a SAS token can be created for a specific Blob, if it is not possible to connect to it via Blob Service Client.

It is possible to create a Read-only SAS token for the whole storage account to get the connection up and running. But where would the Blob SAS go afterwards, so that the Blob can be accessed?

There is something fundamental that I seem to miss, so how can this be accomplished?

const url = `https://${storageName}.blob.core.windows.net/${sasToken}`; // sas for blob from the azure function
// const url = `https://${storageName}.blob.core.windows.net${containerSas}`; // sas from container
// const url = `https://${storageName}.blob.core.windows.net/${containerName}/${fileName}${sasToken}`; // does not work either
const blobService = new BlobServiceClient(url);

await this.setCors(blobService);

// get Container
const containerClient: ContainerClient = blobService.getContainerClient(containerName);

// get client
const blobClient = containerClient.getBlockBlobClient(fileName);

const exists = await blobClient.exists();
console.log('Exists', exists);

// set mimetype as determined from browser with file upload control
const options: BlockBlobParallelUploadOptions = { blobHTTPHeaders: { blobContentType: file.type } };

// upload file
await blobClient.uploadBrowserData(file, options);

EDIT:

The SAS token for the Blob: ?sv=2018-03-28&sr=b&sig=somesecret&se=2021-07-04T15%3A14%3A28Z&sp=racwl

The CORS method, though I can confirm that it works when I use the global storageaccount SAS:

private async setCors(blobService: BlobServiceClient): Promise<void> {
  var props = await blobService.getProperties();
  props.
    cors =
    [{
      allowedOrigins: '*',
      allowedMethods: '*',
      allowedHeaders: '*',
      exposedHeaders: '*',
      maxAgeInSeconds: 3600
    }]
    ;
}

Errors:

When using the Blob SAS, at the setCors/getProperties method: 403 (Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.)

When using the following service url, at the setCors/getProperties method: https://${storageName}.blob.core.windows.net/${containerName}/${fileName}${sasToken} => RestError: The resource doesn't support specified Http Verb.

When using a Storage Account SAS with only READ permissions, when accessing the blob (blob.exists()): 403 (This request is not authorized to perform this operation using this resource type.) (makes sense, but then I would like to use the Blob-specific SAS)

1
Few questions: 1) Can you share what your SAS token looks like? You can obfuscate the sig portion of the token. 2) When you say "it's not working", can you please elaborate that? What part of the code is failing? and 3) Please include the code for setCors method. Please edit your question and provide that information.Gaurav Mantri

1 Answers

2
votes

The reason you're running into this error is because you're trying to set CORS using a SAS token created for a blob (which is a Service SAS). CORS operation is a service level operation and for that you need to use a SAS token obtained at the account level. In other words, you will need to use an Account SAS. Any SAS token created for either blob container or blob is a Service SAS token.

Having said this, you really don't need to set CORS properties on the storage account in each request. This is something you can do at the time of account creation.

Once you remove call to setCors method from your code, it should work fine.

Considering you're working with just a Blob SAS, you can simplify your code considerably by creating an instance of BlockBlobClient directly using the SAS URL. For example, your code could be as simple as:

const url = `https://${storageName}.blob.core.windows.net/${containerName}/${fileName}${sasToken}`;
const blobClient = new BlockBlobClient(url);
//...do operations on blob using this blob client