Binance Signature for this request is not valid error - node.js

I'm trying to make this work
Here's the error I get:
data: { code: -1022, msg: 'Signature for this request is not valid.' }
Functions
const crypto = require('crypto')
export async function signature (query_string) {
return crypto.createHmac('sha256', apiSecret).update(query_string).digest('hex')
}
export async function order (symbol, quantity, price) {
const query = '?symbol=' + symbol + '&isIsolated=TRUE&side=BUY&quantity=' + quantity + '&price=' + price + '&type=LIMIT&sideEffectType=MARGIN_BUY&recvWindow=5000&timestamp=' + Date.now()
const querySigned = await signature(query)
const sendingQuery = query + '&signature=' + querySigned
const queryConfig = {
method: 'POST',
url: 'https://api.binance.com/sapi/v1/margin/order/' + sendingQuery,
headers: {
'X-MBX-APIKEY': apiKey,
},
}
const response = await axios(queryConfig)
return response.data
}

Don't include the question mark in the input for signature.
const query = 'symbol=' + symbol + // removed the question mark
const queryConfig = {
method: 'POST',
url: 'https://api.binance.com/sapi/v1/margin/order/?' + sendingQuery, // added the question mark
headers: {
'X-MBX-APIKEY': apiKey,
},
}
Source: I looked up my old code from a year ago that was also using the Binance API and found a difference in the question mark (not) passed as the signature input.
I used it for a spot order but I'll still include it. Maybe it will help you debug further if this is not a correct solution.
let query = 'recvWindow=5000&timestamp='+(new Date().getTime()) + "&symbol=" + baseAsset + quoteAsset + "&side=" + side + "&quantity=" + baseAmount + "&type=market";
const signature = crypto.createHmac('sha256', apiSecret).update(query).digest("hex");
query += '&signature='+signature;
const result = await axios({
'method': 'POST',
'url': 'https://api.binance.com/api/v3/order?'+query,
'headers': {
'X-MBX-APIKEY': apiKey
}
});

Related

How to authorize with AWS signature 4 -> API Gateway -> Lambda

I've googled around a lot with no luck in finding the solution to my problem. I've read through the entire authentication process for AWS Signature 4 and followed their tutorial as well as view other sources. I'm trying to have client side authentication for a desktop application that makes request to API Gateway.
When I use Postman it works properly but I tried generating my own signature in Nodejs but to no avail, I keep getting 403 messages back from the call.
The function below returns the authenticated requestUrl which is then run by axios.get(requestUrl). When I use the Postman generated request it works perfectly fine but, once I use my generated request I have problems.
Am I missing something while authenticating? Here is what my code currently looks like:
function Authorize() {
const host = "EXAMPLE.execute-api.us-east-1.amazonaws.com"
const reg = 'us-east-1'
const meth = 'GET'
const serv = 'execute-api'
const endpoint = '/development/putImage'
// Keys
let access = "EXAMPLE"
let key = "KEY"
// Get Date
let t = new Date();
let amzDate = t.toJSON().replace(/[-:]/g, "").replace(/\.[0-9]*/, "");
let dateStamp = t.toJSON().replace(/-/g, "").replace(/T.*/, "");
// ************* TASK 1: CREATE CANONICAL REQUEST *************
// Create Canonical Request
let canonical_uri=endpoint
let canonical_headers="host: "+host+"\n"
let signedHeaders = 'host'
let algorithm = 'AWS4-HMAC-SHA256'
let credentialScope = dateStamp + "/" + reg + "/" + serv + "/" + "aws4_request"
// Set query string
let canonicalQueryString = ""
canonicalQueryString += "X-Amz-Date=" + amzDate
canonicalQueryString += "&X-Amz-Algorithm=" + algorithm;
canonicalQueryString += "&X-Amz-Credential=" + encodeURIComponent(access + "/" + credentialScope)
canonicalQueryString += "&X-Amz-SignedHeaders=" + signedHeaders
// Empty payload for get request
var payloadHash = crypto.createHash('sha256').update('').digest('hex');
// Set canonical request
var canonicalRequest = meth + "\n" + canonical_uri + "\n" + canonicalQueryString + "\n" + canonical_headers + "\n" + signedHeaders + "\n" + payloadHash
console.log(canonicalRequest)
// ************* TASK 2: CREATE THE STRING TO SIGN*************
let stringToSign = algorithm + '\n' + amzDate + '\n' + credentialScope + '\n' + crypto.createHash('sha256').update(canonicalRequest).digest('hex');
// ************* TASK 3: CALCULATE THE SIGNATURE *************
var signingKey = getSignatureKey(key, dateStamp, reg, serv)
var signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
// ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
canonicalQueryString += '&X-Amz-Signature=' + signature
let requestUrl = "https://"+host+ endpoint + "?" + canonicalQueryString
console.log(requestUrl)
return requestUrl
}
The below code worked for me well. For more info, please visit https://docs.aws.amazon.com/opensearch-service/latest/developerguide/request-signing.html#request-signing-node
const { HttpRequest} = require("#aws-sdk/protocol-http");
const { defaultProvider } = require("#aws-sdk/credential-provider-node");
const { SignatureV4 } = require("#aws-sdk/signature-v4");
const { NodeHttpHandler } = require("#aws-sdk/node-http-handler");
const { Sha256 } = require("#aws-crypto/sha256-browser");
...
var request = new HttpRequest({
body: JSON.stringify({"users":["G0000000B","G0000000A"]}),
headers: {
'Content-Type': 'application/json',
'apiKey':'XXXXXXXXXXXX',
'apiSecret': 'XXXXXXXXXXXXXXXXXX',
'host': 'service2.xxx.xxx.xx'
},
hostname: 'service2.xxx.xxx.xx',
method: 'POST',
path: 'API/user/list'
});
var signer = new SignatureV4({
credentials: defaultProvider(),
region: 'ap-southeast-1',
service: 'execute-api',
sha256: Sha256
});
const signedRequest = await signer.sign(request);
// Send the request
var client = new NodeHttpHandler();
var { response } = await client.handle(signedRequest)
console.log(response.statusCode + ' ' + response.body.statusMessage);
var responseBody = '';
await new Promise(() => {
response.body.on('data', (chunk) => {
responseBody += chunk;
});
response.body.on('end', () => {
console.log('Response body: ' + responseBody);
});
}).catch((error) => {
console.log('Error: ' + error);
});

