Hey guys, quick question, when calling a restlet f...
# suitescript
e
Hey guys, quick question, when calling a restlet from an external client/application, are you still using oauth 1.0 or it is your standard using oauth 2.0 now?
c
Generally OAuth 2.0, but I'll still roll an OAuth 1.0 for something quick n dirty. Why do you ask?
this 1
e
I see, well it has been a while since I had to work with a restlet from an external client and I know how to authenticate with OAuth 1.0 but I have not tried 2.0 yet and was not sure if the 1.0 was still acceptable or if it was deprecated for security reasons or stuff like that. If I want to read about 2.0 or get a basic example, where should I look/search for this?
n

https://www.youtube.com/watch?v=MAOMQp5dh0U

Robin Mitchell from Head in the Cloud has a couple of videos on his channel that may be a good place to at least start.
e
It seems really cool! thank you @Nathan L 🙌
🫡 1
c
@eminero Here's our reference guide which includes links to the help center articles I used to write it. It's specifically for machine-to-machine flow, not a client flow. Hope it helps you get moving!
🤝 1
🙌 3
e
Oh wow! this is awesome! Thank you @Clay Roper I owe you one.
b
Thank you for sharing! 🙂
Quick question, i could connect to a rest api using postman but when i tried to access the api from a node app I am getting bad request. My question is... how do you create the authorization header object using the consumer key and secret?
c
@Berenice Domínguez If you're trying to use a consumer key and secret, it sounds like you're using an OAuth 1.0 process. Is that correct?
b
Yes. I am having trouble trying to connect using axios actually. I send the required params based on NS documentation but still no luck.
I can connect using postman.
e
I have not used axios for this but if you share the code then probably it would be better to provide help.
👍 1
b
Also running a node app the same.
I will in a bit send the code :)
Copy code
var results = {};
const apiAuthorization = 'OAuth realm="123456_SB1",oauth_consumer_key="e9c90beb74d471e8b457b9b7931c4ddca3c96a57d4a88cb6cd23a4b85b5",oauth_token="6a59bd7d68ca9be7b6a542257dd42005ee02bc291b0559856633ae13",oauth_signature_method="HMAC-SHA256",oauth_timestamp="1710178192",oauth_nonce="NMN8TPc0fvu",oauth_version="1.0",oauth_signature="p9b/MNSBP2/uwGELRzyWQT5+kdHSs+UH7d/iWAoCOXs="';

fetch('<https://123456-SB1.suitetalk.api.netsuite.com/services/rest/record/v1/customer?limit=10>', {
    headers: {
        Authorization: apiAuthorization,
    },
})
.then(response => {
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }
    return response.json();
})
.then(protectedData => {
    console.log('Protected Data:', protectedData);
})
.catch(error => {
    console.error('Error:', error);
});
So that is when i tried to use fetch() in a simple js file.
This is using axios.
Copy code
var axios = require('axios');

const apiAuthorization = 'OAuth realm="123456_SB1",oauth_consumer_key="e9c90beb74d471e8c6398b457b9bddca3c96a57d4a88cb6cd23a4b85b5",oauth_token="6a59bd7d68ca9be7b6a542257dd42005ee02bc291b0559856633ae13",oauth_signature_method="HMAC-SHA256",oauth_timestamp="1710178192",oauth_nonce="NMN8TPc0fvu",oauth_version="1.0",oauth_signature="p9b/MNSBP2/uwGELRzyWQT5+kdHSs+UH7d/iWAoCOXs="';


axios.get(
    '<https://123456-SB1.suitetalk.api.netsuite.com/services/rest/record/v1/customer?limit=10>',
    {
        headers: {
            Authorization: apiAuthorization,
        }
    }
).then(response => {
    console.log(response.data);
}).catch(error => {
    console.log(error);
});
When running the app in the terminal i get this:
Copy code
data: {
      type: '<https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.2>',
      title: 'Unauthorized',
      status: 401,
      'o:errorDetails': [Array]
    }
