Hackernoon logoAdaptive Authentication with Function Libraries in WSO2 Identity Server by@anuradhak

Adaptive Authentication with Function Libraries in WSO2 Identity Server

Author profile picture

@anuradhakAnuradha Karunarathna

What is Adaptive Authentication ??

Adaptive authentication is an evolved form of multi factor authentication (MFA) where the authentication mechanism is decided depending on the the user’s risk profile and the behavior.
You can understand this concept in detail by following “Adaptive Authentication: Why? What? How?” .
WSO2 Identity Server supports script-based adaptive authentication since WSO2-IS 5.7.0 adding numerous advantages to users.

Why you should select WSO2 IS for adaptive authentication?

  1. Ability to write complex authentication policies in a simple manner using embedded script editor in management console. It removes the barriers enforced by traditional UI tools to generate adaptive authentication flows.
  2. Shipping a set of well written templates on use cases such as role-based, user-age-based, tenant-based, user store based, IP-based, new device based, risk-based by default with the product. It makes the identity admin’s life comfortable and easier.
  3. Open and future-proof adaptive authentication platform which provides more value for less cost.
  4. Designed in such as a way to quickly integrate with risk engines and external systems.
NOT only that !!! WSO2 IS introduced function library support for
adaptive authentication to handle adaptive authentication scripts in a
hassle free manner.

Advantages of function library support for adaptive authentication

  1. The common Java script functions which are useful for adaptive authentication scripts can be stored as collections.
  2. Enhance the re-usability of common functions.
  3. Make adaptive authentication scripts more clear by importing function
    libraries and using their functions instead of writing everything in the same script.
  4. Make the users’ life easier by reducing the effort to modify and maintain scripts.
  5. Mechanism of importing libraries to adaptive authentication scripts is developer friendly as it uses a similar way of
    require()
    in NodeJS.

How to use function libraries ?

Let’s use management console to add new function library. We are going to modify the “IP-Based” template which already contains two java script functions inside the authentication script itself. (convertIpToLong and isCorporateIP functions are the two common JS functions.). Let’s move them to one module and make the authentication script more cleaner.
Add Function Library [details]
1. Sign in to in to the Management Console.
2. On the Main menu, click ManageFunction LibrariesAdd.
3. Fill in the Function Library Name, provide a brief Description (optional) and write the Function Library Script for the function library. Then click Register.
4. If the script doesn’t have any compilation errors, it will be sucessfully registerd.
.js
extension is added to the name you specified. You need to use that
.js
extension when importing a function library. (Can see later)

How to write function library scripts ?

There are three main methods:
Option 01: Add individual functions and export all individually. (Even though some functions are only invoked inside the same function library, those functions should be exported. Further, use
this.<function>
as similar to
this.convertIpToLong
usage inside
isCorporateIP 
function).
// Function to convert ip address string to long value
var convertIpToLong = function(ip) {
    var components = ip.split('.');
    if (components) {
        var ipAddr = 0, pow = 1;
        for (var i = 3; i >= 0; i -= 1) {
            ipAddr += pow * parseInt(components[i]);
            pow *= 256;
        }
        return ipAddr;
    } else {
        return -1;
    }
};

// Function to check if the ip address is within the given subnet
var isCorporateIP = function(ip, subnets) {
    var subnetLength = subnets.length;
    for (var i = 0; i < subnetLength; i++) {
        var subnetComponents = subnets[i].split('/');
        var minHost = this.convertIpToLong(subnetComponents[0]);
        var ipAddr = this.convertIpToLong(ip);
        var mask = subnetComponents[1];
        if (subnetComponents && minHost >= 0) {
            var numHosts = Math.pow(2, 32 - parseInt(mask));
            if ((ipAddr >= minHost) && (ipAddr <= minHost + numHosts - 1)) {
                return true;
            }
        }
    }
    return false;
};