Here Map API request token with NodeJS

I am trying to request a token to the Here Map API with NodeJS in order to obtain OAuth 2.0 Token Credentials.
I am blocked a the request level and constantly having the same error but according to the documentation I don't do anything wrong.
Here is the necessary code in NodeJS to make the request :
const keyID = "MyKeyID";
const keySecret = "MySecretKey";
const httpMethod = "POST";
const httpUrl = "https://account.api.here.com/oauth2/token";
let oauthNonce = Math.random().toString(36).substring(6);
let oauthTimestamp = Date.now();
let oauthSignatureMethod = "HMAC-SHA256";
let oauthVersion = "1.0";
let baseString = httpMethod + "&" + httpUrl;
let oauth1Param = [];
oauth1Param.oauth_consumer_key = keyID;
oauth1Param.oauth_signature_method = oauthSignatureMethod;
oauth1Param.oauth_signature = "";
oauth1Param.oauth_timestamp = oauthTimestamp;
oauth1Param.oauth_nonce = oauthNonce;
oauth1Param.oauth_version = oauthVersion;
let paramString = "grant_type=client_credentials"
+ "&oauth_consumer_key=" + oauth1Param.oauth_consumer_key
+ "&oauth_nonce=" + oauth1Param.oauth_nonce
+ '&oauth_signature_method=' + oauth1Param.oauth_signature_method
+ "&oauth_timestamp=" + oauth1Param.oauth_timestamp
+ "&oauth_version=" + oauth1Param.oauth_version;
baseString += "&" + paramString;
let signingKey = keySecret + "&";
let signature = crypto.createHmac('SHA256', signingKey).update(baseString).digest('base64');
oauth1Param.oauth_signature = signature;
console.log(JSON.stringify(paramString));
console.log(oauth1Param);
let headerOauth = "OAuth ";
for (let key in oauth1Param) {
headerOauth += key + "=" + oauth1Param[key] + ",";
}
headerOauth = headerOauth.slice(0, -1);
console.log(headerOauth);
var request = require('request');
request.post({
url : httpUrl,
headers: {
'content-type' : 'application/x-www-form-urlencoded',
'Authorization' : headerOauth
},
body : JSON.stringify({
"grant_type" : "client_credentials"
})
}, function(error, response, body){
if(error){
console.log("------ ERROR -------");
throw error;
}
//console.log(response);
console.log(body);
});
And this is the error that I keep getting :
{"errorId":"ERROR-51fa3a57-1dfa-4da7-b2e9-5f94574a4f75",
"httpStatus":401,
"errorCode":401205,
"message":"Unsupported signature method in the header. Require HMAC-SHA256",
"error":"invalid_request",
"error_description":"errorCode: '401205'. Unsupported signature method in the header. Require HMAC-SHA256"}
According to the documentation, https://developer.here.com/documentation/authentication/dev_guide/topics/error-messages.html, I have an error with the signature method and that it's required to be HMAC-SHA256 but actually this is what I have put at the beginning of my code : let oauthSignatureMethod = "HMAC-SHA256";
Does anyone know how to fix this issue?
The library you're using, request, supports OAuth signing. Therefore the code can be significantly shorter:
const request = require('request');
const OAUTH_URL = 'https://account.api.here.com/oauth2/token';
const KEY_ID = '<KEY_ID>';
const KEY_SECRET = '<KEY_SECRET>';
request.post({
url: OAUTH_URL,
oauth: {
consumer_key: KEY_ID,
consumer_secret: KEY_SECRET,
signature_method: 'HMAC-SHA256'
},
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
},
form: {
'grant_type': 'client_credentials'
}
}, function (e, r, body) {
console.log(body);
});
As for your code snippet, JSON.stringify(obj) is not appropriate as it will encode the curly bracket of the object as well.
Additionally, the values in the OAuth header need to be surrounded by double quotes. It's easy to get little things wrong with OAuth :)

