hello, I have a map/reduce script that is talking ...
# suitescript
k
hello, I have a map/reduce script that is talking to an external API using Basic Authentication, currently I have hardcoded the authentication and the url-path in the script for testing. Is there any resource that I can look into to make this more secure and dynamic?
b
the more secure option is Secrets Management
the dynamic options are Script Parameters
or to use a custom record for a lookup
k
thank you! if I setup a secret in API secrets, how can I call them in the script?
b
k
thank you so much!
b
the basics are that the guid/secret id acts as a placeholder in an expression which netsuite replaces with your secret when it does the https request
it does not handle mixed content well, I advise you do the base 64 encoding of the username and password beforehand and store that in the secret
k
sorry for the silly question! I am very new to this. Is base 64 encoding of username and password something that I would pass on the
headers
like this
Basic thisismybase64
?
b
my advice was to do 'Basic {custsecret_my_secret}'
and store the based 64ed username and password in the secret
k
got it! I was just making sure I understood what base 64ed value is. it is basically the value generated by softwares like postman when you pass login and password!
m
I'm trying to start using secrets as well and I'm struggling with this exact scenario - basic authentication in the headers. @battk, are you saying that if my secret's id is
custsecret_my_secret
, the header should literally include that as a string in brackets like so:
{Authorization:'Basic {custsecret_my_secret}'}
? If it makes a difference, this is for a get request using
https.get()
, and I'm passing that object in as the headers option.
b
what the code look like
m
Here's the currently running code that works:
Copy code
var apiToken = 'API_TOKEN_GOES_HERE';
var apiTokenBase64 = encode.convert({
	'string':apiToken + ':api_token',
	'inputEncoding':encode.Encoding.UTF_8,
	'outputEncoding':encode.Encoding.BASE_64
});
var apiResponse = https.get({
	'url':'URL_GOES_HERE',
	'headers':{Authorization:'Basic ' + apiTokenBase64}
});
And here's what I'm trying that's not working:
Copy code
var apiResponse = https.get({
	'url':'URL_GOES_HERE',
	'headers':{Authorization:'Basic {custsecret_my_secret}'}
});
Where the secret with the id
custsecret_my_secret
is a base 64 encoded string from
API_TOKEN_GOES_HERE:api_token
.
b
Use the credentials parameter
m
Is that available in
https.get()
? The only options I see documented are
url
and
headers
(https://system.netsuite.com/app/help/helpcenter.nl?fid=section_4567631366.html). If it is available in
https.get()
, what would I include at that point? Useful examples in the documentation are rather sparse...
b
it exists on https.get
if you are paranoid, you can use https.request, where its documented
either way, its used the same way as in the Credential Field Sample, except that you are using the script id of the secret instead of a guid
m
Ah got it, thanks. I'll have to play around with that. The documentation for both
<http://https.post|https.post>
and
https.request
only mention that
credentials
does the replacement in the body - does it also work for headers despite the explicit mention?
b
its required for the headers and the body
weirdly enough, not in the url
m
Interesting. Thanks for your help, hopefully I can get it working from here!
n
hi @MTNathan are you able to make the secret works? interestingly it works on my post, but when on get, im getting 401 valid authentication required.
@MTNathan this one is not working var secureToken = https.createSecureString({ // secureToken is an https.SecureString input: '{' + 'custsecret_api_key' + '}' }); var headers = { "Authorization": "Basic " + secureToken, "content-type": "application/json" }; but when i replace the secureToken with the hardcoded value of the actual string.. it works fine 😑
m
@nsdev0001 I haven't had a chance to try it yet but I'm hoping to in the next hour or so - I'll update here again with my findings.
👍 1
n
@MTNathan thanks, i'd be interested to compare notes. interestingly, this works in one of my script also, i have a different secret (not base 64 encoded,) and I am also using it on a different script and it works
var secureToken = https.createSecureString({
input: '{' + 'custsecret_connector_pd' + '}'
});
var bodydata = {
"grant_type": "client_credentials",
"client_id": clientId,
"client_secret": secureToken
};
var response = <http://https.post|https.post>({
url: procoreAuthorizeURL,
body: bodydata
});
m
I haven't been able to get it to work after a number of different iterations. The basic idea of what I'm trying looks like this:
Copy code
var secureString = https.createSecureString({input:'{custsecret_my_secret}'});
var apiResponse = https.get({
	url:'URL_GOES_HERE',
	credentials:['{custsecret_my_secret}'],
	headers:{Authorization:'Basic ' + secureString}
});
Where the secret with id
custsecret_my_secret
is the base64 encoded value of
API_TOKEN_GOES_HERE:api_token
. I've also tried it without the braces in the
credentials
option, and with encoding specified as
https.Encoding.BASE_64
in the
https.createSecureString()
call, none of those worked. I'm at a loss for what else to try, I'm open to any suggestions you may have based on what I've tried so far.
n
var secureToken = https.createSecureString({ input: '{' + 'custsecret_connector_pd' + '}' }); var bodydata = { "grant_type": "client_credentials", "client_id": clientId, "client_secret": secureToken }; var response = https.post({ url: procoreAuthorizeURL, body: bodydata }); This one i have works,,
the other one that is not working for me is when i have the api secret on header, and that value is base64 encoded. 😞 the secret is a combination of api key, api pin in base64 so i converted these first and store it in the api secret record..
m
That sounds almost exactly like my scenario - base64 encoded key/pin in the secret, trying to use it in headers (as opposed to body) and it's not working. @battk, do you by chance have any working examples of using secrets for header-based basic authentication? The few NetSuite documentation examples I can find are all too many steps away from that scenario to seem to be of much use for adapting.
👍 1
n
yes same scenario @MTNathan and not too many documentation..i will do some digging.. i will test out with secret on header but not encoded.. will see if that works.
👍 1
b
The output of
Copy code
'Basic ' + secureString
is not a secure string
its a string
notably it will be a string without any template expressions in it
n
hi @battk what would you recommend for us to do? sorry i ran out of idea on what else i need to do.. literally stuck.
b
easiest way to debug with the https module is an echo service
I like using www.httpbin.org
i personally use a suitelet
Copy code
/**
 * @NApiVersion 2.x
 * @NScriptType Suitelet
 * @NModuleScope SameAccount
 */
define(["N/crypto", "N/encode", "N/https", "N/sftp", "N/file"], function (
  crypto,
  encode,
  https,
  sftp,
  file
) {
  return {
    onRequest: function doSecret(context) {
      var secureToken = https.createSecureString({
        input: "{" + "custsecret_connector_pd" + "}",
      });
      var clientId = "iBlameKnotKnickForLearningInDmInsteadOfPublic";

      var bodydata = {
        grant_type: "client_credentials",
        client_id: clientId,
        client_secret: secureToken,
      };

      var response = <http://https.post|https.post>({
        url: "<https://www.httpbin.org/post>",
        body: bodydata,
      });

      context.response.write(response.body);
    },
  };
});
make that suitelet and check that the response's output matches what you want when you do a get on the deployment's url
m
Ah, I think I see what I was missing now - if I'm following correctly, the secret should be the entirety of
Basic BASE_64_ENCODED_DATA_HERE
and the rest should look like this:
Copy code
var secureString = https.createSecureString({input:'{custsecret_my_secret}'});
var apiResponse = https.get({
	url:'URL_GOES_HERE',
	credentials:['custsecret_my_secret'],
	headers:{Authorization:secureString}
});
Does that align with what you'd expect? It seems to be working for me now but that was admittedly a very quick and dirty test.
b
that will work fine
keep in mind that the input of secure string is a template
m
Great, thanks for confirming. Upon further testing it seems that the
credentials
option isn't necessary in the get call at that point.
b
you can place the string 'Basic ' in the template itself
its slightly prettier
m
Oh interesting, I do like that better, thanks
b
somewhat confusing here is that there are 2 ways of using expressions
m
@nsdev0001 Confirming that this works for me, where the secret is just the base64 encoded `key:pin`:
Copy code
var secureString = https.createSecureString({input:'Basic {custsecret_my_secret}'});
var apiResponse = https.get({
	url:'URL_GOES_HERE',
	headers:{Authorization:secureString}
});
Hopefully you can get it from there as well.
👍 1
b
one is a normal string:
Copy code
"Basic {custsecret_connector_pd}"
the other is a secure string object:
Copy code
var secureToken = https.createSecureString({
  input: "Basic {custsecret_connector_pd}",
});
there are different scenarios where you have to use the credentials parameters and when you dont
I don't memorize them, I just always use the credentials parameter
the reason I tell people to do the base 64 encoding beforehand is that the option to do the encoding: SecureString.convertEncoding works on the entire input
i havent found a way to make it only encode the username:password part and not the Basic part
m
Ah interesting, those might be worth playing around with. If I didn't encode beforehand, I'd probably opt to use the
N/encode
module on just the piece that needs to be encoded rather than rely on the
SecureString
methods, but storing the encoded string in the secret seems to be the most straightforward option.
👍 1
Thanks for all of your insight!
b
the input to N/encode is supposed to be a string
you can't use a secure string with it
and if you pass your template to it, the encoded output is no longer a template
m
Oh right, so I guess that forces my hand on that one
n
thanks @MTNathan and @battk works for me when i added the 'Basic ' on the API secret record