Error API0005 has occurred due to an invalid signature connection when trying to establish a connection between Google Apps Script

Currently, I am attempting to showcase various data points from my BitStamp account on a Google Spreadsheet. To accomplish this task, the Google Apps Script (GAS) that utilizes JavaScript is being utilized.

The issue at hand involves an error with code API0005. Upon referencing the BitStamp API documentation, it appears that this error signifies an "invalid signature:".

API0005    Invalid signature    Posted signature doesn't match with ours

Despite researching extensively on platforms like Stack Overflow, pinpointing the root cause of this problem has proven to be challenging.

An interesting observation I've made pertains to the variation in the output of the nonce as different steps of the "signature synthesis process" are appended within the spreadsheet itself. The dynamic nature of the nonce output changing slightly between each function call or invocation does not come as a surprise, given its design.

However, one question that arises is whether it's normal for the nonce to alter even when the toUpperCase() conversion is executed? This may not necessarily be the reason behind the overall issue at hand.

https://i.sstatic.net/4d9Gw.png

var nonce = new (function() {
    this.generate = function() {
        var now = Date.now();
        this.counter = (now === this.last? this.counter + 1 : 0);
        this.last    = now;
        // add padding to nonce
        var padding = 
            this.counter < 10 ? '000' : 
                this.counter < 100 ? '00' :
                    this.counter < 1000 ?  '0' : '';
        return now+padding+this.counter;
    };
})();

//write function
function write() {

var cred = {
       id:'digit2118',
      key:'2RhZfUKIYJbT8CBYk6T27uRSF8Gufre',
   secret:'T8CBYk6T274yyR8Z2RhZfUxbRbmZZHJ'
};

//Adding cell outputs to monitor each step of the "conversion" process
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var cell = sheet.getRange("B14");
cell.setValue(cred.id);
// .. and so forth, refer to screenshot ..

var message = nonce.generate() + cred.id +  cred.key;

var res = Utilities.computeHmacSha256Signature(cred.secret, message).map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");
var signature = res.toUpperCase();

// Additional data for actual communication
var data = {
  key: cred.key,
  signature: res,
  nonce: nonce.generate()
};

var options = {
  'method' : 'post',
  //'contentType': 'application/json',
  //'payload' : JSON.stringify(data)
  'muteHttpExceptions' : true,
  'payload' : data
};

var response = UrlFetchApp.fetch('https://www.bitstamp.net/api/v2/balance/', options);
  var responseJSON = JSON.parse(response.getContentText());

  Logger.log(responseJSON);
  
return signature;
}//end of write();

Logger.log(write());

Although the exact omission remains elusive, something crucial seems to have slipped through the cracks.

(ps:this is where I got the Nonce code from: Generate Nonce,)

   

EDIT: Question Solved.

Updated code with problem & solution below.

Special thanks to @Tanaike

Answer №1

What are your thoughts on this updated version?

Key changes:

  • The order of arguments in
    Utilities.computeHmacSha256Signature(value, key)
    has been switched from cred.secret and message to message and cred.secret.
    • To implement the change, please update
      Utilities.computeHmacSha256Signature(cred.secret, message)
      to
      Utilities.computeHmacSha256Signature(message, cred.secret)
      .

Revised code snippet:

From:
var res = Utilities.computeHmacSha256Signature(cred.secret, message).map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");
To:
var res = Utilities.computeHmacSha256Signature(message, cred.secret).map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");

Note:

  • If you encounter any errors after making the above modification, consider setting nonce as a constant value in the request and try again.
    • It's possible that fixing nonce as a constant value in the request may be necessary. Please test this out in your own environment.
    • For reference, I observed that a sample script for Node.js fixed nonce as a constant value in a request.

Additional Resources:

I have not personally tested this modification in my setup. If it does not directly address your issue, I apologize. If there are any changes in error messages due to this adjustment, please provide them for further assistance.

Answer №2

Following the insightful advice from @Tanaike to switch value and key in the

Utilities.computeHmacSha256Signature(value, key)
command, I still encountered an issue with receiving an Invalid Signature API0005 error.

To cut the story short, the problem lay in another oversight within the code:

Although I correctly converted the signature to uppercase using toUpperCase(), when sending the array to BitStamp, I mistakenly sent the lowercase version (res) instead of the correct variable (signature):

var signature = res.toUpperCase();

// Data is prepared for actual communication
var data = {
  key: cred.key,
  signature: res,
  nonce: nonce.generate()
};

This detail has been rectified, and now everything is functioning as expected! Here is the updated and fully operational code snippet for your reference:

// Function to write
function write() {

/* New function for generating nonce */
_generateNonce = function() {
  var now = new Date().getTime();

  if(now !== this.last)
    this.nonceIncr = -1;

  this.last = now;
  this.nonceIncr++;

  // Adding padding to nonce increment
  var padding =
    this.nonceIncr < 10 ? '000' :
      this.nonceIncr < 100 ? '00' :
        this.nonceIncr < 1000 ?  '0' : '';
  return now + padding + this.nonceIncr;
}

var nonce = this._generateNonce(); // End of function

var cred = {
       id:'digit2118',
      key:'2RhZfUKIYJbT8CBYk6T27uRSF8Gufrer',
   secret:'T8CBYk6T274yyR8Z2RhZfUxbRbmZZHJr'
};

var message = nonce + cred.id +  cred.key;

var res = Utilities.computeHmacSha256Signature(message, cred.secret).map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");

var signature = res.toUpperCase();

var data = {
  key: cred.key,
  signature: signature,
  nonce: nonce
};

var options = {
  'method' : 'post',
  //'contentType': 'application/json',
  //'payload' : JSON.stringify(data)
  'muteHttpExceptions' : true,
  'payload' : data
};

var response = UrlFetchApp.fetch('https://www.bitstamp.net/api/v2/balance/', options);
  var responseParsed = JSON.parse(response.getContentText());
  var balanceUSD = responseParsed['usd_balance'];
  Logger.log(balanceUSD);
return signature;
}// End of write();

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best method for creating a draggable widget in HTML, specifically with the use of Angular?

