I am trying to create a Customer record in NetSuite by calling a deployed NetSuite Restlet script from Node.js. I am getting a 403 error in response. I am able to call the script successfully through Postman. I am not sure what's the issue when called from Node.js, because the authorization part in the code looks correct. Here's the code in Node.js:
function upsertUsingRestlet(body, callbackFn) {
const baseUrl = `https://${NETSUITE_ACCOUNT_ID}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=${SCRIPT_ID}&deploy=${DEPLOYMENT_ID}`;
const oauthSignatureMethod = 'HMAC-SHA256';
const oauthVersion = '1.0';
const oauthNonce = crypto.randomBytes(32).toString('hex');
const oauthTimestamp = Math.floor(Date.now() / 1000);
const oauthParameters = {
oauth_consumer_key: CONSUMER_KEY,
oauth_token: ACCESS_TOKEN,
oauth_nonce: oauthNonce,
oauth_timestamp: oauthTimestamp,
oauth_signature_method: oauthSignatureMethod,
oauth_version: '1.0'
};
const sortedParameters = Object.keys(oauthParameters)
.sort()
.map((key) => `${key}=${oauthParameters[key]}`)
.join('&');
const signatureBaseString = `POST&${encodeURIComponent(baseUrl)}&${encodeURIComponent(sortedParameters)}`;
const signingKey = `${CONSUMER_SECRET}&${ACCESS_TOKEN_SECRET}`;
const hmac = crypto.createHmac('sha256', signingKey);
hmac.update(signatureBaseString);
let oauthSignature = hmac.digest('base64');
oauthSignature = encodeURIComponent(oauthSignature);
const headers = {
'Prefer': 'transient',
'Content-Type': 'application/json',
'Authorization': `OAuth realm="${REALM}",oauth_nonce="${oauthNonce}",oauth_signature_method="${oauthSignatureMethod}",oauth_consumer_key="${CONSUMER_KEY}",oauth_token="${ACCESS_TOKEN}",oauth_timestamp="${oauthTimestamp}",oauth_version="${oauthVersion}",oauth_signature="${oauthSignature}"`
};
fetch(baseUrl, {
'method': 'POST',
'headers': headers,
'body': body,
'redirect': 'follow'
})
.then((response) => response)
.then((data) => {
callbackFn(data.status);
})
.catch((error) => {
console.error('Error upsertOperation:', error);
});
}
In Netsuite, View login and Audit trail - I see the error in Detail column as InvalidSignature.
However, the same authorization is working successfully when I call a netsuite REST API using the above code. The only difference was that it was a PUT request instead of POST. So, I am not sure why it is not working with Restlet.
Restlet script deployment screenshot for reference:
It worked with the following changes:
Rest everything remains the same.
Here's the updated code for reference:-
function upsertUsingRestlet(body, callbackFn) {
const baseUrl = `https://${NETSUITE_ACCOUNT_ID}.restlets.api.netsuite.com/app/site/hosting/restlet.nl`;
const baseUrlWithParameters = baseUrl + `?script=${SCRIPT_ID}&deploy=${DEPLOYMENT_ID}`;
const oauthSignatureMethod = 'HMAC-SHA256';
const oauthVersion = '1.0';
const oauthNonce = crypto.randomBytes(32).toString('hex');
const oauthTimestamp = Math.floor(Date.now() / 1000);
const oauthParameters = {
script: SCRIPT_ID,
oauth_consumer_key: CONSUMER_KEY,
oauth_token: ACCESS_TOKEN,
oauth_nonce: oauthNonce,
oauth_timestamp: oauthTimestamp,
oauth_signature_method: oauthSignatureMethod,
oauth_version: '1.0',
deploy: SCRIPT_DEPLOYMENT_ID
};
const sortedParameters = Object.keys(oauthParameters)
.sort()
.map((key) => `${key}=${oauthParameters[key]}`)
.join('&');
const signatureBaseString = `POST&${encodeURIComponent(baseUrl)}&${encodeURIComponent(sortedParameters)}`;
const signingKey = `${CONSUMER_SECRET}&${ACCESS_TOKEN_SECRET}`;
const hmac = crypto.createHmac('sha256', signingKey);
hmac.update(signatureBaseString);
let oauthSignature = hmac.digest('base64');
oauthSignature = encodeURIComponent(oauthSignature);
const headers = {
'Prefer': 'transient',
'Content-Type': 'application/json',
'Authorization': `OAuth realm="${REALM}",oauth_nonce="${oauthNonce}",oauth_signature_method="${oauthSignatureMethod}",oauth_consumer_key="${CONSUMER_KEY}",oauth_token="${ACCESS_TOKEN}",oauth_timestamp="${oauthTimestamp}",oauth_version="${oauthVersion}",oauth_signature="${oauthSignature}"`
};
fetch(baseUrlWithParameters, {
'method': 'POST',
'headers': headers,
'body': body,
'redirect': 'follow'
})
.then((response) => response)
.then((data) => {
callbackFn(data.status);
})
.catch((error) => {
console.error('Error upsertOperation:', error);
});
}