Client applications have to make a request over HTTP/HTTPS to Azure Storage to access data in Azure storage account. As the resources are secured, so every request must be authorised to confirm that client is permitted to access the requested data. Azure Storage allows 4 different ways for authorising access to secured resources-

>>Azure Active Direcory (AAD) integration – Follow the steps in these articles to achieves this using Postman-

  1. create-an-azure-service-principal
  2. how-to-authenticate-azure-service-principal-with-client-secret-using-postman
  3. how-to-authenticate-service-principal-with-client_secret-in-pre-request-scripts-using-postman
  4. how-to-use-azure-blob-storage-service-rest-api-operations-using-postman

>> Shared Key Authorisation: We will detailed steps in this article on how to achieve this.

>> Shared Access Signature

>> Anonymous public read access

 

SHARED KEY Authorization:

The Blob, Queue, Table, and File services support the following Shared Key authorization schemes for version 2009-09-19 and later (for Blob, Queue, and Table service)

We will try to create a container in an storage account by authorising using Shared Key.

>>Open Postman and create a collection.

>> Add a PUT request to add a container  (testconnt) in storage account (tblobaccountstorage). Create environment variable “header_date”, “azure_storage_account”, “azure_storage_key” and “header_authorization”.

Add the values to header as shown in screen shot

>> Now copy / paste the below script into Pre-Request tab

const crypto = require("crypto-js");

// Set Date header value for authorization
// Should be UTC GMT string
pm.environment.set("header_date", new Date().toUTCString());
console.log("header date", new Date().toUTCString());
// Sadness: https://github.com/postmanlabs/postman-app-support/issues/2922
var substituteEnvVars = function (varStr) {
    let match;
    let replacedVar = varStr;
    
    while ((match = /\{\{([^{}]*?)}}/g.exec(replacedVar)) !== null ) {
        if (!pm.variables.get(match[1])) {
            continue;
        }
        var envVar = new RegExp(RegExp.escape(match[0]), 'g');
        replacedVar = replacedVar.replace(envVar, pm.variables.get(match[1]));
    }
    
    return replacedVar;
};

// Get hash of all header-name:value
const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });

// Construct Signature value for Authorization header
var signatureParts = [
    pm.request.method.toUpperCase(),
    headers["content-encoding"] || "",
    headers["content-language"] || "",
    pm.request.body.toString().length || "",
    headers["content-md5"] || "",
    headers["content-type"] || "",
    headers["x-ms-date"] ? "" : (pm.variables.get("header_date") || ""),
    headers["if-modified-since"] || "",
    headers["if-match"] || "",
    headers["if-none-match"] || "",
    headers["if-unmodified-since"] || "",
    headers["range"] || ""
];

// Construct CanonicalizedHeaders
const canonicalHeaderNames = [];
Object.keys(headers).forEach(key => {
    if (key.startsWith("x-ms-")) {
        canonicalHeaderNames.push(key);
        console.log("key: ", canonicalHeaderNames);
    }
});
// Sort headers lexographically by name
canonicalHeaderNames.sort();

const canonicalHeaderParts = [];
canonicalHeaderNames.forEach(key => {
    let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];
    console.log("value: ",value);
    // Populate variables
    value = substituteEnvVars(value);
    console.log("value: ",value);
    // Replace whitespace in value but not if its within quotes
    if (!value.startsWith("\"")) {
        value = value.replace(/\s+/, " ");
        console.log("value: ",value);
    }
    
    canonicalHeaderParts.push(`${key}:${value}`);
    console.log("canonicalHeaderParts: ",canonicalHeaderParts);
});

// Add headers to signature
signatureParts.push.apply(signatureParts, canonicalHeaderParts);

// Construct CanonicalizedResource
const canonicalResourceParts = [
    `/${pm.environment.get("azure_storage_account")}${pm.request.url.getPath()}`
];
console.log("canonicalResourceParts: ",canonicalResourceParts);
const canonicalQueryNames = [];
pm.request.url.query.each(query => {
    canonicalQueryNames.push(query.key.toLowerCase());
    console.log("canonicalQueryNames: ",query.key.toLowerCase());
});
canonicalQueryNames.sort();
canonicalQueryNames.forEach(queryName => {
    const value = pm.request.url.query.get(queryName);
    console.log("canonicalQueryNames: ",value);
    // NOTE: This does not properly explode multiple same query params' values
    // and turn them into comma-separated list
    canonicalResourceParts.push(`${queryName}:${value}`);
    console.log("canonicalResourcePartss: ",canonicalResourceParts);
});
// Add resource to signature
signatureParts.push.apply(signatureParts, canonicalResourceParts);

console.log("Signature Parts", signatureParts);

// Now, construct signature raw string
const signatureRaw = signatureParts.join("\n");

console.log("Signature String", JSON.stringify(signatureRaw));

// Hash it using HMAC-SHA256 and then encode using base64
const storageKey = pm.environment.get("azure_storage_key");
const signatureBytes = crypto.HmacSHA256(signatureRaw, crypto.enc.Base64.parse(storageKey));
const signatureEncoded = signatureBytes.toString(crypto.enc.Base64);

console.log("Storage Account", pm.environment.get("azure_storage_account"));
console.log("Storage Key", storageKey);

// Finally, make it available for headers
pm.variables.set("header_authorization", 
    `SharedKey ${pm.environment.get("azure_storage_account")}:${signatureEncoded}`);

Note: More details about the script can be found here.

 

>>Save the request and click Send to create “testconnt” blob container in storage account using the SharedKey which is stored in environment variable “header_authorization”. Successfull submit will show