I am searching for an HTML div that is free floating and can be dragged and positioned anywhere on the page without being affected by other elements. It's okay if the element blocks the view of elements below, and it would be great if there is a minim ...

Utilizing Regular Expressions in Express.js Routes

Is there a way to extract a specific value from my URL using Express? For example, my URL structure is as follows: host/:value.schema I need to retrieve the value from this URL. Here's an example: host/horse.schema (In this case, the value wo ...

I encountered a crash in my app because of an error in my Node.js backend code that was posting the accessories and slug into the database

My node.js backend code is responsible for adding the accessory and slug to the database, but I am encountering app crashes. const express=require('express'); const Category = require("../models/Category"); const slugify=require('s ...

"Proceeding to" express this redirection

Greetings, I am currently struggling to understand why res.redirect('/index') is rendering like this: <p>OK. Redirecting to <a href="/index">/</a></p> Instead of directly redirecting on the page. I searched through th ...

Running multiple nodemon inspect programs concurrently is not supported

Situation: currently operating Ubuntu 19.10 on an Inspiron 560. I am managing 2 distinct Discord bots that need to be active on the same device (my other spare machines are currently occupied with other tasks and lack the capacity to handle Discord bots). ...

Using babel with node.js version 18 allows you to easily import modules with various extensions and run them from the build folder configured with commonJS. Ensure that you have specified "type:module"

I'm currently utilizing Node.JS version 18.20.4 alongside Babel. Below is my babel.config.json: { "presets": [ [ "@babel/preset-env", { "targets": { "node": 18 }, ...

What is the most stylish method for merging a removeCartItem function with an addCartItem function?

Is there a way to combine these functions into a more concise and elegant piece of code? While the current setup works fine, it feels redundant to have two large functions that essentially do the same thing. const modifyCartItem = (cartItems, productToMo ...

Create a collection of SVG illustrations using Vivus.js

Is it possible to draw multiple SVGs using Vivus.js without having to call the function for each individual drawing? I've encountered an issue with the second drawing not animating properly. Any suggestions or experience with this? Check out this pen ...

What makes a single numerical value in an array suitable for basic arithmetic operations?

I find it puzzling how an empty array or an array with just one "numerical" value can be used in various calculations. [] * [] === 0 // true [2] * [2] === 4 // true ["2"] * ["2"] === 4 // true Interestingly, not every operator behaves in the same way. ...

What is the best way to conduct a Javascript test using Jasmine?

I'm encountering an issue with testing this JavaScript code: $("#ShootBtn").on('click', () => foo.testFunc()); var foo = { testFunc: function() { hub.server.shoot(true, username, gameCode); } } For my testing framework, ...

Transmit data from an HTML form to PHP using JSON

I am attempting to utilize JavaScript to send POST data. I have the data in an HTML form: <form name="messageact" action=""> <input name="name" type="text" id="username" size="15" /> <input name="massage" type="text" id="usermsg" si ...

WindowWizard - Unlocks the ability to open two windows simultaneously with just one click

Every time I click on the button below, it seems to open the page twice. I'm having trouble identifying the issue: The code in the .cs file: protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { btnPrint. ...

Hide a div element upon selecting an option from a dropdown menu

On iPhone 6plus and lower, I created a dropdown menu that opens when users click on "jobs" or "contact." However, after clicking on these options, the drop-down menu remains open. How can I make it hide automatically after a list item is clicked at this sp ...

Steps for checking if the specified row has been accurately filled out in a loop

Here is an example of how my JSON data is structured: { "main_object": { "id": "5", "getExerciseTitle": "TestFor", "language": "nl_NL", "application": "lettergrepen", "main_object": { "title": "TestFor", "language": "nl_NL", "exercises": [ { ...

Using the same ng-model to show distinct values in two separate input fields

I have a form with a dropdown list that loads data using the ng-model="placeid". When a user selects an option from the dropdown, I want to display the selected place's latitude (place.lat) in an input box. Here is the dropdown list code: <select ...

Pass a set value in Vue configuration

I need to create a form where users can edit information from a database. The challenge is to display the current value in the input field so users can choose to change it or leave it as is. Here's how I'm attempting to achieve this: <div v-f ...

Creating a server that is exclusive to the application in Node.js/npm or altering the response body of outgoing requests

As I work on developing a node app, the need for a server that can be used internally by the app has become apparent. In my attempts to create a server without listening to a port, I have hit a roadblock where further actions seem impossible: let http = ...

Angular.js: Navigating through HTML content

I am attempting to display a list of HTML data using angular.js and would like to implement ngInfiniteScroll. Here is the template I created: update: <div class="scroll" id="antTalkList" talk-type="total" view-type="total" infinite-scroll=' ...

Adapting the column width to display or hide content with CSS styling

I have a row with 2 columns. The left column contains my main content and the right column is a chatroom. I would like users to be able to minimize and open the chatroom, which I already know how to do. However, when the chatroom is open, I want the left ...

Unlocking the Potential of Checkbox Values in AngularJS

I am attempting to extract data from a $rootScope object that has been linked to the checkboxes. HTML: <tr ng-repeat="system_history in SystemHistoryResponse.system_history"> <td><input type="checkbox" ng-model="versionValues[system_histor ...