Angular2 + Express.js: Return CSV and offer downloadable file?

I have this code in my ng2 component:
data => {
let blob = new Blob([data], { type: "text/csv" });
FileSaver.saveAs(blob, "data.csv);
}
Then there is a file saved, but the contents of the file read:
Response with status: 200 OK for URL: https://MYURL/URI/data.csv
My backend is node/express, do I have to create a stream for this or does it work with direct file access like in the old days?
This is my backend at the moment, how can I alter it to return a stream?
'use strict';
const Transactions = require('../../models').transaction;
module.exports = function * details(req, res) {
logger.debug(' Get TME details ' + req.currentTME._id);
const transaction = yield Transactions.findOne({
_tme: req.currentTME.id
}).lean();
return res
.json({
status: constants.status.SUCCESS,
timemodel: req.currentTME,
linkedInfo: {
transaction
}
});
};
Full Server Call:
request(url: string, data: any, method: RequestMethod, showErrorDialog = true): Observable<any> {
const requestOptionsArgs: RequestOptionsArgs = {
url: this.baseUrl + url,
method: method,
headers: this.createHeaders(),
body: JSON.stringify(data)
};
this.log.info(this.getCurl(requestOptionsArgs), this);
const requestOptions: RequestOptions = new RequestOptions(requestOptionsArgs);
return this.intercept(
this.http.request(new Request(requestOptions)).timeout(3000),
requestOptionsArgs,
showErrorDialog
).share();
}

Signature error in Amazon MWS API

I am trying to hit SubmitFeed API of Amazon MWS API but whenever I try to make a HTTP call to the API it gives me an error from server saying "Signature Mismatch".
Note: First I tried to hit the API without Content MD5 value and the file .
var AWSAccessKeyId = "<AWSAccessKeyId>",
Action = "SubmitFeed",
MarketplaceId = "<MarketplaceId>",
SellerId = "<SellerId>",
SignatureMethod = "HmacSHA256",
SignatureVersion = "2",
Timestamp = new Date().toISOString(),
Version = "2009-01-01",
FeedType = "_POST_INVENTORY_AVAILABILITY_DATA_",
ContentMD5Value = "",
Signature = "",
SecretKey = "<SecretKey>";
var queryString = "AWSAccessKeyId=" + AWSAccessKeyId + "&Action=" + Action + "&ContentMD5Value=" + ContentMD5Value + "&FeedType=" + FeedType + "&MarketplaceIdList.Id.1=" + MarketplaceId + "&Merchant=" + SellerId + "&PurgeAndReplace=false" + "&SignatureMethod=" + SignatureMethod + "&SignatureVersion=2" + "&Timestamp=" + Timestamp + "&Version=" + Version;
var StringToSign = "POST" + "\n" + "mws.amazonservices.com" + "\n" + "/" + "\n" + queryString;
Signature = crypto.createHmac('sha256', SecretKey).update(StringToSign).digest('base64');
request({
url: 'https://mws.amazonservices.com/Feeds/2009-01-01/',
qs: {
AWSAccessKeyId: AWSAccessKeyId,
Action: Action,
FeedType: FeedType,
'MarketplaceIdList.Id.1': MarketplaceId,
Merchant: SellerId,
PurgeAndReplace: false,
SignatureMethod: SignatureMethod,
SignatureVersion: 2,
Timestamp: Timestamp1,
Version: Version,
Signature: Signature
},
method: 'POST',
headers: {
'Content-Type': 'text/txt; charset=iso-8859-1',
'x-amazon-user-agent': 'DIApp/1.0 (Language=Javascript)'
}
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
console.log('-----------Status Code-------------');
console.log(response.statusCode);
console.log('-----------Status Code-------------');
console.log('-----------BODY-------------');
console.log(body);
console.log('-----------BODY-------------');
}
});

