Modeling user behavior using OpenAM and Guardian Analytics

Guardian’s FraudMAP Access

A brief overview of Guardian’s flagship analytics engine is in order. A range of methods from malware to social engineering to phishing schemes plus combinations of these can be used by cyber criminals to defeat authentication and illegally access accounts. FraudMAP Access is able to detect the fraudster’s unexpected or suspicious activity when it occurs, as compared to the victim’s typical login behavior. FraudMAP Access models the normal behavior of every user or account holder, building and continually updating individual profiles against which all subsequent activity is compared to look for unusual activity. Powered by Guardian Analytics’ Dynamic Account Modeling™ technology, FraudMAP Access continually monitors the following parameters:

  1. Location from which the account is being accessed
  2. The device being used to access the account
  3. IP address
  4. Day of week and time of day
  5. Frequency patterns

From a regulatory standpoint, FraudMAP Access helps credit unions and banks conform to FFIEC guidance by meeting minimum expectation for anomaly detection at login (for banks and credit unions).

We are interested in the Login, Logout and DeviceRegistered events. The Login event can be used to signal both login success and failure events. The Login event can also be used to send device signature creating a signal of user behavior that FraudMAP tracks on a per user and per session basis. The DeviceRegistered event is used to register a new device, using the uuid and signature created by OpenAM’s DevicePrint module.

Event Data

OpenAM is required to send the following bits of information to the FraudMAP SOAP endpoint:

BrowserPlugins Cookie CookiesEnabled DeviceID FontList HTTPAccept HTTPAcceptCharsets HTTPAcceptEncoding HTTPAcceptLanguage HTTPClientIP HTTPCookie HTTPForwarded HTTPForwardedFor HTTPHost HTTPLocation HTTPProxy HTTPReferer HTTPRequestURI HTTPVia HTTPXClusterClientIP HTTPXForwarded HTTPXForwardedFor HTTPXTrusteerRapport ImmutableUserID IPAddress IPv6Address JavaEnabled LanguageBrowser LanguageSystem LanguageUser Latitude Longitude OSPlatform ScreenResolution SessionID SignOnID TimeZoneOffset UserAgentString UserAgentStringDOM UTCTimestamp

The ImmutableUserID is the key in this mapping process, and is defaulted to the SignOnID, which is the username used to login into OpenAM. Due to privacy concerns, the immutable user id or the sign on id are not sent in the clear, but are SHA1 hashed before being included in the SOAP payload.

OpenAM Configuration Overview

External User Repository

Create a new realm in OpenAM, call it “deviceid” for example. Associate a datastore such as an OpenDJ instance. Check the “Load schema when saved” checkbox. This will ensure the device fingerprint schema is added to the data store. We need this schema because the integration relies on the Device Save module.

Device (Id) Match and Save modules

For this integration the Device Id Match and Save modules in OpenAM can be used as-is. No customizations are necessary. Just ensure the OpenAM schema is loaded into the external user repository you configured into the “deviceid” realm.

Post Authentication Plugin

The HttpServletRequest object is passed into the PaP in the case of a login success and login failure. It contains the following sample data:

Header Name: host-> Header Value: http://<openam-server-hostname>:8080
Header Name: content-type-> Header Value: application/x-www-form-urlencoded
Header Name: origin-> Header Value: http://<openam-server-hostname>:8080
Header Name: cookie-> Header Value: JSESSIONID=496706A47350E1294B0B207E1D9A35B3; AMAuthCookie=AQIC5wM2LY4Sfcz8NstUlg1Chv-_gRP2s-Fj2ZHkPQDYTug.*AAJTSQACMDEAAlNLABMxMDY3MTM1NTMyODY1NTQyNjg4*; amlbcookie=01
Header Name: content-length-> Header Value: 979
Header Name: connection-> Header Value: keep-alive
Header Name: accept-> Header Value: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Header Name: user-agent-> Header Value: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
Header Name: referer-> Header Value:  http://<openam-server-hostname>:8080/openam/UI/Login
Header Name: accept-language-> Header Value: en-us
Header Name: accept-encoding-> Header Value: gzip, deflate

The user-agent string could be used to detect the type of device used to login.

The value of IDToken0 is also passed into the post authentication module in a Map variable in the case of a login success and failure. A sample value would be:

{"screenWidth":1440,"screenHeight":900,"screenColourDepth":24,"timezone":420,"installedPlugins":"CitrixOnlineWebDeploymentPlugin.plugin;WebEx64.plugin;Default Browser.plugin;Flash Player.plugin;googletalkbrowserplugin.plugin;JavaAppletPlugin.plugin;o1dbrowserplugin.plugin;QuickTime Plugin.plugin;SharePointBrowserPlugin.plugin;;","installedFonts":"cursive;monospace;serif;sans-serif;fantasy;Arial;Arial Black;Arial Narrow;Arial Rounded MT Bold;Bookman Old Style;Century;Century Gothic;Comic Sans MS;Courier;Courier New;Georgia;Impact;Lucida Console;Monotype Corsiva;Papyrus;Tahoma;Times;Times New Roman;Trebuchet MS;Verdana;"}

The timezone offset is the offset in minutes with sign reversed. We simply massage this value in the post authentication module, reverse the sign to get an offset in hours.

In summary, we are able to send most of the information FraudMAP requires using the HttpServletRequest object and the IDToken0 value.


In the case of a successful login, we read the HttpServletRequest object, and use a JSON parser to parse the IDToken0 value to extract device signature.


The logic here is very similar to onLoginSuccess() with one difference. OpenAM does not send the accountState usually, however, the AMLoginContext class in OpenAM-Core can be customized to include the accountState- active, inactive or locked out- in the Map variable passed into this method. This is not necessary to achieve a successful integration but I decided to do it to take advantage of the “LockedOut” and “UserAccessRestricted” events that FraudMAP accepts among the reasons for login failure.


We reused the logic from onLoginSuccess() to read the HttpServletRequest object but also checked if the logout was user initiated or was the result of a timeout. FraudMAP cares about the difference and it helps to build a more accurate model of user behavior.

Performance Note

OpenAM synchronously calls the PaP and therefore it is paramount we keep delays in creating and sending events to FraudMAP to a minimum. I created a new PostAuthHandler thread to create and fire the event inside each of the login, logout and login failed methods in the PaP. The PostAuthHandler creates the SOAP message and uses org.apache.commons.httpclient.HTTPClient to post the message to FraudMAP’s endpoint.

OpenAM Configuration

Create a new realm

Log into the console as “amadmin”. On the dashboard under the “Access Control” tab

  • Click on the “New..” button
  • Specify the Name as “deviceid”
  • Click OK

