IP Whitelist/Blacklist or Allowlist/Denylist on the ForgeRock Identity Cloud

Keith Daly
3 min readJun 14, 2023

Implementation Notes

Method for Implementing IP Blacklisting/Whitelisting

I recently did some work for a PoC that required a scripted version of a client IP whitelist. There has been a compiled node available in our marketplace for a while, but it is not possible to add custom compiled components to the ForgeRock ID Cloud.

Since this was a PoC, I did not need to worry about where the whitelist/blacklist values were stored. In the code below, I have the list defined as a JSON array of CIDR values. This works fine for relatively static address ranges. If desired, this can be reimplemented as a call to retrieve an ESV if stored in one, or as a call-out to an external system (beware of performance issues if you take this path, though).

The code below shows a whitelist implementation, which would be appropriate as a way to allow traffic from only certain sub-domains. In the code, there are two lines that would need to be flipped (true/false) to modify this to enforce a blacklist.

The Authentication Journey:

Before getting to the code, the following image shows the out-of-the-box Login flow augmented to perform whitelist/blacklist checking.

The “Validate IP” node is a Scripted Node that is configured to use the script. Outcomes are defined as “Pass” and “Block” which corresponds to the (case-sensitive) outcomes defined in the script.

The scripted node code (“Validate IP”):

And here is the script... Be sure to at least modify the cidrAddresses value to suite your environment. These do need to be CIDR addresses (lookup “convert ipv4 to cidr” on your favorite search engine to find online tools, if needed).

//----------------------------------------------------//
//-- IP white/black approve/deny list check --//
//----------------------------------------------------//
//-- The following implementation compares against --//
//-- a whitelist (approvelist) --//
//-- This can easily be changesd to a blacklist --//
//-- (denylist) by inverting the true/false --//
//-- statements in the code. These lines are --//
//-- indicated with comments --//
//----------------------------------------------------//
//-- The list here is a simple JSON array of CIDR --//
//-- addressees. This is the fastest comaprison --//
//-- possible, but can be modified to retrieve --//
//-- values from an external source or ESV. --//
//-------------------------------------------------kd-//

function compareIPtoCIDR(ipAddress, cidrAddress) {

// Convert the IP address to a number
const ipParts = ipAddress.split('.');
const ipNumber =
(parseInt(ipParts[0], 10) << 24) +
(parseInt(ipParts[1], 10) << 16) +
(parseInt(ipParts[2], 10) << 8) +
parseInt(ipParts[3], 10);

// Extract the CIDR address and subnet mask
const cidrParts = cidrAddress.split('/');
const cidrIp = cidrParts[0];
const subnetMask = parseInt(cidrParts[1], 10);

// Convert the CIDR IP address to a number
const cidrIpParts = cidrIp.split('.');
const cidrIpNumber =
(parseInt(cidrIpParts[0], 10) << 24) +
(parseInt(cidrIpParts[1], 10) << 16) +
(parseInt(cidrIpParts[2], 10) << 8) +
parseInt(cidrIpParts[3], 10);

// Calculate the subnet mask as a number
const subnetMaskNumber = Math.pow(2, 32 - subnetMask) - 1;

// Calculate the network address as a number
const networkAddress = cidrIpNumber & (~subnetMaskNumber);

// Check if the IP address is in the network range
return ipNumber >= networkAddress && ipNumber <= networkAddress + subnetMaskNumber;
//return true;
}

//-- Change to a lookup from a config object or external system if required
var cidrAddresses = ['34.64.0.0/10', '93.184.216.34/32'];

//-- The client IP address should be the first IP Address listed in the array of addresses returned
var ipAddress = requestHeaders.get("x-forwarded-for").toString().split(",").shift().substring(1);

//-- Change this default value to "true" for blacklist (denylist)
var found = false;
cidrAddresses.forEach((cidrAddress) => {
//-- Change to "found = false" for blacklist (denylist)
if (compareIPtoCIDR(ipAddress, cidrAddress)) {found = true}
});

if (found === true) {
outcome = "Pass";
} else {
outcome = "Block";
}

--

--

Keith Daly

Identity and Access Management Architect, Tech Evangelist, Sales Engineer, Developer, Implementor, and Fixer. https://www.linkedin.com/in/keithdaly/