Skip to end of metadata
Go to start of metadata

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.

NOTE: Those using two-factor authentication (2FA or TFA) in NetSuite are required to use TBA with Jitterbit Harmony. Learn more in NetSuite 2018.2 Token-Based Authentication.

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:

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:

  1. Get Data Center URLs in a Web Service Call
  2. Set TBA Variables Using Scripts
  3. Use NetSuite TBA in a Web Service Call
TIP: For purposes of this documentation, the steps on this page are simplified and correspond with the sample Jitterpak jb.netsuite.simplified (JPK). For a more advanced template that incorporates Jitterbit best practices, refer to the Jitterpak jb.netsuite (JPK).

Prerequisites

To use NetSuite TBA with a web service method in Jitterbit Harmony, these criteria must be met:

  1. You must have the appropriate permissions role on your NetSuite account.
  2. You must be using SuiteTalk version 2015.2 or higher.
  3. You must have TBA enabled for your NetSuite account.
  4. 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.

NOTE: If you already know your data center URL, you can skip this part. The data center URL will be required in Use NetSuite TBA in a Web Service Call.
TIP: More information is available in NetSuite's documentation on getDataCenterUrls (login to NetSuite required).
  1. First, set up a web service method using the wizard as normal, providing these parameters during configuration:
    1. In the Name & WSDL step, specify the NetSuite WSDL version (must be at least 2015.2 to use TBA):

    2. In the Operation step, select the getDataCenterUrls operation:

    3. In the Details step, the Web Service URL from the WSDL and Soap Action getDataCenterUrls are listed.
  2. Now that the web service method is created, use it in a web service call operation.
  3. 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>
  4. 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 called script.
  5. 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>
  6. 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.

  1. 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.

  2. 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
    /*
    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))
  3. The resulting output of this script will return a value to be used for the jb.netsuite.tokenPassword.signature.

  4. 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>
  5. 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.

  1. Create a web service method providing these parameters during configuration:
    1. In the Name & WSDL step, specify the NetSuite WSDL version (must be at least 2015.2 to use TBA).

    2. In the Operation step, select the operation you want to use. The sample Jitterpak uses get to return a single record as an example.

    3. 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.
  2. Now that the web service method is created, use it in a web service call operation.
  3. 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.
  4. 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 in tokenPassport and signature using the variables set in Use Scripts to Set TBA Variables.

    Target FieldSample 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 the RecordRef as an example.

     

  5. 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 the getDataCenterURLs once and store the value in a project variable or use Jitterbit's caching functions in order to reduce network traffic.
  6. 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.
Jitterpaks

Simplified for Instructions

Advanced Template with Best Practices

On This Page

Last updated:  Nov 08, 2018