0

Im trying to convert the C# code found here: AMX Authorization Header in order to connect to an external API. The C# code works when trying to connect to the external API but when I convert it to a nodeJS solution it doesnt work.

I dont have access to the external C# API so can't update that side but was hoping someone could look at this and see something Im missing or doing wrong:

My nodejs solution:

var request = require('request');
var uuid = require('node-uuid');
var CryptoJS = require('crypto-js');

var URL = "https://urltoexternalAPI.com";
var itemAPPId = "testAPPId";
var APIKey = "testAPIKey";

var requestUri = encodeURIComponent(URL.toLowerCase());
var requestHttpMethod = "GET";

var requestTimeStamp = Math.floor(new Date().getTime() / 1000).toString();

var nonce = uuid.v1().replace(/-/g, '');

//I excluded the content hashing part as the API Im hitting is a GET request with no body content
var signatureRawData = itemAPPId + requestHttpMethod + requestUri + requestTimeStamp + nonce;

var secretKeyByteArray = CryptoJS.enc.Base64.parse(APIKey);

var signature = CryptoJS.enc.Utf8.parse(signatureRawData);

var signatureBytes = CryptoJS.HmacSHA256(signature, secretKeyByteArray);

var requestSignatureBase64String = signatureBytes.toString(CryptoJS.enc.Base64);

request({
  url: URL,
  headers: {
    'Authorization': "amx "+itemAPPId+":"+requestSignatureBase64String+":"+nonce+":"+requestTimeStamp
  }
}, function (error, response, body) {
  if (response.statusCode != 200) {
    console.log("Fail");
  } else {
    console.log("Success");
  }
});
2
  • 1
    Have you seen this other question: stackoverflow.com/questions/7480158 ? Commented Mar 26, 2018 at 15:41
  • @Yoryo I have tried that solution, its still coming back with a 401 unauthorised. I don't know if it has something to do with the way they are decoding it but I really dont want to have a c# handler just to run one request to an external API. I used the answer from the question you linked like so: var requestSignatureBase64String = crypto.createHmac('sha256', APIKey).update(signatureRawData).digest('hex'); Commented Mar 26, 2018 at 18:39

1 Answer 1

1

I figured it out! If anyone ever comes across this issue they may find the below helpful:

the following C# code works a little different to nodeJS: System.Web.HttpUtility.UrlEncode(request.RequestUri.AbsoluteUri.ToLower());

Initially I copied this functionality as is and wrote the nodejs equivalent as such:

 var requestUri = encodeURIComponent(URL.toLowerCase());

The encoding of the URL in C# keeps everything in lowercase - for e.g: https:// becomes https%3a%2f%2f - whereas nodeJS uppercases its encoding characters - https%3A%2F%2F - this is what as causing the incorrect hashing.

The solution is to just move the lowercase function to after the encoding has been done on the URL. Like so:

var requestUri = encodeURIComponent(URL).toLowerCase();

Seems rather simple but when trying to replicate the C# solution you may not pick up that the two URL encoders work differently.

Final solution: (updated to crypto thanks to Yoryo)

const fetch = require("node-fetch");
const uuid = require("uuid");
const crypto = require('crypto');

var URL = "https://urltoapi.com";

var itemAPPId = config.itemAPPId;
var APIKey = config.itemAPIKey;

var requestUri = encodeURIComponent(URL).toLowerCase();
var requestHttpMethod = "GET"; //should be dynamic

var requestTimeStamp = Math.floor(new Date().getTime() / 1000).toString();

var nonce = uuid.v1().replace(/-/g, '');
var signatureRawData = itemAPPId + requestHttpMethod + requestUri + requestTimeStamp + nonce;

var key = Buffer.from(APIKey, 'base64');
var requestSignatureBase64String = crypto.createHmac('sha256', key).update(signatureRawData, 'utf8').digest('base64');

const hitExternalAPI = async url => {
  try {
    const res = await fetch(url, { method: 'GET', headers: { "Authorization": "amx "+itemAPPId+":"+requestSignatureBase64String+":"+nonce+":"+requestTimeStamp } })
    .then(res => {
      console.log(res.ok);
    });
  } catch (error) {
    console.log("Error",error);
  }
};
hitExternalAPI(URL);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.