Sending message to Azure Service Bus from POSTMAN nodeJs - node.js

I have been trying to send a sample message to Azure Service Bus Topic from POSTMAN, But it came with an unusual error. Here my doubt is, I am generating the SAS token using NodeJs own 'Buffer' rather than 'utf8' module. Can that be a reason Why I am getting 'The specified HTTP verb (POST) is not valid.'. Here is my snippet to generate the SAS token.
*function createSharedAccessToken(uri, saName, saKey) {
if (!uri || !saName || !saKey) {
throw "Missing required parameter";
}
console.log('inside the function')
var encoded = encodeURIComponent(uri);
var now = new Date();
var week = 60 * 60 * 24 * 7;
var ttl = Math.round(now.getTime() / 1000) + week;
var signature = encoded + '\n' + ttl;
var signatureUTF8 = Buffer.from(signature, 'utf8');
var hash = crypto.createHmac('sha256', saKey).update(signatureUTF8).digest('base64');
return 'SharedAccessSignature sr=' + encoded + '&sig=' +
encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName;
}*
Its a POST call with Authorization and Content-Type header.

Please add 'messages' at the end of the url:
http{s}://{serviceNamespace}.servicebus.windows.net/{queuePath|topicPath}/messages
->
https://mynamespace.servicebus.windows.net/my-topic-name/messages
Send Message

Related

Amazon Selling Partner API 403 - Signature Error

I'm trying to make a request to the Amazon Selling Partner API (node.js/Lambda) but I currently always get a 403 error back. I have plugged the same credentials and access token into Postman and the request works fine. I believe that there must be an error when I'm calculating the signature for the request, but I can't see anything wrong.
I'm calculating the signature as follows:
function constructCanonicalRequest(accessToken, dateTime) {
let canonical = [];
canonical.push('GET');
canonical.push('/fba/outbound/2020-07-01/fulfillmentOrders/FBATestOrder-1');
canonical.push('');
canonical.push('host:' + 'sandbox.sellingpartnerapi-na.amazon.com');
canonical.push('x-amz-access-token:' + accessToken);
canonical.push('x-amz-date:' + dateTime);
canonical.push('');
canonical.push('host;x-amz-access-token;x-amz-date');
canonical.push(crypto.SHA256(''));
let canonicalRequest = canonical.join('\n');
let canonicalRequestHash = crypto.SHA256(canonicalRequest);
return canonicalRequestHash
};
function constructStringToSign(dateTime, date, canonicalRequestHash) {
let stringToSign = [];
stringToSign.push('AWS4-HMAC-SHA256')
stringToSign.push(dateTime);
stringToSign.push(date + '/' + 'us-east-1' + '/' + 'execute-api' + '/aws4_request');
stringToSign.push(canonicalRequestHash);
return stringToSign.join('\n');
};
function constructSignature(date, iamSecret, stringToSign) {
let kDate = crypto.HmacSHA256(date, 'AWS4' + iamSecret);
let kRegion = crypto.HmacSHA256('us-east-1', kDate);
let kService = crypto.HmacSHA256('execute-api', kRegion);
let kSigning = crypto.HmacSHA256('aws4_request', kService);
let signature = crypto.HmacSHA256(stringToSign, kSigning).toString(crypto.enc.Hex);
return signature
};
The rest of the function is:
let dateTimeISO = new Date().toISOString();
let dateTime = dateTimeISO.replace(/(\.\d{3})|\W/g,'');
let date = dateTime.split('T')[0];
let canonicalRequestHash = constructCanonicalRequest(accessToken, dateTimeISO);
let stringToSign = constructStringToSign(dateTime, date, canonicalRequestHash);
let signature = constructSignature(date, iamSecret, stringToSign);
let authHeader = 'AWS4-HMAC-SHA256 Credential=' + iamId + '/' + date + '/' + 'us-east-1' + '/execute-api/aws4_request, SignedHeaders=host;x-amz-access-token;x-amz-date, Signature=' + signature
console.log(authHeader);
let amazonUrl = "https://sandbox.sellingpartnerapi-na.amazon.com/fba/outbound/2020-07-01/fulfillmentOrders/FBATestOrder-1";
const amazonResponse = await fetch(amazonUrl, {
method: 'get',
headers: {
'Authorization':authHeader,
'Content-Type':'application/json; charset=utf-8',
'host':'sandbox.sellingpartnerapi-na.amazon.com',
'x-amz-access-token':accessToken,
'user-agent': 'My Selling Tool/2.0 (Language=JS;Platform=Node)',
'x-amz-date':dateTime,
}
});
I have also tried using multiple difference crypto libraries to see if the HMAC creation is the problem, but this hasn't fixed anything.
I have my access working in C#. There is a Java library available on GitHub:
https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/developer-guide/SellingPartnerApiDeveloperGuide.md
I just followed the instructions there, though there is a deficiency in the C# lib about assume role, that is in the java lib.

