I'm trying to implement an API with HAMC authentication in Nodejs. But always I am receiving an Unauthorised (401) error.
I have followed the below instruction for API implementation:
Authentication
Users will authenticate with the web service using HMAC authentication. The following steps are necessary to authenticate with the web service:
Set the Date header on the HTTP Request to the current UTC date/time expressed in RFC 1123 format (example: Mon, 12 Jan 2015 20:50:07 GMT)
Create a “representation” of the request by concatenating the following values into a single string (in this order, with no separators):
2.1 The request method (GET or POST)
2.2 The full request URI (all lowercase) including any query string parameters
2.2.1. Example: https://estimationapi.zirmed.com/1.0/estimate/1234
2.3 The value of the Date header in “yyyy-MM-ddTHH:mm:ss” format (UTC).
2.4 The CustID.
2.5 The Base64 encoded string of the content body (your estimate in the case of a POST).
Compute an HMAC signature by hashing the UTF-8 encoded “representation” with your UTF-8 encoded private WebCallEncryptionKey using HMACSHA256 as the hashing algorithm.
Convert the resultant hash to a Base64 string. This will be the HMAC signature.
Set the Authorization header on the request by concatenating the CustID and HMAC signature separated by a colon. Specify HMACas the authorization scheme.
5.1 Example of an HMAC Authorization header: HMAC 605:pLKb9P8w5e83BMdUH4 m/EeY7O3PsbV5A89KF7IYjnM=
Below is the code I'm using...
const crypto = require("crypto");
const moment = require('moment');
const http = require('http');
let _apiKey = "API_KEY"; // API KEY/HMAC KEY
let _estimateBaseUrl = "http://estimationapi.zirmed.com/1.0";
let _estimateTransaction = "/estimate";
let CustomerID = 'CustomerID';
let BaseAddress = new URL(_estimateBaseUrl);
const utcDate = new Date().toUTCString();
let requestDt = utcDate
let RequestContent = "DATA IN JSON"
getEstimate = async (req, res) => {
let signature = await createRequestAuth(_estimateTransaction, "POST", requestDt, CustomerID, RequestContent);
let response = await getEstimateID(signature);
res.send(response);
};
createRequestAuth = async (uriPath, method, requestDate, custId, body = "") => {
let fullUri = new URL(BaseAddress uriPath);
let authHashString = await CreateAuthHashString(method, fullUri.toString().toLowerCase(), requestDate, custId, body);
let signature = crypto.createHmac('sha256', _apiKey).update(authHashString).digest("base64");
console.log("signature is === : ", signature)
return signature;
}
getEstimateID = (signature) => {
const data = new TextEncoder().encode( JSON.stringify(RequestContent) );
const options = {
hostname: 'estimationapi.zirmed.com',
port: '',
path: '/1.0/estimate',
method: 'POST',
Headers: {
Authorization: `HMAC ${CustomerID}:${signature}`,
Date: requestDt,
"Content-Type": `application/json`,
'Content-Length': data.length
}
}
console.log("options ------- ", options);
const req = http.request(options, res => {
console.log(`statusCode is: ${res.statusCode}`)
console.log(`res is: ${res}`)
res.on('data', d => { process.stdout.write(d) })
})
req.on('error', error => { console.error('error', error) })
req.write(data)
req.end()
}
CreateAuthHashString = (method, fullUri, requestDate, custId, body) => {
let uri = fullUri.toLowerCase();
let httpMethod = method;
let content = Buffer.from(utf8.encode(body)).toString('base64');
let authHashString = "".concat("", httpMethod,
uri,
moment(requestDate).format('yyyy-MM-DDTHH:mm:ss'),
custId,
content
);
console.log('authHashString: ', authHashString);
return authHashString;
}
module.exports = {
getEstimate
}
Not sure if I have done some silly mistake here but please let me know if anyone is aware of this issue.
CodePudding user response:
2.3 The value of the Date header in “yyyy-MM-ddTHH:mm:ss” format (UTC)
When you use moment(requestDate).format('yyyy-MM-DDTHH:mm:ss')
, the formatted value is in local timezone. To format it in UTC, you need to do it as moment(requestDate).utc().format('yyyy-MM-DDTHH:mm:ss')
.