TOTP (Time based One Time Password)

General Add comments

Make use of one-time passwords for several two-factor authentication systems.

A couple of months ago, I was working for a customer on a ServiceNow implementation where I had to configure an interface to APIGee. This interface consists of an REST and oAUth2.0. This can be done easily within ServiceNow.

For more info about setting up an Outbound REST web service:

So, everything was working fine, until the customer contacted me with some panic in his voice… “the interface user has to be connected via a two-factor authentication with Google Authenticator. Like a normal interactive user session will use.” When I heard this, it felt a little overcomplicated for an interface.

Google Authenticator
Is a service that you can use to enable two-factor authentication for your users. When a user logs in with username and password, another extra password should be entered from the Google authenticator application. As you can see for example in the picture below for a WordPress website:

The generation of the password is using the Time-based One-time Password Algorithm.
To generate the correct password the algorithm needs a shared/secret key. This shared key is setup between the provided (in this case APIGee) and the device (normal the Google Authenticator app on your phone, but for now the ServiceNow instance). This shared key is known by APIGee and you as user. Based on this shared key you can generate the one-time password via the algorithm. More info:

Example of setting up two-factor authentication with the shared key:

The solution
Since ServiceNow is not providing such service for outbound messages I had to figure it out for myself and the customer of course. I created a Script Include that can generate the password simply based on the shared key and the current date-time.
The script include has a large script to enable SHA encryption since this was not available OOB in ServiceNow. Once that was setup the rest of the code was not that hard to create. I did not reinvent the wheel and was able to find some basic example code on the internet 😉 So the Script Include looks like this:

var TOTP = Class.create();
TOTP.prototype = {
initialize: function(secret) {
this.secret = secret;
dec2hex: function(s) {
return (s < 15.5 ? "0" : "") + Math.round(s).toString(16); }, hex2dec: function(s) { return parseInt(s, 16); }, leftpad: function(s, l, p) { if(l + 1 >= s.length) {
s = Array(l + 1 - s.length).join(p) + s;
return s;
base32tohex: function(base32) {
var base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
var bits = "";
var hex = "";
for(var i = 0; i < base32.length; i++) { var val = base32chars.indexOf(base32.charAt(i).toUpperCase()); bits += this.leftpad(val.toString(2), 5, '0'); } for(var i = 0; i + 4 <= bits.length; i+=4) { var chunk = bits.substr(i, 4); hex = hex + parseInt(chunk, 2).toString(16) ; } return hex; }, getOTP: function() { try { var epoch = Math.round(new Date().getTime() / 1000.0); var time = this.leftpad(this.dec2hex(Math.floor(epoch / 30)), 16, "0"); var hmacObj = new jsSHA(time, "HEX"); var hmac = hmacObj.getHMAC(this.base32tohex(this.secret), "HEX", "SHA-1", "HEX"); var offset = this.hex2dec(hmac.substring(hmac.length - 1)); var otp = (this.hex2dec(hmac.substr(offset * 2, 8)) & this.hex2dec("7fffffff")) + ""; otp = (otp).substr(otp.length - 6, 6); } catch (error) { throw error; } return otp; }, type: 'TOTP' };

In my scenario, I had a script for the outbound message where I call this Script Include to get the TOTP password before the REST call is executed. Example:

// Get the Google Authenticator password
var totpObj = new TOTP("Shared secret");
var otp = totpObj.getOTP();

sm = new sn_ws.RESTMessageV2();
sm.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
sm.setRequestHeader("Accept", "application/json;charset=utf-8");
sm.setRequestHeader("Authorization", "Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0");
sm.setQueryParameter("mfa_token", otp.toString());
sm.setQueryParameter("username", "username");
sm.setQueryParameter("password", "password");
sm.setQueryParameter("grant_type", "password");
response = sm.execute();

If you want a copy of my Script Include contact me at .img[at].img
Have nice day!

Leave a Reply