module.exports.convertIpToLong  =  convertIpToLong;
module.exports.isCorporateIP = isCorporateIP;
Option 02: Create a a module which contains multiple functions and export the whole module itself.
var networkUtils = {
    
    // Function to convert ip address string to long value
    convertIpToLong : function(ip) {
        var components = ip.split('.');
        if (components) {
            var ipAddr = 0, pow = 1;
            for (var i = 3; i >= 0; i -= 1) {
                ipAddr += pow * parseInt(components[i]);
                pow *= 256;
            }
            return ipAddr;
        } else {
            return -1;
        }
    },

    // Function to check if the ip address is within the given subnet
    isCorporateIP : function(ip, subnets) {
        var subnetLength = subnets.length;
        for (var i = 0; i < subnetLength; i++) {
            var subnetComponents = subnets[i].split('/');
            var minHost = this.convertIpToLong(subnetComponents[0]);
            var ipAddr = this.convertIpToLong(ip);
            var mask = subnetComponents[1];
            if (subnetComponents && minHost >= 0) {
                var numHosts = Math.pow(2, 32 - parseInt(mask));
                if ((ipAddr >= minHost) && (ipAddr <= minHost + numHosts - 1)) {
                    return true;
                }
            }
        }
        return false;
    }
};

module.exports  =  networkUtils;
Option 03: Define a module and add each function separately. Finally export the module.
var networkUtils = { };

// Function to convert ip address string to long value
networkUtils.convertIpToLong = function(ip) {
    var components = ip.split('.');
    if (components) {
        var ipAddr = 0, pow = 1;
        for (var i = 3; i >= 0; i -= 1) {
            ipAddr += pow * parseInt(components[i]);
            pow *= 256;
        }
        return ipAddr;
    } else {
        return -1;
    }
};

// Function to check if the ip address is within the given subnet
networkUtils.isCorporateIP = function(ip, subnets) {
    var subnetLength = subnets.length;
    for (var i = 0; i < subnetLength; i++) {
        var subnetComponents = subnets[i].split('/');
        var minHost = this.convertIpToLong(subnetComponents[0]);
        var ipAddr = this.convertIpToLong(ip);
        var mask = subnetComponents[1];
        if (subnetComponents && minHost >= 0) {
            var numHosts = Math.pow(2, 32 - parseInt(mask));
            if ((ipAddr >= minHost) && (ipAddr <= minHost + numHosts - 1)) {
                return true;
            }
        }
    }
    return false;
};

module.exports = networkUtils;

IMPORTANT : You must export the functions in a library in order to use them in any adaptive authentication script.

What’s Next !!
Now you have a well prepared function library. It’s time to get use of it.
1. Follow all steps in this document to configure a service provider for adaptive authentication.
2. Instead of original IP-Based template, copy paste the following as the adaptive authentication script and try out the sample scenario.
    var networkUtilsModule = require('networkUtils.js');
    // IP-Based from Template...
    
    // This script will step up authentication for any user who are trying to log in outside from the configured network
    
    // Configure the network ranges here
    var corpNetwork = ['192.168.1.0/24', '10.100.0.0/16'];
    
    var onLoginRequest = function(context) {
        executeStep(1, {
            onSuccess: function (context) {
                var user = context.currentKnownSubject;
                // Extracting the origin IP of the request
                var loginIp = context.request.ip;
                Log.info('User: ' + user.username + ' logged in from IP: ' + loginIp);
                // Checking if the IP is within the allowed range
                if (!networkUtilsModule.isCorporateIP(loginIp, corpNetwork)) {
                    executeStep(2);
                }
            }
        });
    };
    
    // End of IP-Based.......
    To import a function library,
    • Add
      var <module_name> = require('<function library name>');
    Eg:
    var networkUtilsModule = require(‘networkUtils.js’);
    on top of the script/ before the usage of functions in the function library.
    • Use the functions in the loaded function library,
    E.g:
    networkUtilsModule.isCorporateIP(loginIp, corpNetwork)
    3. Similarly you can import multiple function libraries into one adaptive authentication script and use them.
    When you compare the above script (which uses the imported library) and the original IP-Based template, you will agree with the “advantages of function library usage” which I pointed earlier.
    Nothing difficult right?? Try out this and enjoy your life !!
    NOTE: Please read on the instructions to import function libraries to the adaptive authentication scripts.
    Limitation:
    require()
    functionality is not supported inside function libraries. (i.e You won’t be able to import one function library into another function library)
    Grab more knowledge:

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!