Configure the DataStore

  • Click on the deviceid realm, and then click on the “DataStore” tab
  • Delete the existing datastore, and click “New..”
  • Enter the name “DJ” and check the radio button for “OpenDJ”, hit Next
  • Setup the directory configuration details for your OpenDJ
  • Make sure to check “Load schema when saved” check box
  • Click on Save

Create and Configure the Module Instances and Authentication Chain

  • Click on the Authentication tab (OpenAM 12), or the Menu Item (OpenAM 13)
  • Click on “Module Instances”, and click “New” (OpenAM 12), or “Add Module” (OpenAM 13)
  • Create the match and save modules by selecting Device Id (Match) once and Device Id (Save) next

Module instances

  • Click on the match module instance and select the client-side and server-side scripts:


  • Specify “hotp” as the Name and select “HOTP” as type, Click OK
  • Click on the “hotp” module instance to edit its properties


  • Enter a valid gmail account in “Mail Server Authentication Username”
  • Enter a valid “Mail Server Authentication Password”, confirm it
  • Enter a valid “Email From Address”
  • Leave everything else as-is, and click “Save”
  • Chain the HOTP and Device module instances together as shown

Screen Shot 2016-02-02 at 10.03.56 AM

Note that configuring the “match” module instance in Sufficient mode ensures that if an existing device fingerprint were found on the user profile, HOTP will not be triggered since an acceptable device fingerprint was previously registered by the user.
Configure a new Post Authentication Class

  • Click on the Authentication tab
  • Click on “All Core Settings…”, and scroll down to “Post Authentication Processing”
  • Scroll down to “Authentication Post Processing Classes”
  • Add the full name of your PaP class, including the package prefix
  • Click on “Save” and click on “Back to Authentication”
  • Click on “Save” and click on “Back to Access Control”

Ensure users are loaded from DataStore

  • Click on the “deviceid” realm
  • Click on “Subjects”
  • You should see a list of users in your directory

Change test user’s phone number and email

  • Click on the test user, say user.1
  • Edit the phone number to your phone number
  • Edit the email address to your email address
  • This will ensure the SMS sent by the HOTP module arrives at your phone



Login to the realm

  • Browse to the url https://<openam-server.domain>/openam/XUI/#login/&realm=deviceid
  • Enter username and password, and login
  • You should see the following page, click on “Request OTP Code”


  • You will receive an SMS on your phone, enter it here and click “Submit OTP Code”

Confirm profile save

  • The Device Save module will ask you to confirm to add the device profile to list of trusted devices


  • Select “Yes” and click on “Log In”

Login to view your profile

At this point you will be taken to your user profile page, where you can manage all previously registered devices.


Under the hood

Device Registered

In this case, I logged in from a linux virtual machine.

