Trying to convert a Python script that works to Google Apps Script as easier to distribute. Appears to function as it should and generates a signature however with using the same nonce and timestamp from the Python script I can see that Google Apps script creates a different Signature and I can't for the life of me fathom out why. I know it will fail I'm just trying to match up the signatures before continuing!
Any ideas on any methods that will work from Google Apps Script for connecting to NetSuite using TBA - I repeat this needs to use TBA not OAuth2 etc. Any help or guidance appreciated.
function main() {
var REALM = 'XXXXX';
var CONSUMER_KEY = 'XXXXXXx';
var CONSUMER_SECRET = 'XXXXXXXX';
var RESOURCE_OWNER_KEY = 'XXXXXXXXX';
var RESOURCE_OWNER_SECRET = 'XXXXXXXX';
var method = "PATCH";
var url = 'https://' + REALM + '.suitetalk.api.netsuite.com/services/rest/record/v1/vendor/1846';
var params = {
'oauth_consumer_key': CONSUMER_KEY,
'oauth_nonce': '38751934124598253091697458273',
'oauth_signature_method': 'HMAC-SHA256',
'oauth_timestamp': '1697458273',
'oauth_token': RESOURCE_OWNER_KEY,
'oauth_version': '1.0',
'realm': REALM
};
// Sorting and encoding the parameters
// Sorting and encoding the parameters
var paramKeys = Object.keys(params);
paramKeys.sort();
var paramStr = paramKeys.map(function(key) {
return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
}).join('&');
// Create base string
var baseString = method + '&' + encodeURIComponent(url) + '&' + encodeURIComponent(paramStr);
// Create signature key
var signatureKey = encodeURIComponent(CONSUMER_SECRET) + '&' + encodeURIComponent(RESOURCE_OWNER_SECRET);
// Generate HMAC-SHA256 signature
var signature = Utilities.computeHmacSha256Signature(baseString, signatureKey);
signature = Utilities.base64Encode(signature);
Logger.log('Base String: ' + baseString);
Logger.log('Signature Key: ' + signatureKey);
Logger.log('Signature: ' + signature);
// Prepare OAuth1.0 headers
params['oauth_signature'] = signature;
var authHeader = 'OAuth ' + Object.keys(params).map(function(key) {
return encodeURIComponent(key) + '="' + encodeURIComponent(params[key]) + '"';
}).join(', ');
Logger.log("Params are: "+JSON.stringify(params))
// Prepare HTTP options
var options = {
'method': method,
'headers': {
'Authorization': authHeader,
'Content-Type': 'application/json'
},
'payload': JSON.stringify({"custentity_id": "2"})
};
// Send HTTP request and log the response
try {
var response = UrlFetchApp.fetch(url, options);
Logger.log('Response: ' + response.getContentText());
} catch (e) {
Logger.log('Error: ' + e);
}
}
Python Script
import requests
import oauthlib.oauth1
import json
import hmac
import hashlib
import base64
from urllib.parse import quote_plus
from pprint import pprint
# Constants and configurations
REALM = 'XXXXXXX'
CONSUMER_KEY = 'XXXXXXXXXXX'
CONSUMER_SECRET = 'XXXXXXXX'
RESOURCE_OWNER_KEY = 'XXXXXXXX'
RESOURCE_OWNER_SECRET = 'XXXXXXXX'
# Create a session object
session = requests.Session()
def generate_signature(base_string, signature_key):
hmac_obj = hmac.new(signature_key.encode('utf-8'), base_string.encode('utf-8'), hashlib.sha256)
return base64.b64encode(hmac_obj.digest()).decode('utf-8')
def create_netsuite_client():
client = oauthlib.oauth1.Client(
CONSUMER_KEY,
client_secret=CONSUMER_SECRET,
resource_owner_key=RESOURCE_OWNER_KEY,
resource_owner_secret=RESOURCE_OWNER_SECRET,
realm=REALM,
signature_method="HMAC-SHA256",
timestamp='1697458273',
nonce='38751934124598253091697458273'
)
return client
def process_vendor(netsuite, id, netsuite_id):
data = {"custentity_id": id}
hdr = {"Content-Type": "application/json"}
url = f'https://{REALM}.suitetalk.api.netsuite.com/services/rest/record/v1/vendor/{netsuite_id}'
url, headers, body = netsuite.sign(url, headers=hdr, body=json.dumps(data), http_method="PATCH")
pprint(headers)
try:
response = session.request("PATCH", url, headers=headers, data=body)
response.raise_for_status()
print(f"{id},{netsuite_id},Successful")
except requests.RequestException as err:
print(f"{id},{netsuite_id},{str(err)},{json.dumps(response.json()).replace(',', ' ')}")
def main():
netsuite = create_netsuite_client()
process_vendor(netsuite, '2', '1846')
if __name__ == '__main__':
main()
Just incase it helps anyone else - went back to basics and managed to rectify - combination of things but mainly the ordering of the OAuth params and headers. All values are dummy ones!! Probably not the most elegant but the end result has been achieved!!
function upsertOperation(externalId, body) {
// Constants and configurations
var REALM = '123456';
var CONSUMER_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';
var CONSUMER_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';
var RESOURCE_OWNER_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';
var RESOURCE_OWNER_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx';
var NETSUITE_ID = '1234';
var ID = '2';
var METHOD = "PATCH";
var baseUrl = 'https://123456.suitetalk.api.netsuite.com/services/rest/record/v1/vendor/' + NETSUITE_ID;
// Generate OAuth nonce and timestamp
var oauthNonce = Utilities.getUuid();
var oauthTimestamp = Math.floor(new Date().getTime() / 1000);
// OAuth Parameters
var oauthSignatureMethod = 'HMAC-SHA256';
var oauthVersion = '1.0';
var realm = REALM;
// Collect and sort OAuth parameters
var oauthParameters = {
oauth_consumer_key: CONSUMER_KEY,
oauth_token: RESOURCE_OWNER_KEY,
oauth_nonce: oauthNonce,
oauth_timestamp: oauthTimestamp,
oauth_signature_method: oauthSignatureMethod,
oauth_version: oauthVersion
};
var sortedParameters = Object.keys(oauthParameters)
.sort()
.map(function(key) {
return key + '=' + oauthParameters[key];
})
.join('&');
Logger.log("Sorted Params: "+sortedParameters);
// Compute OAuth signature
var signatureBaseString = METHOD + '&' + encodeURIComponent(baseUrl) + '&' + encodeURIComponent(sortedParameters); //WORKING CORRECTLY
Logger.log("Signature BaseString: "+signatureBaseString);
var signingKey = encodeURIComponent(CONSUMER_SECRET) + '&' + encodeURIComponent(RESOURCE_OWNER_SECRET); //WORKING CORRECTLY
Logger.log("Signing Key: "+signingKey);
var hmac = Utilities.computeHmacSha256Signature(signatureBaseString, signingKey);
var oauthSignature = Utilities.base64Encode(hmac);
Logger.log("SIGNATURE: "+ oauthSignature);
// Prepare headers
var headers = {
'Content-Type': 'application/json',
'Authorization': 'OAuth ' +
'realm="' + encodeURIComponent(realm) + '", ' +
'oauth_token="' + encodeURIComponent(RESOURCE_OWNER_KEY) + '", ' +
'oauth_consumer_key="' + encodeURIComponent(CONSUMER_KEY) + '", ' +
'oauth_nonce="' + encodeURIComponent(oauthNonce) + '", ' +
'oauth_timestamp="' + encodeURIComponent(oauthTimestamp) + '", ' +
'oauth_signature_method="' + encodeURIComponent(oauthSignatureMethod) + '", ' +
'oauth_version="' + encodeURIComponent(oauthVersion) + '", ' +
'oauth_signature="' + encodeURIComponent(oauthSignature) + '"'
};
Logger.log("Headers: " + JSON.stringify(headers, null, 2));
var PAYLOAD = {
'id': id
};
// HTTP request options
var options = {
method: METHOD,
headers: headers,
payload: JSON.stringify(PAYLOAD),
followRedirects: true
};
// Perform HTTP request and log response
var response = UrlFetchApp.fetch(baseUrl, options);
if (response.getResponseCode() === 200) {
// Successfully connected
var json = response.getContentText();
var data = JSON.parse(json);
// Do something with the data
} else {
// Log the error information for debugging
Logger.log("Error: " + response.getContentText());
}
}