Azure Table Storage (Table Services REST API) SharedKeyLite not working

After seeing through this link, I tried the same in my postman.
var storageAccount = "mystorage";
var accountKey = "<<primaryKey>>";
var date = new Date();
var UTCstring = date.toUTCString();
var data = UTCstring + "\n" + "/mystorage/Health"
var encodedData = unescape(encodeURIComponent(data));
var hash = CryptoJS.HmacSHA256(encodedData, CryptoJS.enc.Base64.parse(accountKey));
var signature = hash.toString(CryptoJS.enc.Base64);
var auth = "SharedKeyLite " + storageAccount + ":" + signature;
postman.setEnvironmentVariable("auth", auth);
postman.setEnvironmentVariable("date", UTCstring);
When I make the request to ATS, to the following url,
I get the auth denied!
Can someone please guide me what's going wrong here?!
I think you need to generate a bearer token and put it to the Authorization of Postman.
If you are using C#, you can use this to get the bearer token:
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
string accesstoken = azureServiceTokenProvider.GetAccessTokenAsync("https://storage.azure.com/").Result;
string bearertoken = "Bearer " + accesstoken;
Then Copy the bearer token:
After that, it should be ok.
Just realized that the url and the data that we are encoding should exactly match the url we are querying...
After changing
var data = UTCstring + "\n" + "/mystorage/Health"
to
var data = UTCstring + "\n" + "/mystorage/Health(PartitionKey='USA',RowKey='WA')"
things started working.
Update :
It just expects the right table query. The following works fine,
var data = UTCstring + "\n" + "/mystorage/Health()"
with all filter expressions in the url being invoked from postman.

How do I form an SAS token for Microsoft Azure API Management's REST API in Node.js?

I am using Microsoft Azure API Management service and want to use the REST API service. In creating my SAS token, which is needed otherwise the API call doesn't authorize, I'm having difficulty forming a proper token. Microsoft's webpage about this SAS token for API Management only shows an example in C#. I want to know how to form an SAS token in Node.js, which is not shown. Below is my code that was working last week, but is not now for some unknown reason. The error I get is: 401 Authorization error, token invalid
If someone can help me formulate this token, I would appreciate it.
This is Microsoft's webpage regarding this authentication token: https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication
Here's my code:
const crypto = require('crypto');
const util = require('util');
const sign = () => {
const id = ${process.env.id}
const key = `${process.env.SASKey}`;
const date = new Date();
const newDate = new Date(date.setTime(date.getTime() + 8 * 86400000));
const expiry = `${newDate.getFullYear()}${
newDate.getMonth() < 10
? '' + newDate.getMonth() + 1
: newDate.getMonth() + 1
}${newDate.getDate()}${newDate.getHours()}${
newDate.getMinutes() < 10
? '0' + newDate.getMinutes()
: newDate.getMinutes()
}`;
const dataToSignString = '%s\n%s';
const dataToSign = util.format(dataToSignString, ${id}, expiry);
const hash = crypto
.createHmac('sha512', key)
.update(dataToSign)
.digest('base64');
const encodedToken = `SharedAccessSignature ${id}&${expiry}&${hash}`;
console.log(encodedToken);
return encodedToken;
};
Try the code:
protected getAPIManagementSAS(){
let utf8 = require("utf8")
let crypto= require("crypto")
let identifier = process.env.API_IDENTIFIER;
let key = process.env.API_KEY;
var now = new Date;
var utcDate = new Date(now.getUTCFullYear(),now.getUTCMonth(), now.getUTCDate() , now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
let expiry = addMinutes(utcDate,1,"yyyy-MM-ddThh:mm:ss") + '.0000000Z'
var dataToSign = identifier + "\n" + expiry;
var signatureUTF8 = utf8.encode(key);
var signature = crypto.createHmac('sha512', signatureUTF8).update(dataToSign).digest('base64');
var encodedToken = `SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}`;
return encodedToken
}
For more information, see here.
After a million tries, it seems like the only format acceptable right now is:
SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}
If you are using the other format that has the "integration" parameter, that's a hit or a miss, mostly miss though. Set the uid as "integration" if that's your identifier and follow the above format as it works.