This is what i get when accessing the webpage where the script uses fetch()
e
Hmm, a few things, hopefully you are not sharing the real consumer key and token, that is sensitive data. You are not trying to connect to a restlet which was the original conversation in this post, it seems you are trying to call a REST Service or something like that (I'm not familiar with that URL). Probably in order to use oauth 1.0, I would say it is similar but not quite sure. Anyways, you are missing the use of libraries like CryptoJS and OAuth 1.0 to perform the authentication, you are trying to send just a string with values that you think are correct but I have never done that and I'm not sure that is even correct. Take a look at this document and look for: Generating TBA Headers using JavaScript to Test RESTlets Probably there is other section that applies exactly to your scenario. https://suiteanswers.custhelp.com/ci/okcsFattach/getFile/1011960/SuiteAppArchitecturalFundamentalsandExamples.pdf
🙌 1
b
No I am not 🙂
My first approach was to access restlets. Do you use those libraries as well to perform the authentication?
Seems like it. var hmacsha1Data = CryptoJS.HmacSHA256(completeData, CONSUMER_SECRET + '&' + TOKEN_SECRET);
Hi again, i tried the following:
Copy code
var NETSUITE_ACCOUNT_ID = '123456_SB1'
var BASE_URL = '<https://123456-sb1.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=1310&deploy=1>';
Copy code
var HTTP_METHOD = 'POST'
var SCRIPT_ID = '613'
var OAUTH_VERSION = '1.0';
var SCRIPT_DEPLOYMENT_ID = '1'
var TOKEN_ID = "123456"
var TOKEN_SECRET = "123456"
var CONSUMER_KEY = "123456"
var CONSUMER_SECRET = "123456"
var text = "";
var length = 32;
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
}
var OAUTH_NONCE = text;
var TIME_STAMP = Math.round(+new Date() / 1000);
var data = '';
//data = data + 'deploy=' + SCRIPT_DEPLOYMENT_ID + '&';
data = data + 'oauth_consumer_key=' + CONSUMER_KEY + '&';
data = data + 'oauth_nonce=' + OAUTH_NONCE + '&';
data = data + 'oauth_signature_method=' + 'HMAC-SHA256' + '&';
data = data + 'oauth_timestamp=' + TIME_STAMP + '&';
data = data + 'oauth_token=' + TOKEN_ID + '&';
data = data + 'oauth_version=' + OAUTH_VERSION + '&';
//data = data + 'script=' + SCRIPT_ID;
var encodedData = encodeURIComponent(data);
var completeData = HTTP_METHOD + '&' + encodeURIComponent(BASE_URL) + '&' + encodedData;
var hmacsha1Data = CryptoJS.HmacSHA256(completeData, CONSUMER_SECRET + '&' + TOKEN_SECRET);
// var base64EncodedData = Base64.stringify(hmacsha1Data);
var base64EncodedData = btoa(hmacsha1Data);
var oauth_signature = encodeURIComponent(base64EncodedData);
var OAuth = 'OAuth oauth_signature="' + oauth_signature + '",';
OAuth = OAuth + 'oauth_version="1.0",';
OAuth = OAuth + 'oauth_nonce="' + OAUTH_NONCE + '",';
OAuth = OAuth + 'oauth_signature_method="HMAC-SHA256",';
OAuth = OAuth + 'oauth_consumer_key="' + CONSUMER_KEY + '",';
OAuth = OAuth + 'oauth_token="' + TOKEN_ID + '",';
OAuth = OAuth + 'oauth_timestamp="' + TIME_STAMP + '",';
OAuth = OAuth + 'realm="' + NETSUITE_ACCOUNT_ID + '"';
// var request = <http://https.post|https.post>({
// url: BASE_URL + '?script=' + SCRIPT_ID + '&deploy=' + SCRIPT_DEPLOYMENT_ID,
// headers: { "Content-Type": "application/json", "Authorization": OAuth },
// body: JSON.stringify({ hello: "world" })
// })

const myHeaders = new Headers();
myHeaders.append('Authorization', OAuth);
myHeaders.append('Content-Type', 'application/json');