How to access the variable data outside of a Node.js function?

This is a function in Node.js, which reads data from Analytics:
function getDataFromGA(Dimension, Metric, StartDate, EndDate, MaxResults) {
var fs = require('fs'),
crypto = require('crypto'),
request = require('request'); // This is an external module
var authHeader = {
'alg': 'RS256',
'typ': 'JWT'
},
authClaimSet = {
'iss': '***t#developer.gserviceaccount.com', // Service account email
'scope': 'https://www.googleapis.com/auth/analytics.readonly',
// We MUST tell them we just want to read data
'aud': 'https://accounts.google.com/o/oauth2/token'
},
SIGNATURE_ALGORITHM = '**',
SIGNATURE_ENCODE_METHOD = '**',
GA_KEY_PATH = '**',
//finds current directory then appends private key to the directory
gaKey;
function urlEscape(source) {
return source.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
}
function base64Encode(obj) {
var encoded = new Buffer(JSON.stringify(obj), 'utf8').toString('base64');
return urlEscape(encoded);
}
function readPrivateKey() {
if (!gaKey) {
gaKey = fs.readFileSync(GA_KEY_PATH, 'utf8');
}
return gaKey;
}
var authorize = function (callback) {
var self = this,
now = parseInt(Date.now() / 1000, 10), // Google wants us to use seconds
cipher,
signatureInput,
signatureKey = readPrivateKey(),
signature,
jwt;
// Setup time values
authClaimSet.iat = now;
authClaimSet.exp = now + 60; // Token valid for one minute
// Setup JWT source
signatureInput = base64Encode(authHeader) + '.' + base64Encode(authClaimSet);
// Generate JWT
cipher = crypto.createSign('RSA-SHA256');
cipher.update(signatureInput);
signature = cipher.sign(signatureKey, 'base64');
jwt = signatureInput + '.' + urlEscape(signature);
// Send request to authorize this application
request({
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: 'https://accounts.google.com/o/oauth2/token',
body: 'grant_type=' + escape('urn:ietf:params:oauth:grant-type:jwt-bearer') +
'&assertion=' + jwt
}, function (error, response, body) {
if (error) {
console.log(error);
callback(new Error(error));
} else {
var gaResult = JSON.parse(body);
if (gaResult.error) {
callback(new Error(gaResult.error));
} else {
callback(null, gaResult.access_token);
// console.log(gaResult);
console.log("Authorized");
}
}
});
};
var request = require('request'),
qs = require('querystring');
authorize(function (err, token) {
if (!err) {
// Query the number of total visits for a month
var requestConfig = {
'ids': 'ga:72333024',
'dimensions': Dimension,
'metrics': Metric,
// 'sort': '-ga:users',
'start-date': StartDate,
'end-date': EndDate,
'max-results': MaxResults
};
request({
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token // Here is where we use the auth token
},
uri: 'https://www.googleapis.com/analytics/v3/data/ga?' + qs.stringify(requestConfig)
}, function (error, resp, body) {
console.log(body);
var data = JSON.parse(body);
console.log(data.totalsForAllResults);
console.log(data.rows);
});
}
});
}
Here I try to access it from outside:
var gaJSON = utils.getDataFromGA("ga:country", "ga:pageviews", "2011-08-04", "2014-09-12", "50");
res.send(gaJSON);
My question is how I can access the variable data in the end of the first method? How can I call it from outside of the function?
You can assign data to a variable declared in the first function. But since the authorize method is asynchronous the variable data will still be undefined at the end of the first function. The best way to do this is handle with callbacks.
I think you wanna return something related to this variable, right? Try to put a callback parameter to the first function and then call this function passing the result.
callback(variable)
Why do you want to access if from outside. ??
Even though you want to desperately then you need to create a function pass the "data" as argument and then invoke the function .
console.log(body);
var data = JSON.parse(body);
myNewFunction(data);
Write all ur logic inside "myNewFunction" that uses data .

Resources