View Jitterbit Developer Portal
- Created by Lisa Brown, last modified on Jun 08, 2020
Introduction
This page describes how to use NetSuite token-based authentication (TBA) with a web service method in Jitterbit Harmony. Prior to configuring TBA in Jitterbit Harmony, see Jitterbit's recommendations for enabling TBA in NetSuite.
Using a web service method is one of several ways to use NetSuite TBA with Harmony. Other options include using the NetSuite Connector (recommended), calling a RESTlet from an HTTP source or target, or calling a RESTlet from a local file source:
- Using NetSuite TBA in the NetSuite Connector
- Using NetSuite TBA by Calling a RESTlet from an HTTP Source or Target
- Using NetSuite TBA by Calling a RESTlet from a Local File Source
While the NetSuite Connector is recommended, it may be preferred to use a web service method or call the RESTlet in some cases, such as for existing integrations that are already designed in this way, or where certain features are not available with the connector. No matter the method you use, authenticating using TBA is available in all of these.
To use NetSuite TBA with a web service method, these steps are recommended, as covered on this page:
- Get Data Center URLs in a Web Service Call
- Set TBA Variables Using Scripts
- Use NetSuite TBA in a Web Service Call
Prerequisites
To use NetSuite TBA with a web service method in Jitterbit Harmony, these criteria must be met:
- You must have the appropriate permissions role on your NetSuite account.
- You must be using SuiteTalk version 2015.2 or higher.
- You must have TBA enabled for your NetSuite account.
- You must be using Jitterbit Harmony Design Studio version 8.24.2 or higher and Agent versions 8.24 or higher.
TIP: For additional information, refer to the NetSuite documentation on Getting Started with Token-based Authentication (login to NetSuite required).
Get Data Center URLs in a Web Service Call
Before you are able to connect to your NetSuite account via Jitterbit Harmony, you must know the NetSuite data center URL being used for your account. One way to obtain this is to call the getDataCenterUrls
method provided by NetSuite.
- First, set up a web service method using the wizard as normal, providing these parameters during configuration:
In the Name & WSDL step, specify the NetSuite WSDL version (must be at least 2015.2 to use TBA):
https://webservices.netsuite.com/wsdl/v2020_1_0/netsuite.wsdl
(supported with agents version 10.18 or higher)https://webservices.netsuite.com/wsdl/v2019_2_0/netsuite.wsdl
(supported with agents version 10.4 or higher)https://webservices.netsuite.com/wsdl/v2019_1_0/netsuite.wsdl
(supported with agents version 10.4 or higher)https://webservices.netsuite.com/wsdl/v2018_2_0/netsuite.wsdl
(WSDL versions 2018.2 and lower are supported with agents version 9.9 or higher)https://webservices.netsuite.com/wsdl/v2018_1_0/netsuite.wsdl
https://webservices.netsuite.com/wsdl/v2017_2_0/netsuite.wsdl
https://webservices.netsuite.com/wsdl/v2017_1_0/netsuite.wsdl
https://webservices.netsuite.com/wsdl/v2016_2_0/netsuite.wsdl
https://webservices.netsuite.com/wsdl/v2016_1_0/netsuite.wsdl
https://webservices.netsuite.com/wsdl/v2015_2_0/netsuite.wsdl
In the Operation step, select the
getDataCenterUrls
operation:- In the Details step, the Web Service URL from the WSDL and Soap Action
getDataCenterUrls
are listed.
- Now that the web service method is created, use it in a web service call operation.
Next, create the request transformation, which contains the structure of data you are requesting from NetSuite. A source is not required. On the target side, add a Jitterbit Script on the NetSuite
account
field that passes in your NetSuite account ID (no authorization required). This is used to fetch the URLs associated with the provided account ID.Using the sample Jitterpak with a previously defined project variable for the NetSuite account, the contents of the script on the
account
field should include:<trans> $jb.netsuite.account </trans>
- In preparation for mapping NetSuite's response, next define a simple text file format to be used to write the response to. In the sample Jitterpak, the file format is named
script
and contains a single string field calledscript
. Next, create the response transformation, which contains the structure of data being returned from NetSuite. Specify a target of Text, and select the file format created in the previous step. Then map the
webservicesDomain
source field to a global variable within the text target field.The contents of the Jitterbit Script should include:
<trans> $jb.netsuite.dataCenterUrls.webservicesDomain = root$transaction.response$body$getDataCenterUrlsResponse$getDataCenterUrlsResult$dataCenterUrls$webservicesDomain$ </trans>
- This part of the project is complete. You now have the NetSuite data center URL for your account written to a variable called
jb.netsuite.dataCenterUrls.webservicesDomain
that you can then use in subsequent operations.
Use Scripts to Set TBA Variables
You will also need to create several scripts that set the variables that will be used for TBA with NetSuite. The final script will be used within the operation created in the next step, Use NetSuite TBA in a Web Service Call.
First, create a new JavaScript. This JavaScript will use crypto standards to generate the NetSuite signature in HMAC-SHA1 format. The script must be JavaScript, not Jitterbit Script.
Expand the code below and paste the contents into the JavaScript. In the sample Jitterpak, this script is named
jb.netsuite.tokenPassword.signature
.jb.netsuite.tokenPassword.signature Expand source/* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ var CryptoJS = CryptoJS || function (g, l) { var e = {}, d = e.lib = {}, m = function () {}, k = d.Base = { extend: function (a) { m.prototype = this; var c = new m; a && c.mixIn(a); c.hasOwnProperty("init") || (c.init = function () { c.$super.init.apply(this, arguments) }); c.init.prototype = c; c.$super = this; return c }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () {}, mixIn: function (a) { for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } }, p = d.WordArray = k.extend({ init: function (a, c) { a = this.words = a || []; this.sigBytes = c != l ? c : 4 * a.length }, toString: function (a) { return (a || n).stringify(this) }, concat: function (a) { var c = this.words, q = a.words, f = this.sigBytes; a = a.sigBytes; this.clamp(); if (f % 4) for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 24 - 8 * ((f + b) % 4); else if (65535 < q.length) for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2]; else c.push.apply(c, q); this.sigBytes += a; return this }, clamp: function () { var a = this.words, c = this.sigBytes; a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4); a.length = g.ceil(c / 4) }, clone: function () { var a = k.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0); return new p.init(c, a) } }), b = e.enc = {}, n = b.Hex = { stringify: function (a) { var c = a.words; a = a.sigBytes; for (var b = [], f = 0; f < a; f++) { var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255; b.push((d >>> 4).toString(16)); b.push((d & 15).toString(16)) } return b.join("") }, parse: function (a) { for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 2), 16) << 24 - 4 * (f % 8); return new p.init(b, c / 2) } }, j = b.Latin1 = { stringify: function (a) { var c = a.words; a = a.sigBytes; for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255)); return b.join("") }, parse: function (a) { for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4); return new p.init(b, c) } }, h = b.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(j.stringify(a))) } catch (c) { throw Error("Malformed UTF-8 data"); } }, parse: function (a) { return j.parse(unescape(encodeURIComponent(a))) } }, r = d.BufferedBlockAlgorithm = k.extend({ reset: function () { this._data = new p.init; this._nDataBytes = 0 }, _append: function (a) { "string" == typeof a && (a = h.parse(a)); this._data.concat(a); this._nDataBytes += a.sigBytes }, _process: function (a) { var c = this._data, b = c.words, f = c.sigBytes, d = this.blockSize, e = f / (4 * d), e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0); a = e * d; f = g.min(4 * a, f); if (a) { for (var k = 0; k < a; k += d) this._doProcessBlock(b, k); k = b.splice(0, a); c.sigBytes -= f } return new p.init(k, f) }, clone: function () { var a = k.clone.call(this); a._data = this._data.clone(); return a }, _minBufferSize: 0 }); d.Hasher = r.extend({ cfg: k.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { r.reset.call(this); this._doReset() }, update: function (a) { this._append(a); this._process(); return this }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, blockSize: 16, _createHelper: function (a) { return function (b, d) { return (new a.init(d)).finalize(b) } }, _createHmacHelper: function (a) { return function (b, d) { return (new s.HMAC.init(a, d)).finalize(b) } } }); var s = e.algo = {}; return e } (Math); (function () { var g = CryptoJS, l = g.lib, e = l.WordArray, d = l.Hasher, m = [], l = g.algo.SHA1 = d.extend({ _doReset: function () { this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]) }, _doProcessBlock: function (d, e) { for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > a; a++) { if (16 > a) m[a] = d[e + a] | 0; else { var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16]; m[a] = c << 1 | c >>> 31 } c = (n << 5 | n >>> 27) + l + m[a]; c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ h ^ g) - 899497514); l = g; g = h; h = j << 30 | j >>> 2; j = n; n = c } b[0] = b[0] + n | 0; b[1] = b[1] + j | 0; b[2] = b[2] + h | 0; b[3] = b[3] + g | 0; b[4] = b[4] + l | 0 }, _doFinalize: function () { var d = this._data, e = d.words, b = 8 * this._nDataBytes, g = 8 * d.sigBytes; e[g >>> 5] |= 128 << 24 - g % 32; e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296); e[(g + 64 >>> 9 << 4) + 15] = b; d.sigBytes = 4 * e.length; this._process(); return this._hash }, clone: function () { var e = d.clone.call(this); e._hash = this._hash.clone(); return e } }); g.SHA1 = d._createHelper(l); g.HmacSHA1 = d._createHmacHelper(l) })(); (function () { var g = CryptoJS, l = g.enc.Utf8; g.algo.HMAC = g.lib.Base.extend({ init: function (e, d) { e = this._hasher = new e.init; "string" == typeof d && (d = l.parse(d)); var g = e.blockSize, k = 4 * g; d.sigBytes > k && (d = e.finalize(d)); d.clamp(); for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486; p.sigBytes = b.sigBytes = k; this.reset() }, reset: function () { var e = this._hasher; e.reset(); e.update(this._iKey) }, update: function (e) { this._hasher.update(e); return this }, finalize: function (e) { var d = this._hasher; e = d.finalize(e); d.reset(); return d.finalize(this._oKey.clone().concat(e)) } }) })(); /* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var WordArray = C_lib.WordArray; var C_enc = C.enc; /** * Base64 encoding strategy. */ var Base64 = C_enc.Base64 = { /** * Converts a word array to a Base64 string. * * @param {WordArray} wordArray The word array. * * @return {string} The Base64 string. * * @static * * @example * * var base64String = CryptoJS.enc.Base64.stringify(wordArray); */ stringify: function (wordArray) { // Shortcuts var words = wordArray.words; var sigBytes = wordArray.sigBytes; var map = this._map; // Clamp excess bits wordArray.clamp(); // Convert var base64Chars = []; for (var i = 0; i < sigBytes; i += 3) { var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; var triplet = (byte1 << 16) | (byte2 << 8) | byte3; for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); } } // Add padding var paddingChar = map.charAt(64); if (paddingChar) { while (base64Chars.length % 4) { base64Chars.push(paddingChar); } } return base64Chars.join(''); }, /** * Converts a Base64 string to a word array. * * @param {string} base64Str The Base64 string. * * @return {WordArray} The word array. * * @static * * @example * * var wordArray = CryptoJS.enc.Base64.parse(base64String); */ parse: function (base64Str) { // Shortcuts var base64StrLength = base64Str.length; var map = this._map; // Ignore padding var paddingChar = map.charAt(64); if (paddingChar) { var paddingIndex = base64Str.indexOf(paddingChar); if (paddingIndex != -1) { base64StrLength = paddingIndex; } } // Convert var words = []; var nBytes = 0; for (var i = 0; i < base64StrLength; i++) { if (i % 4) { var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2); var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2); words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); nBytes++; } } return WordArray.create(words, nBytes); }, _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' }; }()); SetScriptResult(CryptoJS.HmacSHA1($jb_netsuite_tokenPassword_baseString, $jb_netsuite_tokenPassword_key).toString(CryptoJS.enc.Base64))
The resulting output of this script will return a value to be used for the
jb.netsuite.tokenPassword.signature
.Create a Jitterbit Script that uses several additional project variables predefined in the sample Jitterpak (see Enabling TBA in NetSuite to obtain) and and retrieves the signature from the JavaScript created above. In the sample Jitterpak, this script is named
jb.netsuite.authentication
.jb.netsuite.authentication<trans> $jb.netsuite.tokenPassword.nounce=Guid(); // Gets nonce $jb.netsuite.tokenPassword.timestamp = long(now()); // Gets Unix timestamp $jb_netsuite_tokenPassword_baseString = $jb.netsuite.account+'&'+$jb.netsuite.tokenPassword.consumerKey+'&'+$jb.netsuite.tokenPassword.token+'&'+$jb.netsuite.tokenPassword.nounce+'&'+$jb.netsuite.tokenPassword.timestamp; $jb_netsuite_tokenPassword_key = $jb.netsuite.tokenPassword.consumerSecret+'&'+$jb.netsuite.tokenPassword.tokenSecret; $jb.netsuite.tokenPassword.signature = RunScript("<TAG>Scripts/jitterbit/netsuite/jb.netsuite.tokenPassword.signature</TAG>"); // Executes script to get signature </trans>
This script will be used to authenticate with NetSuite within the next part of the project, Use NetSuite TBA in a Web Service Call.
Use NetSuite TBA in a Web Service Call
Before starting this step, you should already know the data center URL (one method for obtaining is described in Get Data Center URLs in a Web Service Call) and have set up the variables to use for TBA (described in Use Scripts to Set TBA Variables).
Next, you can use this information to create a web service method to authenticate with NetSuite using TBA.
- Create a web service method providing these parameters during configuration:
In the Name & WSDL step, specify the NetSuite WSDL version (must be at least 2015.2 to use TBA).
In the Operation step, select the operation you want to use. The sample Jitterpak uses
get
to return a single record as an example.In the Details step, manually edit the Web Service URL to use the global variable containing your data center URL. In the sample Jitterpak, this was set in a global variable during Get Data Center URLs in a Web Service Call. For example:
[jb.netsuite.dataCenterUrls.webservicesDomain{https://webservices.netsuite.com}]/services/NetSuitePort_2018_1
NOTE: If you skipped the step Get Data Center URLs in a Web Service Call because you already know your data center URL, just include it within the beginning of the URL here.
- Now that the web service method is created, use it in a web service call operation.
- In the operation, insert a script before the request transformation, using the Jitterbit Script created in Use Scripts to Set TBA Variables. In the sample Jitterpak, this is the script named
jb.netsuite.authentication
. Next, create the request transformation for your specific needs depending on your SOAP action. The sample Jitterpak does not use a source. On the target side, add Jitterbit Scripts to map to the target fields under the
header
node intokenPassport
andsignature
using the variables set in Use Scripts to Set TBA Variables.Target Field Sample Jitterpak Mapping account
$jb.netsuite.account
consumerKey
$jb.netsuite.tokenPassword.consumerKey
token
$jb.netsuite.tokenPassword.token
nonce
$jb.netsuite.tokenPassword.nounce
timestamp
$jb.netsuite.tokenPassword.timestamp
signature
$jb.netsuite.tokenPassword.signature
algorithm
"HMAC-SHA1"
Also map any other fields you want to use in your specific web service operation and finish your request
body
as normal. The sample Jitterpak is getting a record based off theRecordRef
as an example.If you created an operation for getting the data center URLs (as described in Get Data Center URLs in a Web Service Call), link the operation you have just created to run upon success of the first operation. In the sample Jitterpak, for example:
NOTE: The operation design above is for illustrative purposes only. In an actual implementation, it is suggested to call thegetDataCenterURLs
once and store the value in a project variable or use Jitterbit's caching functions in order to reduce network traffic.- You have now used TBA to authenticate with NetSuite via a web service method. Each SOAP service call to NetSuite must include the TBA parameters in the SOAP headers, so repeat these steps for any additional web service calls.
Simplified for Instructions
Advanced Template with Best Practices
Last updated: Jun 08, 2020