Why I'm not able to Authenticate to blob storage with generated SAS Token for container?

147 views Asked by At

I'm using Azure functions to generate a SAS Token for Container level. Here is the code:

const {
  StorageSharedKeyCredential,
  ContainerSASPermissions,
  SASProtocol,
  generateBlobSASQueryParameters,
} = require('@azure/storage-blob');
require('dotenv').config();
const {STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY1} = process.env;

const constants = {
  accountName: STORAGE_ACCOUNT_NAME,
  accountKey: STORAGE_ACCOUNT_KEY1
};

const sharedKeyCredential = new StorageSharedKeyCredential(
  constants.accountName,
  constants.accountKey
);

async function createContainerSas(containerName) {
  const sasOptions = {
    containerName,
    permissions: ContainerSASPermissions.parse("racwli"),
    startsOn: new Date(),
    expiresOn: new Date(new Date().valueOf() + 86400000),
    protocol: SASProtocol.Https,
  };

  const sasToken = generateBlobSASQueryParameters(sasOptions, sharedKeyCredential).toString();
  return sasToken;
}

module.exports = async function (context, req) {
  const sasToken = await createContainerSas(req.query.containerName);
  context.res = {
    body: {
      sasToken
    }
  };
};

I'm runing locally as http://localhost:7071/api/blobstorage-controller/{CONTAINER_NAME}?comp=list&restype=container&{GENERATED_SAS_TOKEN} and a deployed version.

When running the URL above I'm getting the following error:

<?xml version="1.0" encoding="utf-8"?> <Error>     <Code>AuthenticationFailed</Code>     <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:0986c9be-d01e-000b-37ba-27627d000000 Time:2023-12-05T20:34:10.2329768Z</Message>     <AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail> </Error>

I followed every doc on Azure and was not able to make this work!

NOTE: Using a token genereted on Azure Portal works! But I'm expecting to get it working with only a SASToken.

[Edited]Alternative Solution

I came across this doc using ConnectionString: Exercise - Build your serverless backend

const {
  StorageSharedKeyCredential,
  ContainerSASPermissions,
  generateBlobSASQueryParameters
} = require("@azure/storage-blob");
require('dotenv').config()
const { extractConnectionStringParts } = require('./utils.js');
const {STORAGE_CONNECTION_STRING}= process.env;
  module.exports = async function (context, req) {  
  const reqPermissions = (req.query.permissions || req.body.permissions || 'rwl').split('');
  const permissionsOrder = ["r", "a", "c", "w", "d", "x", "y", "l", "t", "m", "e", "o", "p"]; 
  const permissions = reqPermissions.sort((a, b) => {
      return (
          permissionsOrder.indexOf(a) - permissionsOrder.indexOf(b)
      );
  }).join('');

    
  console.log(permissions);

  const container = req.query.containerName || req.body.containerName;
  context.res = {
      body: generateSasToken(STORAGE_CONNECTION_STRING, container, permissions)
  };
};

function generateSasToken(connectionString, container, permissions) {
  const { accountKey, accountName } = extractConnectionStringParts(connectionString);
  const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey.toString('base64'));

  let expiryDate = new Date();
  expiryDate.setHours(expiryDate.getHours() + 24);

  const sasToken = generateBlobSASQueryParameters({
      containerName: container,
      permissions: ContainerSASPermissions.parse(permissions),
      expiresOn: expiryDate,
  }, sharedKeyCredential);

  return {
      sasToken: sasToken.toString(),
  };
}

While I have no better way to do that, this alternative solution worked for me.

1

There are 1 answers

2
Vivek Vaibhav Shandilya On BEST ANSWER

When you are trying to access Azure blob container you need to use https://storageaccount.blob.core.windows.net/containername?restype=container&comp=list

if you are trying to access Emulated Storage container use http://127.0.0.1:10000/devstoreaccount1/mycontainer?restype=container&comp=list

For reference check this document.

This code worked for me.

const {
    StorageSharedKeyCredential,
    ContainerSASPermissions,
    SASProtocol,
    generateBlobSASQueryParameters,
  } = require('@azure/storage-blob');
  require('dotenv').config();
  
  const { STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY1 } = process.env;
  
  const constants = {
    accountName: STORAGE_ACCOUNT_NAME,
    accountKey: STORAGE_ACCOUNT_KEY1,
  };
  
  const sharedKeyCredential = new StorageSharedKeyCredential(
    constants.accountName,
    constants.accountKey
  );
  
  async function createContainerSas(containerName) {
    try{
    const sasOptions = {
      containerName,
      permissions: ContainerSASPermissions.parse("racdl"),
      protocol: SASProtocol.HttpsAndHttp,
      startsOn: new Date(),
      expiresOn: new Date(new Date().valueOf()+86400000),
      
    };
  
    const sasToken = generateBlobSASQueryParameters(
      sasOptions,
      sharedKeyCredential
    ).toString();
  
    return sasToken;
  }
    catch (error){
        console.error("error: ", error);
        throw error;

}
  }
  
  module.exports = async function (context, req) {
    try{
    const containerName = req.query.containerName;
    const sasToken = await createContainerSas(containerName);

    console.info("container: ",containerName);
  
    const urlWithSasToken = `https://nodejsstoragecontainer.blob.core.windows.net/${containerName}?restype=container&comp=list&${sasToken}`;
    
    context.res = {
      body: {
        containerName,
        sasToken,
        urlWithSasToken,
      },
    };
  }
  catch (Error) {
    console.error("error: ",Error);
  }
};

OUTPUT:

enter image description here

enter image description here

enter image description here

enter image description here