<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env=""><env:Body><addEvent xmlns=""><requireRiskscore>false</requireRiskscore><serviceId>210057</serviceId><platformId>13</platformId><channel>OLBRetail</channel><payLoad><![CDATA[<?xml version="1.0" encoding="UTF-8"?><GAE:GAEvent xmlns:GAE="" xmlns:GAET="" xmlns:GAT=""><GAE:DeviceRegistered><GAT:Common><GAT:SchemaVersion>2.00</GAT:SchemaVersion><GAT:TenantID>210057</GAT:TenantID><GAT:Channel>OLBRetail</GAT:Channel><GAT:ProviderEventName>DeviceRegistered</GAT:ProviderEventName></GAT:Common><GAT:RetailSession><GAT:ImmutableUserID>8FC62C1442CC32DAA50D5302E5C997080FB9D747</GAT:ImmutableUserID><GAT:SignOnID>8FC62C1442CC32DAA50D5302E5C997080FB9D747</GAT:SignOnID><GAT:UTCTimestamp>1399562334911</GAT:UTCTimestamp><GAT:IPAddress></GAT:IPAddress><GAT:UserAgentString>Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20131029 Firefox/17.0</GAT:UserAgentString><GAT:BrowserHeader><GAT:HTTPAcceptLanguage>en-US,en;q=0.5</GAT:HTTPAcceptLanguage><GAT:HTTPAcceptEncoding>gzip, deflate</GAT:HTTPAcceptEncoding><GAT:HTTPReferer>http://<openam-server.domain>:8080/openam/UI/Login</GAT:HTTPReferer><GAT:HTTPAccept>text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</GAT:HTTPAccept></GAT:BrowserHeader></GAT:RetailSession><GAET:DeviceID>790498b4-0f32-4311-960a-629c70ab4424</GAET:DeviceID><GAET:DeviceType>computer</GAET:DeviceType></GAE:DeviceRegistered></GAE:GAEvent>]]></payLoad><key>8d9d0e4c11a19df474c24da34bc6d3c9fd2368ee</key></addEvent></env:Body></env:Envelope>

User Login

I logged in from a mac using a different test user which is the reason the hash of ImmutableUserID is different from the previous example.

LoginStatus: Successful

<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env=""><env:Body><addEvent xmlns=""><requireRiskscore>false</requireRiskscore><serviceId>210057</serviceId><platformId>13</platformId><channel>OLBRetail</channel><payLoad><![CDATA[<?xml version="1.0" encoding="UTF-8"?><GAE:GAEvent xmlns:GAE="" xmlns:GAET="" xmlns:GAT=""><GAE:Login><GAT:Common><GAT:SchemaVersion>2.00</GAT:SchemaVersion><GAT:TenantID>xxxxxx</GAT:TenantID><GAT:Channel>OLBRetail</GAT:Channel><GAT:ProviderEventName>Login Attempt</GAT:ProviderEventName></GAT:Common><GAT:RetailSession><GAT:ImmutableUserID>B093FEED33DB091084355C1A464290EE6339A109</GAT:ImmutableUserID><GAT:SignOnID>B093FEED33DB091084355C1A464290EE6339A109</GAT:SignOnID><GAT:UTCTimestamp>1399392974892</GAT:UTCTimestamp><GAT:IPAddress>x.y.z.w</GAT:IPAddress><GAT:UserAgentString>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14</GAT:UserAgentString><GAT:BrowserHeader><GAT:HTTPAcceptLanguage>en-us</GAT:HTTPAcceptLanguage><GAT:HTTPAcceptEncoding>gzip, deflate</GAT:HTTPAcceptEncoding><GAT:HTTPReferer>http://<openam-server.domain>:8080/openam/UI/Login</GAT:HTTPReferer><GAT:HTTPAccept>text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</GAT:HTTPAccept></GAT:BrowserHeader><GAT:DeviceAttribute><GAT:FontList>cursive| monospace| serif| sans-serif| fantasy| Arial| Arial Black| Arial Narrow| Arial Rounded MT Bold| Bookman Old Style| Century| Century Gothic| Comic Sans MS| Courier| Courier New| Georgia| Impact| Lucida Console| Monotype Corsiva| Papyrus| Tahoma| Times| Times New Roman| Trebuchet MS| Verdana| </GAT:FontList><GAT:BrowserPlugins>CitrixOnlineWebDeploymentPlugin.plugin| WebEx64.plugin| Default Browser.plugin| Flash Player.plugin| googletalkbrowserplugin.plugin| JavaAppletPlugin.plugin| o1dbrowserplugin.plugin| QuickTime Plugin.plugin| SharePointBrowserPlugin.plugin </GAT:BrowserPlugins><GAT:TimeZoneOffset>-7</GAT:TimeZoneOffset><GAT:ScreenResolution>24|900|1440</GAT:ScreenResolution></GAT:DeviceAttribute></GAT:RetailSession><GAET:LoginStatus>Successful</GAET:LoginStatus></GAE:Login></GAE:GAEvent>]]></payLoad><key>65d67a6d0e87d771d5b843f148d9812d2ec95e10</key></addEvent></env:Body></env:Envelope>

LoginStatus: LockedOut

<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env=""><env:Body><addEvent xmlns=""><requireRiskscore>false</requireRiskscore><serviceId>210057</serviceId><platformId>13</platformId><channel>OLBRetail</channel><payLoad><![CDATA[<?xml version="1.0" encoding="UTF-8"?><GAE:GAEvent xmlns:GAE="" xmlns:GAET="" xmlns:GAT=""><GAE:Login><GAT:Common><GAT:SchemaVersion>2.00</GAT:SchemaVersion><GAT:TenantID>xxxxxx</GAT:TenantID><GAT:Channel>OLBRetail</GAT:Channel><GAT:ProviderEventName>Login Attempt</GAT:ProviderEventName></GAT:Common><GAT:RetailSession><GAT:ImmutableUserID>B093FEED33DB091084355C1A464290EE6339A109</GAT:ImmutableUserID><GAT:SignOnID>B093FEED33DB091084355C1A464290EE6339A109</GAT:SignOnID><GAT:UTCTimestamp>1399392974892</GAT:UTCTimestamp><GAT:IPAddress>x.y.z.w</GAT:IPAddress><GAT:UserAgentString>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14</GAT:UserAgentString><GAT:BrowserHeader><GAT:HTTPAcceptLanguage>en-us</GAT:HTTPAcceptLanguage><GAT:HTTPAcceptEncoding>gzip, deflate</GAT:HTTPAcceptEncoding><GAT:HTTPReferer>http://<openam-server.domain>:8080/openam/UI/Login</GAT:HTTPReferer><GAT:HTTPAccept>text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</GAT:HTTPAccept></GAT:BrowserHeader><GAT:DeviceAttribute><GAT:FontList>cursive| monospace| serif| sans-serif| fantasy| Arial| Arial Black| Arial Narrow| Arial Rounded MT Bold| Bookman Old Style| Century| Century Gothic| Comic Sans MS| Courier| Courier New| Georgia| Impact| Lucida Console| Monotype Corsiva| Papyrus| Tahoma| Times| Times New Roman| Trebuchet MS| Verdana| </GAT:FontList><GAT:BrowserPlugins>CitrixOnlineWebDeploymentPlugin.plugin| WebEx64.plugin| Default Browser.plugin| Flash Player.plugin| googletalkbrowserplugin.plugin| JavaAppletPlugin.plugin| o1dbrowserplugin.plugin| QuickTime Plugin.plugin| SharePointBrowserPlugin.plugin </GAT:BrowserPlugins><GAT:TimeZoneOffset>-7</GAT:TimeZoneOffset><GAT:ScreenResolution>24|900|1440</GAT:ScreenResolution></GAT:DeviceAttribute></GAT:RetailSession><GAET:LoginStatus>LockedOut</GAET:LoginStatus></GAE:Login></GAE:GAEvent>]]></payLoad><key>65d67a6d0e87d771d5b843f148d9812d2ec95e10</key></addEvent></env:Body></env:Envelope>
Note that this article was first published in the OpenAM Wiki Confluence site for OpenAM 11: OpenAM and Guardian Analytics using Device module  The technical content has been adapted for OpenAM 13. Please let us know if we can improve it in anyway.

Implementing Knowledge Based Authentication in OpenAM


Knowledge-based authentication, commonly referred to as KBA, is a method of authentication which seeks to prove the identity of someone accessing a service, such as a financial institution or website. As the name suggests, KBA requires the knowledge of private information of the individual to prove that the person providing the identity information is the owner of the identity. There are two types of KBA: “static KBA”, which is based on a pre-agreed set of “shared secrets”; and “dynamic KBA”, which is based on questions generated from a wider base of personal information. Source: Wikipedia.

In this demonstration of KBA to step-up authentication with OpenAM, we use a predefined question the response to which has already been collected from the user during registration. This response is stored either SHA1 hashed or in clear text on the user profile (by OpenIDM, for example).


A high level overview of the steps involved is presented here:

Copy openam-auth-challengeresponse-module.jar to /webapps/openam/WEB-INF/lib
Copy crblocked.jsp and ChallengeResponseModule.xml in /webapps/openam/config/auth/default
Copy to /webapps/openam/WEB-INF/classes
Copy amAuthChallengeResponseModule.xml to /tmp
 ./ssoadm create-svc –adminid amadmin –password-file pwdfile –xmlfile /tmp/amAuthChallengeResponseModule.xml
 ./ssoadm register-auth-module –adminid amadmin –password-file pwdfile –authmodule org.forgerock.openam.authentication.modules.challengeresponse.ChallengeResponseModule


The custom authentication module compares the stored response with the user input at the time of step-up prompt. The name of the attribute that OpenAM must read to determine the hashed or clear text response is configured using the Module Instances menu item under Realm->Authentication.

Register the module

Setup the Challenge Response Module

And then proceed to configure the attribute name and default question to be asked of the user:

You are also able to check or uncheck Hashed Response which would indicate whether the previously captured user response is stored using SHA1 or not.

Chain it to DataStore

Chain the module to the DataStore module to ensure that the user is prompted for their username and password prior to invoking the Challenge Response module.

Ensure the attribute being used to store the hashed or un-hashed response on the user profile is included in the list of Data Store LDAP user attributes:

Now we are ready to test.


User logs in to the realm under which the module chain described above is configured:

Immediately after password validation, the following screen is presented:

The user enters the response, which is then hashed if the Hash Enabled setting is left untouched, i.e., responses are stored hashed by default, and then compared to the attribute value stored on the user profile in OpenDJ. The user profile is retrieved by the AMIdentity API. If the response is incorrect, then the following error page is presented to the user:

Otherwise, if the correct response was entered, the user is taken to their profile page, or other target requested.


Pick it up at GitHub


This article was first published in the OpenAM Wiki Confluence site: KBA in OpenAM

OpenIDM Forgot Password: Randomly select security questions

Configuration Changes

This note describes the customizations necessary to use 2 or more security questions for the following OpenIDM features: user self-registration, change security data and forgot password. Please note that this article was originally written for v3.0.

Common Configurations


Add the new user profile attributes under allowed properties.


Add policies to the newly added security question and answer attributes.


Include the security answer as a managed object.


Return a comma separated list of values in the endpoint that is called before security questions are to be shown to the user:


Ensure the new security question gets aggregated as shown here:

Self Registration

HTML Template Changes


Edit the template to include another security question as shown here:


If you want questions pre-selected when viewing the registration page, add this script to the page:

setTimeout(function () {
document.getElementById("securityQuestion").selectedIndex = 1;
document.getElementById("securityQuestion2").selectedIndex = 2;
}, 200);

JavaScript Changes


Here we setup the registration code such that it allows the user to setup 2 security questions at the time of self-registration.

Change Security Data

HTML Template Changes


Change the template to include the option of setting up 2 security questions instead of a default of one OpenIDM ships with:

JavaScript Changes


In the javascript, we need to ensure that whenever the user wants to change their security questions, the previously configured ones show up as selected:

Note that this demo does not attempt to manage the UI display order of the previous security questions.

Forgot Password

This code change in UserDelegate.js enables randomly showing any of the 2 configured security questions.



When a user has forgotten their password, they need to click on the Forgot Password link. The security question presented to them is randomly selected from the 2 configured list of questions.

User Interface Views

Self Registration

Change Security Data

Forgot Password



The following REST calls are made to manage a user’s password reset.

List of Questions

The list of security questions can be received via this API call:

GET http://openidm-server:port/openidm/config/ui/secquestions

User Defined Security Question(s)

This pulls up the security question(s) defined for the user indicated by user-name:

POST http://openidm-server:port/openidm/endpoint/securityQA?_action=securityQuestionForUserName&uid=user-name

Submit Security Response

A security response can be submitted to OpenIDM via:

POST http://openidm-server:port/openidm/endpoint/securityQA?_action=checkSecurityAnswerForUserName&uid=user-name&securityAnswer=<user-response>

Reset User Password

Once the user has entered the correct security response, you may change the password via this API call:

POST http://openidm-server:port/openidm/endpoint/securityQA?_action=setNewPasswordForUserName&newPassword=new-password&uid=user-name&securityAnswer=<response>

Scripting in OpenAM 13

Scripting Design

This section introduces Client side and Server side scripts in OpenAM 13. These can be glued together to create Scripted Authentication modules that serve to create powerful authentication workflows as shown later in the Examples section.

Client Side Script

Authentication modules can use Client Side scripts and Server Side scripts using either JavaScript or Groovy. Note that the use of a Client side script is optional. If there is a requirement to collect information from the user, or browser plugins, fonts, screen resolution of a device, or even geolocation then by all means use the Client side script to collect and return the data to the Server side script.

The collected data is returned in the clientScriptOutputData object. You can use the .split() method to pull the client side data and parse accordingly.

An example Client side script is shown below.


Server Side Script

The Server side script handles authentication and is the place to put authentication workflow business logic. The Server side script has access to clientScriptOutputData and OpenAM’s powerful Scripting API. This script must set the authentication state to either success of failed, for example:

An example of using Server side scripts is as follows:


Chaining Scripted Authentication in OpenAM

You would setup your scripted module similar to any other authentication module in OpenAM, such as:

In this particular example, I have required DataStore to be a requisite module, followed immediately by a Scripted module that requires the authenticated user’s userId. See the following code snippet:


This Server side script is written in Groovy. The script reads the username and password from Shared State – a protected and shared area in memory – and uses the credential to make REST-based Policy calls to another OpenAM server (which could be a central policy hub in your enterprise, for example).

Scripting API

HTTP Commons Framework

OpenAM 13 introduces the HTTP Commons Framework. The following packages are available to developers:

org.forgerock.http.protocol.* – Request, Cookie, Entity, Header, Headers, and Response objects

org.forgerock.util.promise.* – Promise objects that represent the result of an asynchronous task in OpenAM

groovy.json.JsonSlurper – a very useful JSON parser that can be used in the scripts.

The API documentation is at
You can use the new HTTP Commons Framework to create a new Request, and invoke REST API calls from the Server Side Script. For example: httpClient.send(Request), returns a Promise. Promise.get(), returns a Response. You may then parse the Response using JsonSlurper. The Server side script can access the Authentication State via the authState, and sharedState objects, as mentioned earlier.

Note that you will need to add the JsonSlurper, or any other class you wish to use, to the whitelist. Otherwise you will get a Security Exception stating use of the class is prohibited. You can whitelist a class using instructions documented here:!/docs/openam/12.0.0/dev-guide/chap-scripted-auth-module#scripted-auth-module-sandbox


The logger object provides error(), message() and warning() methods for logging requirements.

Entity Profile

You are able to access user, group or device profile data via the idRepository object that provides the getAttribute(), setAttribute() and addAttribute() methods.

Original Request

You can also access the original login request using the requestData object, that provides the getHeader(s) and getParameter(s) methods.


Included here are some examples of using the Scripting API.


You can acquire an ssoToken object over REST

Policy Evaluation

Given the following two policies:


and AuthnPolicyY:


You can evaluate policy for the resource “authn/self’ and “authn/view”.

A policy decision is returned:

Check the policy decision and permit authentication if requested actions are ALLOWED:

Finally, logout the service account used for policy invocation using another REST call:

Invoking HOTP over REST

HOTP Introduction

An excellent article about the core OpenAM HOTP capability is: Use HOTP for two factor authentication

Building on the article, we show here a typical REST-based programmatic interaction when invoking HOTP from a client, such as a Portal.

Configuration in OpenAM

Setup HOTP as REQUISITE after DataStore under a realm called “levels”. You could choose not to use realms and create the authentication chain at the top level realm. However, you would modify the REST URL for all the ensuing calls.

Using PostMan, invoke a POST on the realm=/levels




{ "authId": "eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJqd3QiIH0.eyAib3RrIjogIm05ZWVpdjltNmsxdGRoN2dkdGM4MG5qdWFmIiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3hieHYzZjI0aXlzNDlEaG9NUlU3VENhMkU3dFdfcXY1Zy4qQUFKVFNRQUNNREVBQWxOTEFCUXROelU0TmpBME1UZzJNVFl5TWpnNU5EWXdOZy4uKiIsICJyZWFsbSI6ICJvPWxldmVscyxvdT1zZXJ2aWNlcyxkYz1vcGVuYW0sZGM9Zm9yZ2Vyb2NrLGRjPW9yZyIgfQ.SsOE-9DyAZ6apnj5SXaD9ED28T_RDFfpjV8hslHP99g", "template": "", "stage": "DataStore1", "callbacks": [ { "type": "NameCallback", "output": [ { "name": "prompt", "value": " User Name: " } ], "input": [ { "name": "IDToken1", "value": "" } ] }, { "type": "PasswordCallback", "output": [ { "name": "prompt", "value": " Password: " } ], "input": [ { "name": "IDToken2", "value": "" } ] } ] }


Add in the username and password as shown below:

Body (from the screenshot above)-

{ "authId": "eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJqd3QiIH0.eyAib3RrIjogIjl2Mm1lZGQ3NmpwdXViZGlncGkyZjFtNzk5IiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3d1amwzczR4TXdyMHI0S3paMFI2Qmk3LV9aTHMxcnRQby4qQUFKVFNRQUNNREVBQWxOTEFCTXlOelE1TmpJeU5qUTBOVFUwTlRreE5EQTMqIiwgInJlYWxtIjogIm89bGV2ZWxzLG91PXNlcnZpY2VzLGRjPW9wZW5hbSxkYz1mb3JnZXJvY2ssZGM9b3JnIiB9.WGemKm2u0O-vbhAkAClg0l0rndGEhDbhS3pncAsn6PA", "template": "", "stage": "DataStore1", "callbacks": [ { "type": "NameCallback", "output": [ { "name": "prompt", "value": " User Name: " } ], "input": [ { "name": "IDToken1", "value": "user.4" } ] }, { "type": "PasswordCallback", "output": [ { "name": "prompt", "value": " Password: " } ], "input": [ { "name": "IDToken2", "value": "Password2" } ] } ] }



{ "authId": "eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJqd3QiIH0.eyAib3RrIjogIjl2Mm1lZGQ3NmpwdXViZGlncGkyZjFtNzk5IiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3d1amwzczR4TXdyMHI0S3paMFI2Qmk3LV9aTHMxcnRQby4qQUFKVFNRQUNNREVBQWxOTEFCTXlOelE1TmpJeU5qUTBOVFUwTlRreE5EQTMqIiwgInJlYWxtIjogIm89bGV2ZWxzLG91PXNlcnZpY2VzLGRjPW9wZW5hbSxkYz1mb3JnZXJvY2ssZGM9b3JnIiB9.WGemKm2u0O-vbhAkAClg0l0rndGEhDbhS3pncAsn6PA", "template": "", "stage": "HOTP2", "callbacks": [ { "type": "PasswordCallback", "output": [ { "name": "prompt", "value": " Enter OTP " } ], "input": [ { "name": "IDToken1", "value": "" } ] }, { "type": "ConfirmationCallback", "output": [ { "name": "prompt", "value": "" }, { "name": "messageType", "value": 0 }, { "name": "options", "value": [ " Submit OTP ", " Request OTP " ] }, { "name": "optionType", "value": -1 }, { "name": "defaultOption", "value": 0 } ], "input": [ { "name": "IDToken2", "value": 0 } ] } ] }


Set IDToken2 to value “1”, and re submit as a POST. OpenAM will generate the OTP code and send it to the user.



{ "authId": "eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJqd3QiIH0.eyAib3RrIjogImYwZW5xYTI2aHZla3Q0bG5tc24wbDFlbTI5IiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3k4cllnb2V3MDBWNkpOUW5rR1RjLVoyVjZmcjZFMS11US4qQUFKVFNRQUNNREVBQWxOTEFCTTRNak01TXpRMU9EazROekl4TlRnNU16UXkqIiwgInJlYWxtIjogIm89bGV2ZWxzLG91PXNlcnZpY2VzLGRjPW9wZW5hbSxkYz1mb3JnZXJvY2ssZGM9b3JnIiB9.Dfxj7bAtxHEenVPA_9t3iCFhu92zqk8lXAqxmQD1COU", "template": "", "stage": "HOTP2", "callbacks": [ { "type": "PasswordCallback", "output": [ { "name": "prompt", "value": " Enter OTP " } ], "input": [ { "name": "IDToken1", "value": "" } ] }, { "type": "ConfirmationCallback", "output": [ { "name": "prompt", "value": "" }, { "name": "messageType", "value": 0 }, { "name": "options", "value": [ " Submit OTP ", " Request OTP " ] }, { "name": "optionType", "value": -1 }, { "name": "defaultOption", "value": 0 } ], "input": [ { "name": "IDToken2", "value": 1 } ] } ] }



{ "authId": "eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJqd3QiIH0.eyAib3RrIjogImYwZW5xYTI2aHZla3Q0bG5tc24wbDFlbTI5IiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3k4cllnb2V3MDBWNkpOUW5rR1RjLVoyVjZmcjZFMS11US4qQUFKVFNRQUNNREVBQWxOTEFCTTRNak01TXpRMU9EazROekl4TlRnNU16UXkqIiwgInJlYWxtIjogIm89bGV2ZWxzLG91PXNlcnZpY2VzLGRjPW9wZW5hbSxkYz1mb3JnZXJvY2ssZGM9b3JnIiB9.Dfxj7bAtxHEenVPA_9t3iCFhu92zqk8lXAqxmQD1COU", "template": "", "stage": "HOTP2", "callbacks": [ { "type": "PasswordCallback", "output": [ { "name": "prompt", "value": " Enter OTP " } ], "input": [ { "name": "IDToken1", "value": "" } ] }, { "type": "ConfirmationCallback", "output": [ { "name": "prompt", "value": "" }, { "name": "messageType", "value": 0 }, { "name": "options", "value": [ " Submit OTP ", " Request OTP " ] }, { "name": "optionType", "value": -1 }, { "name": "defaultOption", "value": 0 } ], "input": [ { "name": "IDToken2", "value": 1 } ] } ] }


Supply OTP Code

Change IDToken2 to value “0” and this time have the user supply the OTP code s/he received:



{ "authId": "eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJqd3QiIH0.eyAib3RrIjogImYwZW5xYTI2aHZla3Q0bG5tc24wbDFlbTI5IiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3k4cllnb2V3MDBWNkpOUW5rR1RjLVoyVjZmcjZFMS11US4qQUFKVFNRQUNNREVBQWxOTEFCTTRNak01TXpRMU9EazROekl4TlRnNU16UXkqIiwgInJlYWxtIjogIm89bGV2ZWxzLG91PXNlcnZpY2VzLGRjPW9wZW5hbSxkYz1mb3JnZXJvY2ssZGM9b3JnIiB9.Dfxj7bAtxHEenVPA_9t3iCFhu92zqk8lXAqxmQD1COU", "template": "", "stage": "HOTP2", "callbacks": [ { "type": "PasswordCallback", "output": [ { "name": "prompt", "value": " Enter OTP " } ], "input": [ { "name": "IDToken1", "value": "86848280" } ] }, { "type": "ConfirmationCallback", "output": [ { "name": "prompt", "value": "" }, { "name": "messageType", "value": 0 }, { "name": "options", "value": [ " Submit OTP ", " Request OTP " ] }, { "name": "optionType", "value": -1}, { "name": "defaultOption", "value": 0 } ], "input": [ { "name": "IDToken2", "value": 0 } ] } ] }


At this time, if the OTP code was correct, the user will be logged into OpenAM.

The value of the iPlanetDirectoryPro cookie will be returned as tokenId.



{ "tokenId": "AQIC5wM2LY4Sfcyt-pO9V3hEMYK924cDC2_SlWLROXUBpsc.*AAJTSQACMDEAAlNLABQtMTcyMDQ4ODk5NTU0ODUzNTQ4MQ..*", "successUrl": "/openam/console" }


The iPlanetDirectoryPro can then be used to do other stuff, such as list user data values over REST:

Integration with PaloAlto Networks Panorama


Palo Alto Networks virtualized firewalls, called the VM-Series support the exact same next-generation firewall and advanced threat prevention features available in their physical form factor appliances, allowing customers to safely enable applications flowing into, and across their private, public and hybrid cloud computing environments. Automation features such as VM monitoring, dynamic address groups and a REST-based API allow developers and administrators to proactively monitor VM changes dynamically feeding that context into security policies, thereby eliminating the policy lag that may occur when the Virtual Machines change.

Consider the following use case:

A customer of Palo Alto Networks has firewalls protecting an insurance & claims Portal. The customer seeks an identity & access management platform with following key capabilities:
a) Onboard millions of existing users from existing internet-facing Directory Server
b) Provide a self registration capability for new customers with a new policy (individual or via employer)
c) Be able to detect portal sessions, and selectively enable enterprise applications in the Portal for customers, affiliates and partners alike.

The ForgeRock powered Palo Alto Networks solution provides all these key capabilities.

Users login to the Portal, protected by OpenAM distributed User Interface living in the DMZ. Upon successful authentication, OpenAM punches application-specific ports in the Palo Alto Networks firewall(s) on behalf of the user via the Palo Alto Networks User-ID API. This powerful real-time action instantly enables user access to Portal iFrames, AJAX widgets (etc) to make inbound API calls to retrieve live policy, insurance or claims content. When the user logs out of Portal, OpenAM instantly closes the application-specific ports via the Palo Alto Networks User-ID API. This real-time action instantly disables access to the specific applications for the user.

This page describes a simpler use case, wherein REST calls are used to log into OpenAM directly, triggering User-Id API calls in the backend to open and subsequently close application ports in the virtualized firewalls managed by Panorama.


Technical Overview

All software used in this demo is on evaluation virtual appliances from Palo Alto Networks.

Download, install and configure Panorama 6 to connect to two 5.0.6 firewalls and two 6.0.0 firewalls. For this demo we shall only be using the 6.0.0 firewall.

Note that Panorama has to have the highest version always. The managed firewalls can be the same version as Panorama or older.

Finally, Panorama 6 needs to be run on a 64 bit ESXi host.

Generate an API key that needs to be sent with every REST call using the endpoint: http(s)://hostname/api/?type=keygen&user=username&password=password 

Note that the API returns separate keys each time a keygen query is run. All of the returned keys are valid.

When installations and configurations are done, you should see the virtualized firewalls in a connected state from the Panorama interface:

Write a post authentication plugin (PaP) in OpenAM that uses the Palo Alto Networks REST-based User-ID API to open up an application port in Panorama-managed firewalls.

The PaP creates a user-id mapping file under /tmp, such as the following:

<?xml version="1.0" encoding="UxTF-8"?>
<entry blocksize="2" endport="8081" ip="" name="demo" startport="8080"/></login>

The PaP uploads this XML file to the endpoint: http://hostname/api/?type=user-id&action=set&key=api-key&target=firewall-serial-number

The endpoint is the Panorama listener that dispatches the incoming User-ID API “set” request to the target firewall specified in the target parameter.


Here is the step by step flow:

Login to OpenAM as demo via curl:

The Post Authentication Plugin creates the API payload and upload it to the Panorama endpoint specifying the target managed firewall.

The User-ID API “punches” an access-grant into the virtualized firewall as shown here. This access-grant happens to never expire in our demo, however, it could be time bounded.

The user logs out via CURL as shown here:

The request is sent to the same target:

The user access, previously granted is now unblocked as shown here.

That sums up this simple demo.


This article was first published on the OpenAM Wiki Confluence site: OpenaM and PaloAlto Networks

Google+ Social Login Risk Profiling using ZeroFOX


ZeroFOX Enterprise evaluates and records the risk associated with social media users and organizations. This evaluation is conducted through the ZeroFOX Enterprise security analysis engine which analyzes the full social graph for a targeted user to include their associated people, organizations and interactions.

The ZeroFOX Social Risk Score API allows OpenAM to query the ZeroFOX Enterprise Platform RESTful API for risk attributes associated with a given social media asset- social media user or organization. If ZF does not have recent ZeroFOX data on the requested asset, it is immediately submitted for analysis and can be queried again at a later time.

For purposes of this demo with Google Plus, I created a custom OpenAM 13 build that enables chaining the Scripted Authentication Module to the OAuth module. The custom build also enables passing an Authorization header into the httpClient POST API in the server-side script. On every login attempt from a Social Login platform, the Scripted Authentication Module invokes the ZF api with an Authorization header to assess the risk associated with the login.

The following risk attributes can be flagged on a social account and each one of these can be detected from within the Scripted Authentication Module in OpenAM via the ZeroFOX Social Login Risk Score API.


From Common Tasks, under the “Configure Social Authentication” menu, select and configure ‘Configure Google Authentication’.

Create a Scripted Authentication Module instance in OpenAM. Add code in the server side script to invoke the ZF API:"", "{"network":"google+", "user_id":"+userId +"}", {cookies:[], headers:[{"field": "Content-type","value":"application/json"},{"field": "Authorization","value": "forgerock:*******"}]});

Chain the Scripted Authentication Module instance to the GoogleSocialAuthentication instance created in the previous step:

In Google Plus, open a test account and post a phishing link. At this point you should kick off the scan by invoking the ZF API from Postman or using curl. If you do not do it now, the scan is started when you attempt to login into OpenAM using this malicious account, except of course, the Social Login Score is not ready just yet and authentication passes.


From the OpenAM login page, click on the Google Plus icon to login:

Login as the malicious user and you should immediately see an Authentication denied error. The Scripted Authentication Module checks for high threat indicators and denies login if one or more are found.

Using Postman, invoking the ZF Social Login Score API confirms that phishing links were found on the account and threat level is too high to allow login into OpenAM.

  • "phishing": "2015-04-24 17:13:24 UTC",
  • "drugs_and_alcohol": "2015-04-24 17:09:24 UTC",
  • "malware": "2015-04-24 17:13:24 UTC"

Impersonation Authentication module for OpenAM


Support for impersonation is useful in the enterprise use cases where designated administrators are required to act on behalf of a user in certain scenarios. By impersonating another user an administrator, if authorized to do so, gains access to a restricted view of the user’s profile in the system. This is helpful in situations involving password reset, request-based access and profile updates. However, the design of such a system must call for controls that actively restrict access to the user’s entitlements at the outset. This can be achieved using step-up authentication for gaining access to private user data, and also by using the OpenAM policy engine for performing advanced resource-based decisioning.


An OpenAM custom authentication module was written to enable impersonation support. The module requires input of the username of the end-user being impersonated and the administrator credentials. After submitting the username and password, the admin account is authenticated first and then it is also authorized to complete the impersonation request using REST calls to a specified OpenAM Policy endpoint. This policy can be either local or external as we shall examine further. The impersonated user is also validated for being in active status in the system. If all is okay, the administrator is permitted to impersonate and OpenAM creates a session for the impersonated user. The module can be configured using the following gauges to complete the described functions correctly:

  1. Setup the resource-set you want to check policy for. This resource set its nothing but a special URL that invokes policy evaluation for impersonation
  2. The authentication realm you want the administrator to authenticate in. The authentication module allows for realm-specific authentication
  3. The OpenAM server where the policy resides, the realm where the policy resides, and the policy-set name. The policy does not need to be local and can be on a remote policy host
  4. Check whether you want the administrator to be a member of a local group as well, in addition to the external policy authorization.
A step by step account of the workings of the module follows.


Configuration read from Module Instance

options -> {iplanet-am-auth-check-group-membership=[True], iplanet-am-auth-impersonation-hash-enabled=[true], iplanet-am-auth-authentication-realm=[authn], iplanet-am-auth-impersonation-auth-level=[1], iplanet-am-auth-resource-set=[http://openam:8080/openam/index.html], moduleInstanceName=impersonate, iplanet-am-auth-impersonation-id=[Enter the user id to impersonate?], iplanet-am-auth-impersonation-group-name=[impersonation], iplanet-am-auth-openam-server=[http://openam:8080/openam], iplanet-am-auth-policy-realm=[impersonation], iplanet-am-auth-policy-set-name=[impersonation]}

Authorize the administrator locally

In our test scenario, the ‘user.0’ is really an administrative user that has been granted membership to the group named ‘impersonation’, as configured in the module (see above).

We build an AMIdentity object for the group and validate membership.

[AMIdentity object: id=impersonation,ou=group,o=impersonation,ou=services,dc=openam,dc=forgerock,dc=org]
value of attribute: uid=user.0,ou=People,dc=forgerock,dc=com
userName to check: user.0
match found! admin: user.0 allowed to impersonate user: user.1

Authorize the Administrator

Get the ssotoken for the admin who is trying to impersonate via a policy call, and authenticate the user to the realm specified in the config

json/authn/authenticate response-> {"tokenId":"AQIC5wM2LY4Sfcxokjvdayf3ig0oDuQITXRTWT9B_3hq72A.*AAJTSQACMDEAAlNLABI1ODk0Nzg1NTEyNDUzNzcxNDI.*","successUrl":"/openam/console"}
tokenId-> AQIC5wM2LY4Sfcxokjvdayf3ig0oDuQITXRTWT9B_3hq72A.*AAJTSQACMDEAAlNLABI1ODk0Nzg1NTEyNDUzNzcxNDI.*


Build the 2nd policy rest call, and use the resource set, openam server, policy set and policy container from the configuration passed to the module.

stringentity-> {"resources": ["http://openam:8080/openam/index.html"],"application":"impersonation", "subject": {"ssoToken":"AQIC5wM2LY4Sfcxokjvdayf3ig0oDuQITXRTWT9B_3hq72A.*AAJTSQACMDEAAlNLABI1ODk0Nzg1NTEyNDUzNzcxNDI.*"}}
json/impersonation/policies?_action=evaluate response-> [{"advices":{},"actions":{"POST":true,"PATCH":true,"GET":true,"DELETE":true,"OPTIONS":true,"PUT":true,"HEAD":true},"resource":"http://openam:8080/openam/index.html","attributes":{"uid":["user.0"],"cn":["Javed Shah"],"roleName":["timeBoundAdmin"]}}]

Custom response attributes can be passed back to the module for further evaluation if needed. For example, a statically defined roleName=timeBoundAdmin could be used to further restrict this impersonation request within the time window specified in the ‘timeBoundAdmin’ control. This example is only given to seed the imagination, the module currently does not restrict the impersonation session using a time window, but this is possible to do.

Parse the JSON response from Policy Evaluation

jsonarray-> {"resource":"http://openam:8080/openam/index.html","attributes":{"uid":["user.0"],"cn":["Javed Shah"],"roleName":["timeBoundAdmin"]},"advices":{},"actions":{"POST":true,"PATCH":true,"GET":true,"DELETE":true,"OPTIONS":true,"HEAD":true,"PUT":true}}
If the ACTION set returned for GET/POST is TRUE, the admin is permitted to impersonate. This could be extended to include other actions as necessary. Finally, destroy the admin session, now that it is not needed anymore and return the impersonated user as the Principal for constructing an OpenAM session.


Our short demo begins with the administrator being asked for the username of the user they want to impersonate.
Next, the module asks for the admin credentials.
If the administrator is unable to authenticate, or does not belong to the local group, or fails external policy evaluation, the following error screen is shown.
If all checks pass, the adminsitrator is granted the user’s session and logs into OpenAM.


This article was first published on the OpenAM Wiki Confluence site: Impersonation in OpenAM

Multi Factor Authentication using Duo Security


Duo Security provides an array of security products including Duo Mobile which is a stronger method of authentication using a registered device per user. More details can be found here Duo Mobile.

OpenAM is able to invoke the Duo Security API with the “push” factor that uses a push notification to the Duo Mobile app installed on your device for one-tap authentication. Please note that the demo presented here is purely for instructional purposes.


The user and device are first registered with the Duo Security application platform. This is done by the designated administrator. Multiple integrations can be created per organization allowing categorization of user communities by device, geo location or other factors.

Configuration in Duo


You can setup certain users with special privileges such as “bypass”, etc.

The user-id in Duo Security must match the user-id in OpenAM’s user repository for the authentication to succeed.

Configuration in OpenAM

Chain the DataStore module with the custom Duo module.


The user simply has to enter username and password to OpenAM. After verification of username and password, OpenAM starts the Duo custom authentication module.

After hitting submit, the following placeholder screen is shown. This is automatically refreshed until the user accepts or rejects the logon attempt from their device.

The custom authentication module invokes the Duo REST API with factor=push, and username. The request is signed with the integration key and secret key. A transaction id is returned that is saved for polling the request in the next step. The user gets a push notification on their Duo Mobile app.

After opening the Duo Mobile app the user is presented with an accept/deny screen.

Meanwhile, OpenAM polls the Duo REST API using the transaction id to check if the user accepted the push-notification. If so, they are immediately logged into OpenAM.

The user may decline the logon attempt.

The user may indicate whether it was a mistake or seems fraudulent.


If declined, authentication to OpenAM is denied immediately.



Simplified pseudo-code for a custom authentication module is presented here.

func process[] {






               if “waiting”, set-next-auth-state[poll-step]

               if “allow”, set-next-auth-state[LOGIN_SUCCEED]

               if “deny”, throw Exception


func get-transactionId-from-Duo-REST-API[] {

    invoke-HTTP-POST on /auth/v2/auth


func poll-Duo-REST-API[] {

    invoke-HTTP-GET on /auth/v2/auth_status


Source Code

OpenAM Duo Security module on Github

Geo-Fencing using Pitney Bowes ReverseGeocode API


Latitude. Longitude. They’re a way of reaching consumers based not just on transactional data, but also where they are at any given moment. It is possible to protect data by reaching consumers on the go. Precision Reverse Geocoding, only from Pitney Bowes Software, uses a smartphone’s GPS signal to pinpoint a consumer’s location. That location can then be used to determine proximity to secured data zones and even cross-referenced with nearby mobile users within an individual’s social network. Details can be found at Pitney Bowes Geocoding.

While location coordinates can be used for increasing sales and relevance in the Retail and Fast Moving Consumer Goods industries, there are authentication and authorization use cases for the security market as a whole that could use geo-fencing. This technology could enable an organization to protect data from unauthorized access by ‘enveloping’ it with an authorization protocol based on geo-location. Devices that are able to transmit geolocation to OpenAM using either the built-in GPS, or by other means, may then be checked for authorized access based on the current user location before access to data is permitted. This can be accomplished simply by using a scripted authentication connector in OpenAM and the Pitney Bowes ReverseGeocodeUSLocation API.


For the purpose of this demo, we will use a scripted authentication module in OpenAM with the client side script supplying the latitude and longitude, as shown here:

When the user hits the OK button, the coordinates are submitted to the server side script, which then makes an authenticated REST call to the ReverseGeocodeUSLocation Pitney Bowes API.

If the user is within the approved zone for this particular request for access, then she is granted permission to proceed, otherwise an authentication denied error is shown. In our case here, if the demo device is moved more than 15 feet away, our position coordinates change to the address of my neighbor, as shown here:

Since these one-shot coordinates map to a different street address than the one permitted, for this demo anyway, authentication is immediately denied:

If I stay within the approved zone when logging on to OpenAM from my device, the access attempt is approved.

Client Side Script

The client side script uses the Mozilla Geolocation API to retrieve the coordinates which are passed on to the server side script in the clientScriptOutputData JSON object. The implementation of the Geolocation API is based on the W3C Geolocation API Specification. The Geolocation API defines a high-level interface to location information associated only with the device hosting the implementation, such as latitude and longitude. The API itself is agnostic of the underlying location information sources. Common sources of location information include Global Positioning System (GPS) and location inferred from network signals such as IP address, RFID, WiFi and Bluetooth MAC addresses, and GSM/CDMA cell IDs, as well as user input. No guarantee is given that the API returns the device’s actual location. The API is designed to enable both “one-shot” position requests and repeated position updates, as well as the ability to explicitly query the cached positions. Location information is represented by latitude and longitude coordinates. For purposes of this demo, no special coding provisions were made to reject potentially cached position coordinates.

Server Side Script

The server side script extracts the coordinates, creates an HTTP Basic authorization header and calls the ReverseGeocodeUSLocation API. The API could easily be modified to permit HTTP Token or other forms of authorization. Note that in OpenAM 12 it is not currently possible to pass an HTTP authorization header and therefore a custom build was used to perform this integration. This facility will be available in OpenAM 13. The API returns the complete Census output fields containing U.S. Census information about the coordinates, including the precise street address. This street address is extracted from the returned message XML body, and checked with a known value. The demo keeps it simple, obviously. Much more could be accomplished with the data returned from the Pitney Bowes API. One could, for instance, during a “cut over” period, map the coordinates to create a heat-map, or user’s preferred area of usage. This zone could then be used to enforce any departures by denying access to requests for sensitive data outside it. Another use case could be co-relating the user’s one-shot position coordinates with nearby sensors and tags, to determine which building floor or room the user is currently requesting access to the data from, and taking policy actions based on that information.


The API defined in the W3C specification is used to retrieve the geographic location of a hosting device. In almost all cases, this information also discloses the location of the user of the device, thereby potentially compromising the user’s privacy. OpenAM’s use of Mozilla’s implementation of this specification provides a mechanism that protects the user’s privacy by ensuring that no location information is made available through this API without the user’s express permission. In this demo, by attempting a login into OpenAM, with implied access to secured and sensitized data, the user implicitly gives the permission for the location coordinates to be evaluated (shared with Pitney Bowes), however, no information identifying the user is sent over the wire.