var myOptions = {
    method: 'GET',
    headers: myHeaders,
    mode: 'no-cors',
    cache: 'default',
    // body: JSON.stringify({ hello: "world" })
};

var request = fetch(REST_API_BASE_URL, myOptions);
console.log('request', request);
var response = JSON.parse(request.body)
log.debug('response', response)
And I am still getting unauthorized.
Any ideas about this?
c
@Berenice Domínguez For one thing, I believe you need the
deploy
and
script
parameters included in the base string of your signature
🙌 1
b
Thank you for your input I will try that next. Another question do you know how to access a suitetalk API instead of a restlet? Like what's the way of creating the signature?
c
I don't have experience with that, but perhaps the docs on this will help?
b
Thank you so much!!!
🙌 1
I added those parameters and i am getting this error:
Copy code
errno: -61,
  code: 'ECONNREFUSED',
m
@Berenice Domínguez: I am able to make a connection using OAuth 1.0. However, sometimes I am receiving "InvalidSignature" error and sometimes it work perfectly.
Copy code
const crypto = require('crypto');
const axios = require('axios');

const credentials = require('./secret').credentials;

const ACCOUNT_ID = '12345_SB1';
const CONSUMER_KEY = credentials.CONSUMER_KEY;
const CONSUMER_SECRET = credentials.CONSUMER_SECRET;
const TOKEN = credentials.TOKEN;
const TOKEN_SECRET = credentials.TOKEN_SECRET;

const timestamp = Math.floor(Date.now() / 1000);
const nonce = (() => {

    let nonce = ''
    const alphaNum = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    for (let index = 20; index > 0; index--) {
        nonce += alphaNum.charAt(Math.floor(Math.random() * alphaNum.length));
    }
    return nonce;
})();

console.log('timestamp:: ' + timestamp);
console.log('nonce:: ' + nonce);

const SIGNATURE_METHOD = 'HMAC-SHA256';
const urlParams = {
    limit: 5,
};
const reqParams = {
    ...{
        oauth_consumer_key: CONSUMER_KEY,
        oauth_nonce: nonce,
        oauth_signature_method: SIGNATURE_METHOD,
        oauth_timestamp: timestamp,
        oauth_token: TOKEN,
        oauth_version: '1.0'
    },
    ...urlParams
};

const encodeRFC3986URIComponent = (str) => {
    return encodeURIComponent(str).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,);
}

const METHOD = 'GET';
const baseString = ((METHOD, url, reqParams) => {

    let strReqParams = '';
    const arrReqParamKeys = Object.keys(reqParams).sort();
    for (let index = 0; index < arrReqParamKeys.length; index++) {

        const name = arrReqParamKeys[index];
        const value = reqParams[name];

        if (index < arrReqParamKeys.length - 1) {
            strReqParams += name + '=' + value + '&';
        } else {
            strReqParams += name + '=' + value;
        }
    }
    return METHOD + '&' + encodeRFC3986URIComponent(url) + '&' + encodeRFC3986URIComponent(strReqParams);
})(METHOD, '<https://12345-sb1.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder>', reqParams);

const key = encodeRFC3986URIComponent(CONSUMER_SECRET) + '&' + encodeRFC3986URIComponent(TOKEN_SECRET);
console.log('\nbaseString:: ' + baseString);

const signature = crypto.createHmac('sha256', key)
    .update(baseString)
    .digest('base64');
console.log('\nsignature:: ' + signature);

axios.get('<https://12345-sb1.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder?limit=5>',
    {
        headers: {
            Authorization: `OAuth realm="${ACCOUNT_ID}",oauth_consumer_key="${CONSUMER_KEY}",oauth_token="${TOKEN}",oauth_signature_method="${SIGNATURE_METHOD}",oauth_timestamp="${timestamp}",oauth_nonce="${nonce}",oauth_version="1.0",oauth_signature="${signature}"`,
            Accept: '*/*',
            "Cache-Control": "no-cache",
            Connection: "keep-alive"
        }
    }
).then(response => {
    console.log(response.data);
}).catch(error => {
    console.log(error);
});
🙌 1
v
not sure why you're replying to very old threads