Follow the steps below to configure headers for a G Web Application so that it is authorised to access a Web Service configured with security permissions:
1. Create a HTTP Method VI in the LabVIEW Web Service.
- Right-click the Web Resources folder and select New VI.
- For demonstration purposes, a GET HTTP Method VI that returns a JSON string has been created. The JSON string will be accessed by a G Web Application in later steps to verify that the HTTP Method URL works as expected.
- Note: the VI Snippet below does not configure CORS headers by default. This is because the G Web Application will be included in the Public Contents folder of the Web Service and therefore, has the same origin as the Web Service. If your G Web Application has a different origin, enable CORS as described in Enabling CORS for a Web Service.
Note: This image is a LabVIEW snippet, which includes LabVIEW code that you can reuse in your project. To use a snippet, right-click the image, save it to your computer, and drag the file onto your LabVIEW diagram.
2. Configure the HTTP Method VI to use API Keys.
- Right-click the Web Service and select Properties.
- Navigate to the HTTP Method VI Settings tab and select the HTTP Method VI from the table.
- Click the Application Web Server tab from the Web Service VI Properties section.
- Enable the Require API key checkbox, as shown.
- Note: This option requires HTTP requests to be digitally signed with a secret key, as explained here. To check the secret key, right-click the Web Service and select Application Web Server >> Manage Web Server. From the browser window, navigate to the Web Services API Key tab. The secret key is a combination of the Access ID and Secret ID.

3. Set up the G Web Project.
- Launch G Web Development Software and select File >> New >> Project.
- Go to File >> Save All and save the project as "Secure G Web Client.gwebproject".
- From the Project Files tab on the left-hand side, right-click WebApp.gcomp and select New >> VI. Save the VI as "Add Auth Headers.gvi".
- From the Project Files tab, right-click WebApp.gcomp and select New >> JavaScript Library Interface. Save the JavaScript Library Interface (JSLI) as "Encryption Functions.jsli".
- The G Web Project should now consist of the following file hierarchy:

4. Create JavaScript (JS) code to generate the G Web Application headers. JS libraries will be used to create the client signature described under the "Creating the Signature in Client Applications" section of
LabVIEW Web Services Security.
- Open a text editor and paste the code below.
- Save the file as "support_functions.js" to <G Web Project Directory>/WebApp.gcomp/support/
- Note: this JS file consists of three functions, which are used to generate a SHA-256 hash, convert a string to Base64, and implement a MD5 digest respectively.
//function taken from - https://remarkablemark.medium.com/how-to-generate-a-sha-256-hash-with-javascript-d3b2696382fd
function sha256(ascii) {
function rightRotate(value, amount) {
return (value>>>amount) | (value<<(32 - amount));
};
var mathPow = Math.pow;
var maxWord = mathPow(2, 32);
var lengthProperty = 'length'
var i, j; // Used as a counter across the whole file
var result = ''
var words = [];
var asciiBitLength = ascii[lengthProperty]*8;
//* caching results is optional - remove/add slash from front of this line to toggle
// Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes
// (we actually calculate the first 64, but extra values are just ignored)
var hash = sha256.h = sha256.h || [];
// Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes
var k = sha256.k = sha256.k || [];
var primeCounter = k[lengthProperty];
/*/
var hash = [], k = [];
var primeCounter = 0;
//*/
var isComposite = {};
for (var candidate = 2; primeCounter < 64; candidate++) {
if (!isComposite[candidate]) {
for (i = 0; i < 313; i += candidate) {
isComposite[i] = candidate;
}
hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0;
k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0;
}
}
ascii += '\x80' // Append Ƈ' bit (plus zero padding)
while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding
for (i = 0; i < ascii[lengthProperty]; i++) {
j = ascii.charCodeAt(i);
if (j>>8) return; // ASCII check: only accept characters in range 0-255
words[i>>2] |= j << ((3 - i)%4)*8;
}
words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0);
words[words[lengthProperty]] = (asciiBitLength)
// process each chunk
for (j = 0; j < words[lengthProperty];) {
var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration
var oldHash = hash;
// This is now the undefinedworking hash", often labelled as variables a...g
// (we have to truncate as well, otherwise extra entries at the end accumulate
hash = hash.slice(0, 8);
for (i = 0; i < 64; i++) {
var i2 = i + j;
// Expand the message into 64 words
// Used below if
var w15 = w[i - 15], w2 = w[i - 2];
// Iterate
var a = hash[0], e = hash[4];
var temp1 = hash[7]
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1
+ ((e&hash[5])^((~e)&hash[6])) // ch
+ k[i]
// Expand the message schedule if needed
+ (w[i] = (i < 16) ? w[i] : (
w[i - 16]
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0
+ w[i - 7]
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1
)|0
);
// This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0
+ ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj
hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice()
hash[4] = (hash[4] + temp1)|0;
}
for (i = 0; i < 8; i++) {
hash[i] = (hash[i] + oldHash[i])|0;
}
}
for (i = 0; i < 8; i++) {
for (j = 3; j + 1; j--) {
var b = (hash[i]>>(j*8))&255;
result += ((b < 16) ? 0 : '') + b.toString(16);
}
}
return result;
};
//function taken from - https://stackoverflow.com/questions/23190056/hex-to-base64-converter-for-javascript#:~:text=function%20hexToBase64(hexstring)%20%7B%0A%20%20%20%20return%20btoa(hexstring.match(/%5Cw%7B2%7D/g).map(function(a)%20%7B%0A%20%20%20%20%20%20%20%20return%20String.fromCharCode(parseInt(a%2C%2016))%3B%0A%20%20%20%20%7D).join(%22%22))%3B%0A%7D%0A%0AhexToBase64(%22a6b580481008e60df9350de170b7e728%22)%3B
function hexToBase64(hexstring) {
return btoa(hexstring.match(/\w{2}/g).map(function(a) {
return String.fromCharCode(parseInt(a, 16));
}).join(""));
}
//function taken from - https://stackoverflow.com/questions/1655769/fastest-md5-implementation-in-javascript
// A formatted version of a popular md5 implementation.
// Original copyright (c) Paul Johnston & Greg Holt.
// The function itself is now 42 lines long.
function md5(inputString) {
var hc="0123456789abcdef";
function rh(n) {var j,s="";for(j=0;j<=3;j++) s+=hc.charAt((n>>(j*8+4))&0x0F)+hc.charAt((n>>(j*8))&0x0F);return s;}
function ad(x,y) {var l=(x&0xFFFF)+(y&0xFFFF);var m=(x>>16)+(y>>16)+(l>>16);return (m<<16)|(l&0xFFFF);}
function rl(n,c) {return (n<<c)|(n>>>(32-c));}
function cm(q,a,b,x,s,t) {return ad(rl(ad(ad(a,q),ad(x,t)),s),b);}
function ff(a,b,c,d,x,s,t) {return cm((b&c)|((~b)&d),a,b,x,s,t);}
function gg(a,b,c,d,x,s,t) {return cm((b&d)|(c&(~d)),a,b,x,s,t);}
function hh(a,b,c,d,x,s,t) {return cm(b^c^d,a,b,x,s,t);}
function ii(a,b,c,d,x,s,t) {return cm(c^(b|(~d)),a,b,x,s,t);}
function sb(x) {
var i;var nblk=((x.length+8)>>6)+1;var blks=new Array(nblk*16);for(i=0;i<nblk*16;i++) blks[i]=0;
for(i=0;i<x.length;i++) blks[i>>2]|=x.charCodeAt(i)<<((i%4)*8);
blks[i>>2]|=0x80<<((i%4)*8);blks[nblk*16-2]=x.length*8;return blks;
}
var i,x=sb(inputString),a=1732584193,b=-271733879,c=-1732584194,d=271733878,olda,oldb,oldc,oldd;
for(i=0;i<x.length;i+=16) {olda=a;oldb=b;oldc=c;oldd=d;
a=ff(a,b,c,d,x[i+ 0], 7, -680876936);d=ff(d,a,b,c,x[i+ 1],12, -389564586);c=ff(c,d,a,b,x[i+ 2],17, 606105819);
b=ff(b,c,d,a,x[i+ 3],22,-1044525330);a=ff(a,b,c,d,x[i+ 4], 7, -176418897);d=ff(d,a,b,c,x[i+ 5],12, 1200080426);
c=ff(c,d,a,b,x[i+ 6],17,-1473231341);b=ff(b,c,d,a,x[i+ 7],22, -45705983);a=ff(a,b,c,d,x[i+ 8], 7, 1770035416);
d=ff(d,a,b,c,x[i+ 9],12,-1958414417);c=ff(c,d,a,b,x[i+10],17, -42063);b=ff(b,c,d,a,x[i+11],22,-1990404162);
a=ff(a,b,c,d,x[i+12], 7, 1804603682);d=ff(d,a,b,c,x[i+13],12, -40341101);c=ff(c,d,a,b,x[i+14],17,-1502002290);
b=ff(b,c,d,a,x[i+15],22, 1236535329);a=gg(a,b,c,d,x[i+ 1], 5, -165796510);d=gg(d,a,b,c,x[i+ 6], 9,-1069501632);
c=gg(c,d,a,b,x[i+11],14, 643717713);b=gg(b,c,d,a,x[i+ 0],20, -373897302);a=gg(a,b,c,d,x[i+ 5], 5, -701558691);
d=gg(d,a,b,c,x[i+10], 9, 38016083);c=gg(c,d,a,b,x[i+15],14, -660478335);b=gg(b,c,d,a,x[i+ 4],20, -405537848);
a=gg(a,b,c,d,x[i+ 9], 5, 568446438);d=gg(d,a,b,c,x[i+14], 9,-1019803690);c=gg(c,d,a,b,x[i+ 3],14, -187363961);
b=gg(b,c,d,a,x[i+ 8],20, 1163531501);a=gg(a,b,c,d,x[i+13], 5,-1444681467);d=gg(d,a,b,c,x[i+ 2], 9, -51403784);
c=gg(c,d,a,b,x[i+ 7],14, 1735328473);b=gg(b,c,d,a,x[i+12],20,-1926607734);a=hh(a,b,c,d,x[i+ 5], 4, -378558);
d=hh(d,a,b,c,x[i+ 8],11,-2022574463);c=hh(c,d,a,b,x[i+11],16, 1839030562);b=hh(b,c,d,a,x[i+14],23, -35309556);
a=hh(a,b,c,d,x[i+ 1], 4,-1530992060);d=hh(d,a,b,c,x[i+ 4],11, 1272893353);c=hh(c,d,a,b,x[i+ 7],16, -155497632);
b=hh(b,c,d,a,x[i+10],23,-1094730640);a=hh(a,b,c,d,x[i+13], 4, 681279174);d=hh(d,a,b,c,x[i+ 0],11, -358537222);
c=hh(c,d,a,b,x[i+ 3],16, -722521979);b=hh(b,c,d,a,x[i+ 6],23, 76029189);a=hh(a,b,c,d,x[i+ 9], 4, -640364487);
d=hh(d,a,b,c,x[i+12],11, -421815835);c=hh(c,d,a,b,x[i+15],16, 530742520);b=hh(b,c,d,a,x[i+ 2],23, -995338651);
a=ii(a,b,c,d,x[i+ 0], 6, -198630844);d=ii(d,a,b,c,x[i+ 7],10, 1126891415);c=ii(c,d,a,b,x[i+14],15,-1416354905);
b=ii(b,c,d,a,x[i+ 5],21, -57434055);a=ii(a,b,c,d,x[i+12], 6, 1700485571);d=ii(d,a,b,c,x[i+ 3],10,-1894986606);
c=ii(c,d,a,b,x[i+10],15, -1051523);b=ii(b,c,d,a,x[i+ 1],21,-2054922799);a=ii(a,b,c,d,x[i+ 8], 6, 1873313359);
d=ii(d,a,b,c,x[i+15],10, -30611744);c=ii(c,d,a,b,x[i+ 6],15,-1560198380);b=ii(b,c,d,a,x[i+13],21, 1309151649);
a=ii(a,b,c,d,x[i+ 4], 6, -145523070);d=ii(d,a,b,c,x[i+11],10,-1120210379);c=ii(c,d,a,b,x[i+ 2],15, 718787259);
b=ii(b,c,d,a,x[i+ 9],21, -343485551);a=ad(a,olda);b=ad(b,oldb);c=ad(c,oldc);d=ad(d,oldd);
}
return rh(a)+rh(b)+rh(c)+rh(d);
}5. Add the JS functions to the G Web Project.
- Right-click WebApp.gcomp under the Project Files tab and select Add namespace. Call the new folder "support".
- Right-click the "support" folder and click Import Files... Locate the support_functions.js file and import it.
- Double-click the Encryption Functions.jsli file and add the following line to the bottom of the HTML script and link dependencies section.
<script src="support/support_functions.js"></script>
- Save the JSLI file.
- In the JavaScript global input, enter SHA-256 to add the SHA-256 JS function to the JSLI.
- Change the name of the JAVASCRIPT GLOBAL property to exactly match the name of the JS function. In this case, the property should be set to sha256.
- Change the name of the return value to hashHex.
- Click the DATA TYPE field and from the Item tab on the right-hand side, change the data type to String.
- Click Add parameter and specify an input parameter called string with data type String.
- Repeat this process for the remaining JS functions (hexToBase64 and md5), ensuring that the names of the functions match exactly, the return value data types match, and all of the input parameter data types match. Once complete, the JSLI should resemble the following picture.
- Save the JSLI file.