How to create ConsumerGroups for Eventhub programatically using nodejs in Azure?

How to create consumergroups in eventhub using nodejs in Azure?
I tried to replicate what .net SDK offers, it did not work.
const { NamespaceManager } = require("#azure/service-bus");
let namespaceManager = NamespaceManager.CreateFromConnectionString(eventHubConnectionString);
let ehd = namespaceManager.GetEventHub(eventHubPath);
namespaceManager.CreateConsumerGroupIfNotExists(ehd.Path, consumerGroupName);
Here is the process that worked:
https://learn.microsoft.com/en-us/rest/api/eventhub/create-consumer-group
Steps:
Create SAS Token
Supply the right headers and make a https call to REST api
You can create it only once, if you call for the second time, it will throw an 409 error. If you want an update or insert call, you need to check for it.
SAS Token:
https://learn.microsoft.com/en-us/rest/api/eventhub/generate-sas-token
uri -- url of your eventhub
saName -- Name of your Managed Policy
saKey -- Primary / Secondary Key of your EventHub Manage Policy (Ensure it has the Manage)
function createSharedAccessToken(uri, saName, saKey) {
if (!uri || !saName || !saKey) {
throw "Missing required parameter";
}
var encoded = encodeURIComponent(uri);
var now = new Date();
var week = 60*60*24*7;
var ttl = Math.round(now.getTime() / 1000) + week;
var signature = encoded + '\n' + ttl;
var signatureUTF8 = utf8.encode(signature);
var hash = crypto.createHmac('sha256', saKey).update(signatureUTF8).digest('base64');
return 'SharedAccessSignature sr=' + encoded + '&sig=' +
encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName;
}
Request Parameters:
URL:
https://your-namespace.servicebus.windows.net/your-event-hub/consumergroups/testCG?timeout=60&api-version=2014-01
Headers:
Content-Type: application/atom+xml;type=entry;charset=utf-8
Host: your-namespace.servicebus.windows.net
Authorization: {replace with the content from your SAS Token}
Payload:
<entry xmlns="http://www.w3.org/2005/Atom">
<content type="application/xml">
<ConsumerGroupDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">Any name you want</ConsumerGroupDescription>
</content>
</entry>
Possible Return Statuses:
201 -- Successful Creation
404 -- Not found, you are using a name that does not exist
409 -- The messaging entity 'XXX' already exists.
If you notice any other issues, please leave a comment.
Event hubs Node.JS SDK doesn't support management operations.
Try a management client like https://www.nuget.org/packages/Microsoft.Azure.Management.EventHub/

DocumentDB REST API - Authorization Token Error

