0

I need to do this all within Postman application. I have found examples that use blob and tables but they didn't seem to fit with the queue storage. I think the biggest problem is that I am unable to create the signature part properly.

I believe I managed to execute Get Message in postman but I get "Server failed to authenticate the request." error upon Post request. The message I'm trying to post is in XML format and uses the same pre-request Script as the Get.

Pre-request Script of the Post and Get request (copied from online sources):

const storageAccount = pm.variables.get('azure_storage_account');
const accountKey = pm.variables.get('azure_storage_key');

pm.variables.set("header_date", new Date().toUTCString());

// 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"] || "",
    headers["content-length"]  || "",
//    pm.request.body ? 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);
    }
});
// Sort headers lexographically by name
canonicalHeaderNames.sort();

const canonicalHeaderParts = [];
canonicalHeaderNames.forEach(key => {
    let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];

    // Populate variables
    value = pm.variables.replaceIn(value);

    // Replace whitespace in value but not if its within quotes
    if (!value.startsWith("\"")) {
        value = value.replace(/\s+/, " ");
    }

    canonicalHeaderParts.push(`${key}:${value}`);
});

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

// Construct CanonicalizedResource
const canonicalResourceParts = [
    `/${pm.variables.get("azure_storage_account")}${pm.request.url.getPath()}`
];
const canonicalQueryNames = [];
pm.request.url.query.each(query => {
    canonicalQueryNames.push(query.key.toLowerCase());
});
canonicalQueryNames.sort();
canonicalQueryNames.forEach(queryName => {
    const value = pm.request.url.query.get(queryName);

    // NOTE: This does not properly explode multiple same query params' values
    // and turn them into comma-separated list
    canonicalResourceParts.push(`${queryName}:${value}`);
});
// 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.variables.get("azure_storage_key");
const signatureBytes = CryptoJS.HmacSHA256(signatureRaw, CryptoJS.enc.Base64.parse(storageKey));
const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);

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


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

Headers in Postman: Headers and URI of Request

Does anyone have working examples or knows where to move from here?

Edit: Adding console and Error images:

Console output censored for safety

Error output censored for safety

Simon
  • 1
  • 1
  • 3
  • Can you share the complete error message and the console.log outputs. – Gaurav Mantri Feb 24 '22 at 16:07
  • Since you have the account key you can accomplish the request without signature, just use SharedKey auth: https://learn.microsoft.com/en-us/rest/api/storageservices/put-message – Peter Lillevold Feb 24 '22 at 17:31
  • Can you edit your question and include the text version of `SignatureString` and the error message? Whatever you have masked in the screenshot (like name of the account), please replace them with xxxx. – Gaurav Mantri Feb 24 '22 at 17:42

1 Answers1

0

As you said it is working well for Get And if using SAS ,it maybe permission issue .make sure to Storage-Queue-Contributor permissions on the storage queue.

There can be many reasons for this error if using SAS :

  1. Check if you have insufficient SAS Permissions. Check if you marked read, write,list permissioms .Or try generating the new SAS key .
  2. Make sure request does not include any empty headers when it is being access programmatically. If the value of a particular header is empty (or null), the header should be excluded from the request.
  3. Also if above are not the reasons , try upgrading versions of sdks 4.Do check for proper encoding ex: UTF-8.

See Azure Storage Queue via REST API c# using Shared Key Authentication - Stack Overflow

References:

  1. c# - The MAC signature found in the HTTP request ' ' is not the same as any computed signature. Server used following string to sign: 'PUT - Stack Overflow
  2. Authorize with Shared Key (REST API) - Azure Storage | Microsoft Docs
  3. rest - The MAC signature found in the HTTP request '...' is not the same as any computed signature - Stack Overflow
kavyaS
  • 8,026
  • 1
  • 7
  • 19