6. Create the G Web Application headers.
- Navigate to the Diagram tab of the Add Auth Headers.gvi project item.
- Right-click on the Block Diagram and select Project Items >> Software >> WebApp >> Encryption Functions >> Md5.
- Place the Md5 function on the Block Diagram and connect a String Constant to the Input String input. Populate this constant with the Secret ID. Note: find the Secret ID by accessing the Web Server Configuration, as shown in step 2.
- Place a Format Date and Time String function on the diagram and connect the following inputs:
- A String Constant with a value of %Y-%m-%d %H:%M:%SZ to the time format string input.
- A True Constant to the UTC format input.
- Place a Concatenate Strings function on the Block Diagram and connect the following inputs:
- A String Control called HTTP Method to the first input.
- A String Control called Requested Resource to the second input.
- The output of the Format Date and Time String function to the third input.
- A String Constant with the Access ID as the fourth input. Note: This can also be found by accessing the Web Server Configuration described in step 2.
- The output of the Md5 function to the fifth input.
- Note: the example code below utilises the default Access ID and Secret ID. Ensure that your Access ID and Secret ID match the ones configured for your Web Server.
Note: This image is a G Web snippet, which includes G Web code that you can reuse in your project. To use a snippet, right-click the image, save it to your computer, and drag the file onto your G Web diagram.
- Place a Case Structure on the Block Diagram.
- Connect a String Control called content-body (optional) on the Conditional Terminal.
- Create a default case called body with the following code:
- The JSLI Md5 function with the content-body (optional) string connected to Input String.
- Place a Concatenate Strings function. Connect the output of the first Concatenate Strings function to the first input of this Concatenate Strings function. Connect the output of the Md5 function to the second input.
- Connect the output of the Concatenate Strings function to the output of the Case Structure.
- Create a String Constant with a value of NIWS2 %s:%s and connect it to the output of the Case Structure.
- Create another case for an empty string:
- In this case, wire the output of the first Concatenate Strings function all the way through the case.
- Place a String Constant with a value of NIWS2 %s:%s to the associated Case Structure output.
- The code should now resemble the snippet below.
Note: This image is a G Web snippet, which includes G Web code that you can reuse in your project. To use a snippet, right-click the image, save it to your computer, and drag the file onto your G Web diagram.
- On the right-hand side of the Case Structure, place the SHA-256 JSLI function. Note: Locate this in the same location as the Md5 function.
- Connect the Concatenated String output from the Case Structure to the string input on SHA-256.
- Place a HexToBase64 function, connecting the hashHex output of SHA-256 to the Hex String input of HexToBase64.
- Place a Format into String function. Connect the following inputs:
- Connect the NIWS2 %s:%s String Constant to the format string input.
- Connect the Access ID String Constant to input 1.
- Connect the Base64 string output of HexToBase64 to input 2.
- The VI should now resemble the image below.
Note: This image is a G Web snippet, which includes G Web code that you can reuse in your project. To use a snippet, right-click the image, save it to your computer, and drag the file onto your G Web diagram.
- From the Data Communications >> Internet >> HTTP >> Utilities Palette, place a Add Header function and connect the following inputs:
- A Control on the client handle input.
- a String Constant on the header input. Set the header to x-ni-date.
- The output of the Format Date Time String function to the value input.
- Place another Add Header function. Connect the following inputs:
- The client handle out from the first Add Header to client handle of this Add Header.
- A String Constant on the header input. Set the header to x-ni-authentication.
- The output of Format into String to the value input.
- The error out from the first Add Header to error in on this Add Header.
- Create an Indicator on the client handle out and error out terminals.
- Navigate to the Icon tab. Connect all of the Controls and Indicators to the input/output terminals as shown.
- The VI is now complete and should appear similar to below.
Note: This image is a G Web snippet, which includes G Web code that you can reuse in your project. To use a snippet, right-click the image, save it to your computer, and drag the file onto your G Web diagram.
7. Create the main G Web Application.
- Double-click the index.gviweb Project Item and navigate to the Diagram tab.
- From the Data Communications >> Internet >> HTTP Palette, place an Open HTTP Handle function.
- From the Project Items >> Software >> WebApp Palette, place the Add Auth Headers sub-VI and connect the following:
- client handle out from Open HTTP Handle to the client handle input of Add Auth Headers.
- A String Constant with a value of GET on the HTTP Method input. Note: Change the value to match the method type of the HTTP Method VI in your LabVIEW Web Service.
- A String Constant with a value of the relative HTTP Method URL on the Requested Resource input. This should resemble /<Web Service Name>/<HTTP Method VI Name>. In this example, the string value is /WebService/Generate_Number. Note: Remember to substitute spaces in the HTTP Method VI name with an underscore.
- Place a While Loop to the right of the Add Auth Headers function. In the While Loop:
- Wire a False Constant to the Stop Condition.
- Place a Wait (Milliseconds) function and wire a Numeric Constant to the input. Set the numeric value to 100.
- From the Data Communications >> Internet >> HTTP Palette, place a GET function.
- Connect a String Constant to the URL input and set it to the name of your HTTP Method VI. Note: If the G Web Application will run from a different origin to the Web Service, set this String Constant to the absolute HTTP Method URL.
- Connect an Indicator to the body output.
- Connect the client handle output from Add Auth Headers to the client handle input of GET. Repeat this with the error wires.
- Ensure that the client handle and error wires are configured as Shift Registers on the While Loop border.
- To the right of the While Loop, place a Close HTTP Handle. Connect the client handle and error wires to the inputs.
- Navigate to the Panel tab.
- From the Unplaced Items Palette, place the body String Indicator and resize to your liking. Note: This Panel is how the G Web Application will appear when accessed from a web browser.
- The VI is now complete and should resemble the following snippet.
Note: This image is a G Web snippet, which includes G Web code that you can reuse in your project. To use a snippet, right-click the image, save it to your computer, and drag the file onto your G Web diagram.
8. Build the G Web Project.
- Double-click on WebApp.gcomp from the Project Files tree.
- Ensure that index.gviweb is set as the Top-level VI and all other files are set as Always include.
- Click the hammer icon from the top toolbar to build the project.
- When the build is complete, click on the Output directory hyperlink, as shown.

9. Include the G Web Application in the LabVIEW Web Service.
- From the LabVIEW Project Tree, right-click the Web Service and select Add Public Content Folder...
- Navigate to the G Web build output directory. The Public Content folder will auto-populate with the build files.
10. Publish and test the Web Service.
- Right-click the Web Service and select Application Web Server >> Publish.
- Once published, right click on the index.html file inside the Public Content folder and select Show Public URL...
- Copy the URL and paste it into a supported browser, like Google Chrome.
- If the G Web Application headers have been configured correctly, the correct JSON string will appear similarly to the image below.
Additional Information
Any client that accesses the HTTP Method URL must consist of the same headers that have been configured in the G Web Application. Without these, the client will not be authorised to access the URL and the browser will return
error 403: Forbidden Access.