node.jsazureazure-functionsazure-blob-storagesas-token

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


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.


Solution

  • 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