I am trying to connect to Cryptopia API to get my balance in Google Sheet and I can't make it work. The error I am currently getting is:
Signature does not match request parameters
API Private Documentation
My code:
var key1 = "API_KEY";
var secret1 = "API_SECRET";
var nonce = Math.floor(new Date().getTime()/1000);
var params = {"Currency" : "BTC"};
var url = 'https://www.cryptopia.co.nz/api/GetBalance';
var requestContentBase64String = Utilities.base64Encode(Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, JSON.stringify(params), Utilities.Charset.UTF_8)); // Added
var signature = key1 + "POST" + encodeURIComponent(url).toLowerCase() + nonce + requestContentBase64String; // Modified
var hmacsignature = Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_256, signature, Utilities.base64Decode(secret1), Utilities.Charset.UTF_8)); // Modified
var header_value = "amx " + key1 + ":" + hmacsignature + ":" + nonce;
var headers = {
"Authorization": header_value,
"Content-Type": 'application/json; charset=utf-8'
//"Content-Length" : Utilities.newBlob(JSON.stringify(params)).getBytes().length // Added
};
var options = {
"method": 'POST',
"headers": headers,
"payload": JSON.stringify(params),
"contentLength": JSON.stringify(params).length
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response)
return JSON.parse(response.getContentText());
There is one more thread regarding the same issue on stackoverflow but without correct answer.
Thank you
Related
I'm trying to test my javascript code online via Programiz
but I'm having trouble figuring out where I'm going wrong. Please send help.
Here's the response that I'm getting when I send the request: {"error" : {"code" : "INVALID_LOGIN_ATTEMPT", "message" : "Invalid login attempt."}}
And when I check via Login Audit Trail in NetSuite, the Role is not showing compared to when I tried using POSTMAN.
var crypto = require("crypto");
var fetch = require('node-fetch');
var url = "https://{accountID}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=729&deploy=1";
var realm = "{accountId}";
var signatureMethod = "HMAC-SHA256";
var oauthVersion = "1.0";
var consumerKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var consumerSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var tokenId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var tokenSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var nonce = randomString(10);
var timeStamp = Math.round((new Date()).getTime() / 1000);
var baseString = realm + "&" + consumerKey + "&" + tokenId + "&" + nonce + "&" + timeStamp;
var signingKey = consumerSecret + "&" + tokenSecret;
var hmac = crypto.createHmac("sha256", signingKey);
hmac.update(baseString);
var signature = encodeURIComponent(hmac.digest("base64"));
var authorizationStr = "OAuth realm=\""+ realm + "\"" +
",oauth_consumer_key=\"" + consumerKey + "\"" +
",oauth_token=\"" + tokenId + "\"" +
",oauth_signature_method=\"" + signatureMethod + "\"" +
",oauth_timestamp=\"" + timeStamp + "\"" +
",oauth_nonce=\"" + nonce + "\"" +
",oauth_version=\"" + oauthVersion + "\"" +
",oauth_signature=\"" + signature + "\"";
console.log("timeStamp: " + timeStamp);
console.log("nonce: " + nonce);
console.log("signature: " + signature);
var raw = JSON.stringify({
"searchId": "customsearch_pendingso"
});
var requestOptions = {
method: 'POST',
headers: {
"Authorization": authorizationStr,
"Content-Type": "application/json",
"Cookie": "NS_ROUTING_VERSION=LAGGING",
"Cache-Control": "no-cache",
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br"
},
body: raw,
redirect: 'follow'
};
fetch(url, requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
function randomString(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
Here's the solution to my problem.
So instead of trying to do it on an online compiler, I installed NodeJS and used oauth-1.0a.
var request = require('request');
var OAuth = require('oauth-1.0a');
var realm = "{ACCOUNTID}";
var token = {
public: "{TOKEN_ID}",
secret: "{TOKEN_SECRET}"
};
var oauth = OAuth({
consumer: {
public: "{CONSUMER_KEY}",
secret: "{CONSUMER_SECRET}"
},
signature_method: "HMAC-SHA256"
});
var request_data = {
url: "{RESTLET_EXTERNAL_URL}",
method: 'POST'
};
var request_headers = oauth.toHeader(oauth.authorize(request_data, token));
request_headers.Authorization += ', realm="' + realm + '"';
request_headers['content-type'] = 'application/json';
var request_payload = {
"searchId": "customsearch_pendingso"
}
request({
url: request_data.url,
method: request_data.method,
headers: request_headers,
json: request_payload
}, function(error, response, body) {
if (error) console.log(error);
console.log(body);
});
I have written a script function to get google sheet data and send the email as an attached PDF. I have used here pivot tables in the google sheet.
The problem is here. after the filtered pivot table and then if I send an email.it sent only without filter data. That means is default values only export as pdf.
How to send an email pdf with current filtered data?
function sendEmail(){
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var getTabsheetName=ss.getSheetByName('report');
var url = 'https://docs.google.com/a/mydomain.org/spreadsheets/d/'
+ ss.getId() //Your File ID
+ '/export?exportFormat=pdf&format=pdf'
+ '&size=LETTER'
+ '&portrait=true'
+ '&fitw=true'
+ '&top_margin=0.50'
+ '&bottom_margin=0.50'
+ '&left_margin=0.50'
+ '&right_margin=0.50'
+ '&sheetnames=false&printtitle=false&pagenumbers=true'
+ '&pagenum=false'
+ '&gridlines=false'
+ '&fzr=FALSE'
+ '&gid='
+ getTabsheetName.getSheetId(); //the sheet's Id
var emailsheet=ss.getSheetByName('Email');
var emailContentsheet=ss.getSheetByName('EmailContent');
var subject = emailContentsheet.getRange(2,1).getValue();
var n=emailsheet.getLastRow();
var params = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
var blob = UrlFetchApp.fetch(url, params).getBlob().setName('Report.pdf');
for (var i = 2; i < n+1 ; i++ ) {
// SpreadsheetApp.flush();
var emailAddress = emailsheet.getRange(i,2).getValue();
var name=emailsheet.getRange(i,1).getValue();
var message = emailContentsheet.getRange(2,2).getValue();
var returnData = [name,message];
var templ = HtmlService.createTemplateFromFile('index');
templ.data = returnData;
var htmlboday = templ.evaluate().getContent();
MailApp.sendEmail({
to: emailAddress,
subject: subject,
htmlBody: htmlboday,
attachments:[blob]
});
}
} catch (f) {
Logger.log(f.toString());
}
}
Writing specialized S3 file upload request signing function that will run on Cloudflare workers (I guess should be the same as in browsers):
let s3PutSign = function(region, keyId, keySecret, contentType, date, bucket, fileName) {
return crypto.subtle.importKey('raw', new TextEncoder().encode(keySecret), { name: 'HMAC', hash: 'SHA-256' }, true, ['sign'])
.then(key => {
let path = `/${bucket}/${fileName}`
let strToSign = `PUT\n\n${contentType}\n${date}\n${path}`
return crypto.subtle.sign('HMAC', key, new TextEncoder().encode(strToSign))
.then(sig => {
return {
url: `https://s3.${region}.amazonaws.com${path}`,
headers: {
'content-type': contentType,
'Authorization': `AWS ${keyId}:${btoa(sig)}`,
'x-amz-date': new Date(new Date().getTime() + 10000).toISOString().replace(/[:\-]|\.\d{3}/g, '').substr(0, 17)
}
}
})
})
}
Wrote function using PUT example: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
Variable strToSign:
PUT
application/pdf
Wed, 27 May 2020 12:26:33 GMT
/mybucket/file.pdf
function result:
{
url: "https://s3.eu-central-1.amazonaws.com/mybucket/file.pdf",
headers: {
content-type: "application/pdf",
Authorization: "AWS AKXAJE7XIIVXQZ4X7FXQ:W29iamVXZCBBcnJheUJ1ZmZlcl0=",
x-amz-date: "20200527T122643Z"
}
}
Requests always result this response:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>InvalidRequest</Code>
<Message>The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.</Message>
<RequestId>7CECC87D5E855C48</RequestId>
<HostId>rtGLR0u9Qc29bllgKnJf7xD00iQ0+/BZog5G/wYWjsN8tkXio9Baq7GZvbQTD40EVCQ9FzuCo9c=</HostId>
</Error>
Please advise how to debug or give a hint what could be wrong with this function.
Researching this a bit, it seems that AWS4-HMAC-SHA256 may define a specific hashing algorithm. Looking at this (awesome) gist, the author calls out the full algo name.
You might try replacing your call ({ name: 'HMAC', hash: 'SHA-256' }) with { name: 'HMAC', hash: 'AWS4-HMAC-SHA256' }
Another thought is to remove the dash (-) from your algorithm name. Go from SHA-256 to SHA256 and see if that makes a diff.
I know the post is not new, but maybe it will help someone else. :)
Here is a working code example, that I gathered from AWS documentation and some other sources.
// make the call
let signer = new S3Signer(body,'ACCESKEYS','SECRETACCESSKEY','us-east-1','my-test-bucket23.s3.us-east-1.amazonaws.com','/something/SOME/2023/02/some6.pdf','multipart/form-data');
let signerRes = await signer.makeTheCall();
// implementation
const axios = require('axios');
const crypto = require('crypto');
class S3Signer {
constructor(body, accessKey, secretKey, region, host, path, contentType) {
this.region = region; //'us-east-1';
this.host = host; // `my-test-bucket23.s3.us-east-1.amazonaws.com`
this.method = 'PUT';
this.service = 's3';
this.path = path; // `/something/SOME/2023/02/some6.pdf`
this.url = `https://${this.host}${this.path}`;
this.contentType = contentType; //'multipart/form-data';
this.amzDate = null;
this.body = body;
//this.bodyUTF8 = body.toString('utf8');
this.algorithm = 'AWS4-HMAC-SHA256';
this.credentialScope = '';
this.signedHeaders = 'content-type;host;x-amz-content-sha256;x-amz-date';
this.accessKey = accessKey;
this.secretKey = secretKey;
}
getSignatureKey = (key, dateStamp, regionName, serviceName) => {
let kDate = this.hmac(('AWS4' + key), dateStamp);
let kRegion = this.hmac(kDate, regionName);
let kService = this.hmac(kRegion, serviceName);
let kSigning = this.hmac(kService, 'aws4_request');
return kSigning;
}
makeTheCall = async () => {
let canonicalRequest = this.createCanonicalReq();
let signature = this.calculateSignature(canonicalRequest);
// ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
// Put the signature information in a header named Authorization.
let authorizationHeader = this.algorithm + ' ' + 'Credential=' + this.accessKey + '/' + this.credentialScope + ', ' + 'SignedHeaders=' + this.signedHeaders + ', ' + 'Signature=' + signature;
// For DynamoDB, the request can include any headers, but MUST include "host", "x-amz-date",
// "x-amz-target", "content-type", and "Authorization". Except for the authorization
// header, the headers must be included in the canonical_headers and signed_headers values, as
// noted earlier. Order here is not significant.
//// Python note: The 'host' header is added automatically by the Python 'requests' library.
let headers = {
'Authorization': authorizationHeader,
'Content-Type': this.contentType,
'X-Amz-Content-Sha256' : this.hash(this.body, 'hex'),
'X-Amz-Date': this.amzDate
}
let request = {
host: this.host,
method: this.method,
url: this.url,
data: this.body,
body: this.body,
path: this.path,
headers: headers
}
// send the file to s3
let res = await axios(request);
console.log(res);
return res;
}
calculateSignature = (canonicalReq) => {
// SHA-256 (recommended)
let dateStamp = this.amzDate.substring(0, 8);
this.credentialScope = dateStamp + '/' + this.region + '/' + this.service + '/' + 'aws4_request'
let stringSign = this.algorithm + '\n' + this.amzDate + '\n' + this.credentialScope + '\n' + this.hash(canonicalReq, 'hex')
// ************* TASK 3: CALCULATE THE SIGNATURE *************
// Create the signing key using the function defined above.
let signingKey = this.getSignatureKey(this.secretKey, dateStamp, this.region, this.service);
// Sign the string_to_sign using the signing_key
let signature = this.hmac(signingKey, stringSign, 'hex');
return signature;
}
createCanonicalReq = () => {
// ************* TASK 1: CREATE A CANONICAL REQUEST *************
// http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
// Step 1 is to define the verb (GET, POST, etc.)--already done.
// Step 2: Create canonical URI--the part of the URI from domain to query
// string (use '/' if no path)
let canonical_uri = this.path;
//// Step 3: Create the canonical query string. In this example, request
// parameters are passed in the body of the request and the query string
// is blank.
let canonical_querystring = '';
///// set the date
let date = new Date();
this.amzDate = date.toISOString().replace(/[:\-]|\.\d{3}/g, '')
// Step 4: Create the canonical headers. Header names must be trimmed
// and lowercase, and sorted in code point order from low to high.
// Note that there is a trailing \n.
let canonical_headers = 'content-type:' + this.contentType + '\n' + 'host:' + this.host + '\n'
+ 'x-amz-content-sha256:' + this.hash(this.body, 'hex') + '\n'
+ 'x-amz-date:' + this.amzDate + '\n'
// Step 5: Create the list of signed headers. This lists the headers
// in the canonical_headers list, delimited with ";" and in alpha order.
// Note: The request can include any headers; canonical_headers and
// signed_headers include those that you want to be included in the
// hash of the request. "Host" and "x-amz-date" are always required.
// For DynamoDB, content-type and x-amz-target are also required.
//this.signedHeaders = 'content-type;host;x-amz-date'
// Step 6: Create payload hash. In this example, the payload (body of
// the request) contains the request parameters.
let payload_hash = this.hash(this.body, 'hex');
// Step 7: Combine elements to create canonical request
let canonicalRequest = this.method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + this.signedHeaders + '\n' + payload_hash
return canonicalRequest;
}
hmac = (key, string, encoding) => {
return crypto.createHmac('sha256', key).update(string, 'utf8').digest(encoding)
}
hash = (string, encoding) => {
return crypto.createHash('sha256').update(string, 'utf8').digest(encoding)
}
// This function assumes the string has already been percent encoded
// only if you have reuquest query
encodeRfc3986 = (urlEncodedString) => {
return urlEncodedString.replace(/[!'()*]/g, (c) => {
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
})
}
}
I want to Upload an event to Azure LOG Analytics and Retrieve an event using plain JavaScript with XMLHTTPRequest or JQuery rest .
1.Using powershell invoke-webrequest, i am able to Upload the event but not through the below request from html
2. Need help in retrieving an event from Azure Log Analytics RestApi
for the first one Here is the sample code HTTP405: BAD METHOD - The HTTP verb used is not supported.
var strBody = {
"Hostname": "sdfsfsdf",
"Customer": "sdfsfdsfdsf",
"RoundTripLatencyInMs": 67,
};
debugger;
customerId = "xxxxxxxxxxxxxxx";
sharedkey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
logType = "xxxxxxxxx";
TimeStampField = "YYYY-MM-DDThh:mm:ssZ";
// encodebody = stringToUtf16ByteArray(JSON.stringify(strBody).toString('utf8'));//encode_utf8(JSON.stringify(strBody));
str = strBody;
var byteArray = [];
for (var i = 0; i < str.length; i++)
if (str.charCodeAt(i) <= 0x7F)
byteArray.push(str.charCodeAt(i));
else {
var h = encodeURIComponent(str.charAt(i)).substr(1).split('%');
for (var j = 0; j < h.length; j++)
byteArray.push(parseInt(h[j], 16));
}
// return byteArray;
encodebody = byteArray;
method = 'POST';
resource = '/api/logs';
//contentType = 'application/json; charset=utf-8';
var d =new Date();
msdate = d.toUTCString(); //'Thu, 14 Jul 2017 06:35:52 GMT';
contentLength = encodebody.length;
//Signature
xHeaders = "x-ms-date:" + msdate;
stringToHash = method + "\n" + contentLength + "\n" + xHeaders + "\n" + resource;
//message=stringToHash;
var hash = CryptoJS.HmacSHA256(stringToHash, sharedkey);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
// document.write(hashInBase64);
//authorization = 'SharedKey ' + customerId +':'+ hashInBase64 ;
signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(CryptoJS.enc.Utf8.parse(stringToHash), CryptoJS.enc.Base64.parse(sharedkey)));
authorization = 'SharedKey ' + customerId + ':' + signature;
uri = "https://" + customerId + ".ods.opinsights.azure.com" + resource + "?api-version=2016-04-01";
var myHttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
debugger;
$.ajax({
url: uri,
type: 'POST',
success: function (data) {
//do something to data
},
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', "SharedKey " + customerId + ":" + signature);
xhr.setRequestHeader('x-ms-date', msdate);
xhr.setRequestHeader('x-ms-version', '2014-02-14');
xhr.setRequestHeader('Accept-Charset', 'UTF-8');
xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
//xhr.setRequestHeader('Accept', 'application/json;odata=nometadata');
xhr.setRequestHeader("Log-Type", logType);
xhr.setRequestHeader("time-generated-field", TimeStampField);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
//xhr.setRequestHeader('DataServiceVersion', '3.0;NetFx');
//xhr.setRequestHeader('MaxDataServiceVersion', '3.0;NetFx');
},
datatype:'json',
//contentType: 'application/json',
data:encodebody,
error: function (rcvData) {
console.log("ERRor");
console.log(rcvData);
}
});
Any pointers will be helpfull
As you made a REST call from browser side using javascript, the browser would first send an HTTP request by the OPTIONS method to the resource to look for CORS headers. See Preflighted requests. And Log Analytics REST API doesn't allow the OPTIONS HTTP verb. It means that the API doesn't support CORS. So, it raises your issue.
So, you should call the REST API by using server side language like PHP, Python, Node.js, etc.
I have a rather large and complex code of SVG that generates using JavaScript and jQuery dynamically based on the pages information.
I then have an AJAX post save.
What am I failing to do to convert this to post the image data properly?
var canvas = $("#canvas")[0];
var string= canvas.toDataURL("image/png");
base64=string.replace("data:image/png;base64,", "");
var rid = kRid || "";
var fileN = " Product " + rid + ".png";
var req = "";
req += "<qdbapi>";
req += "<field fid='323' filename='" + fileN + "'>" + base64 + "</field>";
req += "</qdbapi>";
$.ajax({
type: "POST",
contentType: "text/xml",
dataType: "xml",
processData: false,
//url altered
url: "https://removed.quickbase.com/db/removedDBID?act=API_EditRecord&rid=" + rid,
data: req,
success: function(responce) {
//auto reload page
var str = window.location.href;
setTimeout(function() {
window.location.href = str;
}, 5000);
}
})
The idea came from this snippet of code that I used else ware to get current PNG files and move them:
$.get(url, function(xml) {
var promises = [];
$("record", xml).each(function() {
var url = $("f#9 url", this).text();
xhr.responseType = "arraybuffer";
xhr.onload = function() {
var arrayBuffer = xhr.response;
var base64 = btoa([].reduce.call(new Uint8Array(arrayBuffer), function(p, c) {
return p + String.fromCharCode(c)
}, ''))
var req = "";
req += "<qdbapi>";
req += "<field fid='6' filename='" + name + "'>" + base64 + "</field>";
req += "<field fid='54' >" + Rid + "</field>";
req += "<field fid='44' >" + comment + "</field>";
req += "</qdbapi>";
... then the AJAX post.
I do not have access to do this via PHP.
(Posted on behalf of the OP).
I forgot to add == to mark the end of the file so:
var string= canvas.toDataURL("image/png");
string+="==";
base64=string.replace("data:image/png;base64,", "");
and huzza it works...