I’ve been working with NetSuite Suite Bundler since 2015. At the time authentication to NetSuite’s API or NetSuite’s RESTlet was accomplished by sending a username and password with each call to NetSuite. If you’re just getting started be sure to read Netsuite API Developer’s Guide first.
NetSuite (I think in 2017) changed this to also allow Access Token based authentication, instead of a raw username/password. As of January 2019, this is now required; the old username/password mechanism has official been deprecated.
Let’s explore how to make API calls with the Access Token based authentication.
What is Token Based Authentication
Token based authentication is a mechanism that allows servers (in this case NetSuite) to not use session based authentication. This means that each request is decoded from a signature that confirms who the user is and what access they have.
Creating the signature is the key to how TBA works. It requires the integration (you in this case) to create an application and generate a consumer key and secret. These are used in conjunction with the access tokens your end user will create. Let’s know look at generating the consumer keys.
Creating an Integration
Inside of NetSuite, you need to create a new integration. This will generate an application ID, consumer key, and consumer private key. Click Setup -> Integrations -> Manage Integrations -> New. In this form you need to populate an integration name and be sure to select the Token-Based Authentication checkbox.
After clicking save, NetSuite will generate the unique keys. You will need to store these somewhere safe. After you leave this page, NetSuite will not show these again. If you lose them, you will need to regenerate them and update all spots where they were used; hopefully a single config setting.
Creating an Access Token
That you have a created an integration, it’s time for your users to create an access token for you integration. Your customer will need to install or update your application to continue.
Direct your user to Setup -> Users/Roles -> Tokens -> New. On this page click New Access Token. If an administrator is creating this token for another user, there are two additional fields: the user and role. If the user is creating it for them self, these fields are not present. Select the application, the user this token is for, the role, and a token name. The token name should most likely reference your application name so it can be easily identified by the user in the future.
Once saved (like the integration page), the user’s access token ID and secret are only shown once. I would suggest having your user enter this into your application for your application to use and reference when making API calls.
Generating the token-based signature
The full sample code is on GitHub. It will require changing the variables populated.
Now for the actually code with all of the leg work out of the way. The signature must be generated for each API call. Each call will generate a unique signature because of the timestamp.
The following fields are used to generate a signature:
- Deployment ID: As far as I can tell this is always 1
- Consumer Key: The key when you created the integration
- Consumer Secret: The other key when you created the integration
- NOnce: A unique identifier for this request
- Signature Method: The encryption method of the signature
- Timestamp: Now in seconds
- Access Token Key: The key provided by your user
- OAuth version: Currently 1.0
- Script ID: Used when performing a RESTlet call
This data is then merged with some URL data:
- HTTP Method: E.g. GET or POST
- NetSuite URL: E.g. the RESTlet endpoint
Here is an example of generating and merging this data together:
[code]
private string GenerateSignature(NetsuiteSignatureParamaters paramaters, int timestamp, string nonce)
{
var rightSide = string.Format(
“deploy={0}&oauth_consumer_key={1}&oauth_nonce={2}&oauth_signature_method={3}&oauth_timestamp={4}&oauth_token={5}&oauth_version={6}&script={7}”,
paramaters.DeploymentId,
paramaters.ConsumerKey,
nonce,
paramaters.SignatureMethod,
timestamp,
paramaters.TokenKey,
“1.0”,
paramaters.ScriptId
);
var baseString = string.Format(
“{0}&{1}&{2}”,
paramaters.HttpMethod,
Uri.EscapeDataString(paramaters.NetsuiteUrl.ToLower()),
Uri.EscapeDataString(rightSide)
);
var signature = Generate(paramaters.ConsumerSecret, paramaters.TokenSecret, baseString);
return Uri.EscapeDataString(signature);
}
private string Generate(string consumerSecret, string tokenSecret, string baseString)
{
var key = string.Format(
“{0}&{1}”,
Uri.EscapeDataString(consumerSecret),
Uri.EscapeDataString(tokenSecret)
);
var signature = CreateSignature(baseString, key);
return signature;
}
private string CreateSignature(string data, string key)
{
// Initialize the keyed hash object using the secret key as the key
HMACSHA1 hashObject = new HMACSHA1(Encoding.UTF8.GetBytes(key));
// Computes the signature by hashing the data with the secret key as the key
byte[] signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(data));
// Base 64 Encode
return Convert.ToBase64String(signature);
}
[/code]
The above code does a few different things:
- Creates a string using the input variables described previously
- Concatenates the signature with the two secret keys: consumer and token
- This data is then encrypted using HMACSHA1
- Converted to a base 64 string
This code leverages a custom class that contains the various fields. It also accepts a previously generated timestamp and nonce. Here is an example of the NetsuiteSignatureParamaters class:
[code]
public class NetsuiteSignatureParamaters
{
public string NetsuiteUrl { get; set; }
public string NetsuiteId { get; set; }
public string ConsumerSecret { get; set; }
public string ConsumerKey { get; set; }
public string TokenSecret { get; set; }
public string TokenKey { get; set; }
public string HttpMethod { get; set; }
public string DeploymentId { get; set; }
public string ScriptId { get; set; }
public string SignatureMethod
{
get
{
return “HMAC-SHA1”;
}
}
}
[/code]
Example of how to generate the timestamp and nonce:
[code]
public int GenerateTimestamp()
{
return ((int)(DateTime.UtcNow – new DateTime(1970, 1, 1)).TotalSeconds);
}
public string GenerateNonce()
{
return Guid.NewGuid().ToString().Replace(“-“, “”);
}
[/code]
The signature is now generated and ready to be compiled into an authentication OAuth string to be sent with the HTTP request:
[code]
private string CreateAuth(NetsuiteSignatureParamaters paramaters, string signature, int timestamp, string nonce)
{
return string.Format(
“OAuth realm=\”{0}\”,oauth_consumer_key=\”{1}\”,oauth_token=\”{2}\”,oauth_signature_method=\”{3}\”,oauth_timestamp=\”{4}\”,oauth_nonce=\”{5}\”,oauth_version=\”1.0\”,oauth_signature=\”{6}\””,
paramaters.NetsuiteId,
paramaters.ConsumerKey,
paramaters.TokenKey,
paramaters.SignatureMethod,
timestamp,
nonce,
signature
);
}
[/code]
This string is then added to HTTP calls:
[code]
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.TryAddWithoutValidation(“Authorization”, [[final oauth string]]);
var result = client.GetAsJsonAsync(url).Result;
if (!result.IsSuccessStatusCode)
{
var content = result.Content.ReadAsStringAsync().Result;
throw new Exception(content);
}
}
[/code]
The code creates an HttpClient and uses the signature generated (replace [[final oauth string]] with the actual string) and makes a get call. The result is checked for success, if it is not successful an error is thrown.
This completes the pieces required to put all of the pieces together. A few assumptions are made about knowing what URL you will be calling and what script will be executed by NetSuite.
The full sample code is on GitHub. It will require changing the variables populated.