Problem
We are seeing this error returned from the DocumentDB REST API whenever we request a list or query, but not when we fetch objects by name/id:
The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used.
Background
We have been successfully using the node.js sdk with DocumentDB for over a year now, but as we want to migrate our back-end restful API code from a node.js App Service to Azure Functions we are seeing 10-30 second lag times come into play as the DocumentDB sdk loads slowly when the Function hasn't been called in a while. We know that the Function instance is hot, and this isn't a cold instance issue based on previous communication with the Azure Functions team.
To work around this we want to test the DocumentDB REST API which requires zero external libraries to run in a node.js Function and should execute as quickly as possible.
Code
This is the test harness running in local node.js. We'll move this to an Azure Function once it's working.
var express = require('express');
var router = express.Router();
var crypto = require("crypto");
var request = require('request');
router.get('/', function (req, res, next) {
var key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
var uri = "https://xxxxxx.documents.azure.com";
var verb = 'GET';
var type = 'dbs';
var link = 'dbs';
var url = `${uri}/${link}`;
var headers = getDefaultRequestHeaders();
// var body = `{"query":"SELECT * FROM c", "parameters": []}`;
var body = '';
headers['content-length'] = body.length;
headers['authorization'] = getAuthorizationTokenUsingMasterKey(verb, type, link, headers['x-ms-date'], key);
request[verb.toLowerCase()]({"url": url, "headers": headers, "body": body}, function (error, response, body) {
// console.log(`error is ${error}`);
// console.log(`response is ${JSON.stringify(response, null, 2)}`);
console.log(`body is ${body}`);
res.status(response.statusCode).json(body);
});
});
function getDefaultRequestHeaders(isQuery, date) {
var headers = {
"content-type": "application/json",
"x-ms-date": new Date().toUTCString(),
"x-ms-version": "2017-02-22",
"accept": "application/json",
"cache-control": "no-cache",
"user-agent": "xxxxxx/1.0"
};
if(isQuery) {
headers["x-ms-documentdb-isquery"] = true;
headers["content-type"] = "application/query+json";
}
if(date) {
headers["x-ms-date"] = date;
}
return headers;
}
function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceLink, date, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceLink || "") + "\n" +
date.toLowerCase() + "\n" +
"" + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
module.exports = router;
We are using the getAuthorizationTokenFromMasterKey function verbatim from the Access control in the DocumentDB API page.
The key, app name, and user-agent have been replaced with x's for privacy/security.
Test Results
List Databases
When I try the most basic call to list dbs the server returns the token error:
var verb = 'GET';
var type = 'dbs';
var link = 'dbs';
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 12 aug 2017 12:28:41 gmt\n\n'\r\nActivityId: acbf19d9-6485-45c5-9c30-6aa21f14d5b3\"}"
Get Database
However, when I perform the get database request it works fine:
var verb = 'GET';
var type = 'dbs';
var link = 'dbs/00001';
Response:
"{\"id\":\"00001\",\"_rid\":\"0eUiAA==\",\"_ts\":1441256154,\"_self\":\"dbs\/0eUiAA==\/\",\"_etag\":\"\\"00007d4a-0000-0000-0000-55e7d2da0000\\"\",\"_colls\":\"colls\/\",\"_users\":\"users\/\"}"
List Collections
Similarly, requesting the list of collections from this database returns a token error:
var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls';
Respose:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ncolls\ndbs/00001\nsat, 12 aug 2017 12:32:19 gmt\n\n'\r\nActivityId: 8a9d4ff8-24ef-4fd2-b400-f9f8aa743572\"}"
Get Collection
But when I call get collection I get a valid response:
var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls/00001';
Response:
"{\"id\":\"00001\",\"indexingPolicy\":{\"indexingMode\":\"consistent\",\"automatic\":true,\"includedPaths\":[{\"path\":\"\/*\",\"indexes\":[{\"kind\":\"Range\",\"dataType\":\"Number\",\"precision\":-1},{\"kind\":\"Range\",\"dataType\":\"String\",\"precision\":-1},{\"kind\":\"Spatial\",\"dataType\":\"Point\"}]}],\"excludedPaths\":[]},\"_rid\":\"0eUiAJMAdQA=\",\"_ts\":1454200014,\"_self\":\"dbs\/0eUiAA==\/colls\/0eUiAJMAdQA=\/\",\"_etag\":\"\\"00000100-0000-0000-0000-56ad54ce0000\\"\",\"_docs\":\"docs\/\",\"_sprocs\":\"sprocs\/\",\"_triggers\":\"triggers\/\",\"_udfs\":\"udfs\/\",\"_conflicts\":\"conflicts\/\"}"
List Documents
Requesting list documents on that collection gives me this error:
var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndocs\ndbs/00001/colls/00001\nsat, 12 aug 2017 12:34:48 gmt\n\n'\r\nActivityId: 57097e95-c41b-4770-b91a-370418ef2cce\"}"
Get Document
Not surprisingly, fetching a single document works fine:
var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs/e7fe638d-2152-2097-f9c6-9801d7cf5cdd';
Response:
"{\"name\":\"test rest api\",\"id\":\"e7fe638d-2152-2097-f9c6-9801d7cf5cdd\",\"_rid\":\"0eUiAJMAdQCbHgAAAAAAAA==\",\"_self\":\"dbs\/0eUiAA==\/colls\/0eUiAJMAdQA=\/docs\/0eUiAJMAdQCbHgAAAAAAAA==\/\",\"_etag\":\"\\"0d00d1ee-0000-0000-0000-598ef7d40000\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1502541779}"
Query Documents
Finally, sending a query also results in a token error:
var verb = 'POST';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
var body = `{"query":"SELECT * FROM c", "parameters": []}`;
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'post\ndocs\ndbs/00001/colls/00001\nsat, 12 aug 2017 12:35:42 gmt\n\n'\r\nActivityId: b8b95f8c-1339-423e-b0e7-0d15d3056180\"}"
I believe the documentation is incorrect. Where they say resourceLink, they should actually say resource id. If you look at the Node SDK code, this is how they are calculating the authorization header (notice the use of resourceId):
getAuthorizationTokenUsingMasterKey: function (verb, resourceId, resourceType, headers, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceId || "") + "\n" +
(headers["x-ms-date"] || "").toLowerCase() + "\n" +
(headers["date"] || "").toLowerCase() + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return "type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature;
},
So if you want to list the databases, because there is no resource id you will need to use an empty string for your link variable. Similarly, if you want to list collections in the database, the link should actually be the id of the database (e.g. dbs/00001 and not dbs/00001/colls).
I was getting the same issue. For querying documents I was getting authorization token error. It was due to wrong ResourceId/ResourceLink
var verb = 'POST';
var type = 'docs';
var link = 'dbs/{db-id}/colls/{coll-id}/docs';
var url = `${uri}/${link}`;
var resourceLink = "dbs/{db-id}/colls/{coll-id}"
getAuthorizationTokenUsingMasterKey(verb, type, resourceLink, headers['x-ms-date'], key)
the only correction is required from the given question data is to change the appropriate resourceLink while generating AuthorizationToken. For querying Documents the resourceLink is <dbs/{db-id}/colls/{coll-id}> instead of <dbs/{db-id}/colls/{coll-id}/docs>
I want to provide another thing to consider in addressing this issue. In my case I had to add this header: x-ms-documentdb-query-enablecrosspartition: true, because I created my container with a partitionKey.
I also want to confirm how my main parameters were setup to calculate the authorization header for querying over Documents:
resourceType: docs
resourceLink: dbs/<databaseId>/colls/<containerId>
I had thought, up to this point, that the resourceLInk had to match with the request URL but this is showing me I was wrong. Similarly notice that the resourceType is not present in the resourceLink.
POST /dbs/<databaseId>/colls/<containerId>/docs HTTP/1.1
accept: application/json
x-ms-documentdb-isquery: true
x-ms-version: 2018-12-31
authorization: type%3Dmaster%26ver%3D1.0%26sig%***********************
x-ms-date: Sat, 03 Apr 2021 22:34:24 GMT
x-ms-documentdb-query-enablecrosspartition: true
x-correlation-id: be1b1fe1-94cc-11eb-a0a4-38f9d3924940
Host: <host>.documents.azure.com
User-Agent: AHC/1.0
Connection: keep-alive
Content-Type: application/query+json
Content-Length: 72
{
"query": "SELECT * FROM <containerId>",
"parameters": [
]
}
I got the same error while making an update to document DB but in my case I realized I was using the Read-Only keys. After changing connection string to use it to Read-Write Keys, I was able to update the records.

Resources