Oracle Adaptive Access Manager 11g provides the framework to support the One Time Password authentication method.
One Time Password (OTP) is used to authenticate an individual based on a single-use alphanumeric credential.
The OTP is delivered to the user's configured delivery method. The user then provides the OTP credential as the response to proceed with the operation.
The following are major benefits of using out-of-band OTP:
If the end user's browser/internet is compromised, the authentication can safely take place in another band of communication separate from the browser
The user does not require any proprietary hardware or client software of any kind.
This chapter provides an example of how to integrate OTP into the system. It contains the following sections:
To integrate OTP into your system, the following tasks must be performed:
Implement a challenge processor.
Configure the authentication device
Configure the user information properties
Challenge Processors are a plug-in framework that allows integrators the ability to write custom processor classes that can be triggered when an associated rule action is returned by the challenge checkpoint.
Challenge processors perform the following tasks:
Generate challenge secret (PIN) to send to the user.
Validate the user answer
Control delivery wait page (if needed)
Check if delivery service is available (if needed)
Processor classes are:
Customizable challenge method processors
Customizable rules result (rule action) processors (including legacy processors for existing policies)
To use SMS, you must implement a method for generating the secret PIN and checking the status of the send and the class that is called for a challenge type. You must also configure policies to use SMS.
For instructions on customizing, extending, or overriding Oracle Adaptive Access Manager properties, refer to Chapter 12, "Customizing Oracle Adaptive Access Manager."
To implement a challenge processor, you will need to extend the following class:
com.bharosa.uio.processor.challenge.AbstractChallengeProcessor
Later, you will compile the code by adding oaam.jar from $ORACLE_IDM_HOME\oaam\cli\lib
folder to the build classpath.
A few methods to implement a challenge processor is shown as follows:
protected boolean generateSecret(UIOSessionData sessionData, boolean isRetry) to generate code to send to client protected boolean validateAnswer(UIOSessionData sessionData, String answer) to validate the user answer public String checkDeliveryStatus(UIOSessionData sessionData, boolean userWaiting, boolean isRetry) if you want to provide wait until message is sent public boolean isServiceAvailable(UIOSessionData sessionData) to check if external service is available
An example of implementing a challenge processor is shown as follows:
package oracle.oaam.challenge.processor.challenge; import com.bharosa.common.util.*; import com.bharosa.uio.util.UIOUtil; import com.bharosa.uio.util.UIOSessionData; import com.bharosa.common.logger.Logger; import java.io.Serializable; /** * Email Challenge Processor - provides OTP Code generation, delivery and validation */ public class EmailChallengeProcessor extends com.bharosa.uio.processor.challenge.AbstractOTPChallengeProcessor implements Serializable{ static Logger logger = Logger.getLogger(EmailChallengeProcessor.class); public EmailChallengeProcessor( ) { } /** * Generates OTP Code and stores it in sessionData * * @param sessionData data object available for the session * @param isRetry boolean value if method was called as a result of a failed answer attempt * @return */ protected boolean generateSecret(UIOSessionData sessionData, boolean isRety) { String otpCode = sessionData.getOTPCode(); // If no secret code is present in session, generate one. if (StringUtil.isEmpty(otpCode)) { if (logger.isDebugEnabled()) logger.debug("ChallengeEmail generating security code for user: " + sessionData.getCustomerId()); otpCode = generateCode(sessionData); // save the code for later reference - validate / resend sessionData.setOTPCode(otpCode); } if (logger.isDebugEnabled()) logger.debug("OTP code for user " + sessionData.getCustomerId() + " : " + otpCode); if (StringUtil.isEmpty(otpCode)) { logger.error("Email Challenge pin generation returned null."); return false; } // isRetry flag is turned on if user fails to answer the question if (!isRetry) { return sendCode(sessionData); } return true; } /** * Validate user entered answer against value in sessionData * * @param sessionData validate code and return result. * @param answer answer provided by the user * @return */ protected boolean validateAnswer(UIOSessionData sessionData, String answer){ //need to authenticate OTP Code String otpCode = sessionData.getOTPCode(); if (otpCode != null && otpCode.equals(answer)) { // Expire OTP Code sessionData.setOTPCode(null); return true; } return false; } /** * Private methods to send secret code to client * * @param sessionData * @return */ private boolean sendCode(UIOSessionData sessionData){ String otpCode = sessionData.getOTPCode(); try { // UIOUtil.getOTPContactInfo fetches the information registered by the user. Refer to ChallengeEmail.requiredInfo in configuration. String toAddr = UIOUtil.getOTPContactInfo(sessionData, "email"); if (StringUtil.isEmpty(toAddr)) { logger.error("No user email in profile."); return false; } // Send secret code to customer using your email provider } catch (Exception ex) { logger.error("ChallengeEmail Error sending code.", ex); return false; } return true; } public String checkStatus(UIOSessionData sessionData, boolean userWaiting, boolean isRetry) { String target = ChallengeProcessorIntf.TARGET_WAIT; // user already has code, trying again - send to challenge page if (isRetry){ return ChallengeProcessorIntf.TARGET_CHALLENGE; } boolean sendComplete = false; if (userWaiting){ // if secret code is sent set target to target = ChallengeProcessorIntf.TARGET_CHALLENGE; // failed to send target = ChallengeProcessorIntf.TARGET_ERROR; // still processing target = ChallengeProcessorIntf.TARGET_WAIT; } return target; } }
Challenge types are configured by the enum, "challenge.type.enum". The actual enum value is shown as follows:
bharosa.uio.<application>. challenge.type.enum.<challenge type>
Example for Defining an OTP Type
Configure the bharosa.uio.default.challenge.type.enum property to edit out of the box OTP challenge types or add a new challenge type.
Here is an example for defining an OTP type:
bharosa.uio.default.challenge.type.enum.MyChallenge
In the example, the "default" is the UIO application name, and "MyChallenge" is challenge Type being newly added
To enable/disable a challenge type, the available flag should be set (not the enable flag, that would remove it from list)
Table 9-1 Challenge type Properties
Property | Description |
---|---|
available |
if the challenge type is available for use (service ready and configured). To enable/disable an OTP challenge type, the available flag should be set. |
processor |
java class for handling challenges of this type. |
requiredInfo |
comma separated list of inputs from the registration input enum |
Attributes of the enum with example values is shown as follows:
bharosa.uio.default.challenge.type.enum.MyChallenge = 1 // unique value to identify Challenge Email in bharosa.uio.default.challenge.type.enum bharosa.uio.default.challenge.type.enum.MyChallenge.name = MyChallenge // unique string to identify Challenge Email in bharosa.uio.default.challenge.type.enum, no spaces bharosa.uio.default.challenge.type.enum.MyChallenge.description = Email Challenge // descriptive name bharosa.uio.default.challenge.type.enum.MyChallenge.processor = oracle.oaam.challenge.processor.challenge.EmailChallengeProcessor // Processor used for sending emails instance of com.bharosa.uio.processor.challenge.ChallengeProcessorIntf bharosa.uio.default.challenge.type.enum.MyChallenge.requiredInfo = email // comma separated field names, User registration flow captures these data fields, check Contact information Inputs section to define this enum bharosa.uio.default.challenge.type.enum.MyChallenge.available = false // to turn off this service bharosa.uio.default.challenge.type.enum.MyChallenge.otp = true // indicates this challenge is used for OTP, set it to true
bharosa.uio.default.challenge.type.enum.ChallengeEmail = 1 bharosa.uio.default.challenge.type.enum.ChallengeEmail.name = Email Challenge bharosa.uio.default.challenge.type.enum.ChallengeEmail.description = Email Challenge bharosa.uio.default.challenge.type.enum.ChallengeEmail.processor = com.bharosa.uio.processor.challenge.EmailChallengeProcessor bharosa.uio.default.challenge.type.enum.ChallengeEmail.requiredInfo = mobile bharosa.uio.default.challenge.type.enum.ChallengeEmail.available = true bharosa.uio.default.challenge.type.enum.ChallengeEmail.enabled = true
bharosa.uio.default.challenge.type.enum.ChallengeSMS = 2 bharosa.uio.default.challenge.type.enum.ChallengeSMS.name = SMS Challenge bharosa.uio.default.challenge.type.enum.ChallengeSMS.description = SMS Challenge bharosa.uio.default.challenge.type.enum.ChallengeSMS.processor = com.bharosa.uio.processor.challenge.SmsChallengeProcessor bharosa.uio.default.challenge.type.enum.ChallengeSMS.requiredInfo = mobile bharosa.uio.default.challenge.type.enum.ChallengeSMS.available = true bharosa.uio.default.challenge.type.enum.ChallengeSMS.enabled = true
By default, challenge devices are configured through rules. The rules are under the AuthentiPad checkpoint and determine the type of device to use based on the purpose of the device (ChallengeEmail, ChallengeSMS, ChallengeQuestion, and so on).
To create/update policies to use the challenge type:
Add a new rule action, MyChallenge, with the enum, rule.action.enum.
Create policy to return newly created action, MyChallenge, to use the challenge method.
Alternatively, if you want to configure challenge devices using properties, you can bypass the AuthentiPad checkpoint by setting bharosa.uio.default.use.authentipad.checkpoint
to false
.
Devices to use for the challenge type can be added.
bharosa.uio.<application>.<challengeType>.authenticator.device=<value>
The examples shown use the challenge type key, ChallengeEmail and ChallengeSMS to construct the property name.
bharosa.uio.default.ChallengeSMS.authenticator.device=DevicePinPad bharosa.uio.default.ChallengeEmail.authenticator.device=DevicePinPad
Available challenge device values are DeviceKeyPadFull, DeviceKeyPadAlpha, DeviceTextPad, DeviceQuestionPad, DevicePinPad, and DeviceHTMLControl.
For instructions on customizing, extending, or overriding Oracle Adaptive Access Manager properties, refer to Chapter 12, "Customizing Oracle Adaptive Access Manager."
Instructions to configure user information properties are in the following sections:
For instructions on customizing, extending, or overriding Oracle Adaptive Access Manager properties, refer to Chapter 12, "Customizing Oracle Adaptive Access Manager."
Default configurations for settings for registration of information, preference, and PIN generation are listed as follows:
Contact information registration
bharosa.uio.default.register.userinfo.enabled=false
Contact information preferences
bharosa.uio.default.userpreferences.userinfo.enabled=false
# Length of the Pin bharosa.uio.otp.generate.code.length = 5 # Characters to use when generating the Pin bharosa.uio.otp.generate.code.characters = 1234567890
The AbstractOTPChallengeProcessor
class has a default pin generation method, generateCode
, that you can override to provide your pin generation logic.
Contact information inputs are defined in userinfo.inputs.enum. The enum element is:
bharosa.uio.<application>.userinfo.inputs.enum.<inputname>
bharosa.uio.default.userinfo.inputs.enum.email=1 bharosa.uio.default.userinfo.inputs.enum.email.name=Email Address bharosa.uio.default.userinfo.inputs.enum.email.description=Email Address bharosa.uio.default.userinfo.inputs.enum.email.inputname=email bharosa.uio.default.userinfo.inputs.enum.email.inputtype=text bharosa.uio.default.userinfo.inputs.enum.email.maxlength=40 bharosa.uio.default.userinfo.inputs.enum.email.required=true bharosa.uio.default.userinfo.inputs.enum.email.order=2 bharosa.uio.default.userinfo.inputs.enum.email.enabled=true bharosa.uio.default.userinfo.inputs.enum.email.regex=.+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3} bharosa.uio.default.userinfo.inputs.enum.email.errorCode=otp.invalid.email bharosa.uio.default.userinfo.inputs.enum.email.managerClass=com.bharosa.uio.manager.user.DefaultContactInfoManager
For OTP to be receive via SMS, you must:
To enable OTP profile registration and preference setting, set the following properties to true:
bharosa.uio.default.register.userinfo.enabled
Setting the property to true enables the profile registration pages if the OTP channel is enabled and requires registration.
bharosa.uio.default.userpreferences.userinfo.enabled
Setting the property to true enables the user to set preferences if the OTP channel is enabled and allows preference setting.
If user information registration or user preferences is true, configure input information via enum:
bharosa.uio.default.userinfo.inputs.enum
Table 9-2 OTP Properties for Contact Input
Property | Description |
---|---|
inputname |
Name used for the input field in the HTML form |
inputtype |
Set for text or password input |
maxlength |
Maximum length of user input |
required |
Set if the field is required on the registration page |
order |
The order displayed in the user interface |
regex |
Regular expression used to validate user input for this field |
errorCode |
Error code used to look up validation error message (bharosa.uio.<application ID>.error.<errorCode>) |
managerClass |
java class that implements com.bharosa.uio.manager.user.UserDataManagerIntf (if data is to be stored in Oracle Adaptive Access Manager database this property should be set to com.bharosa.uio.manager.user.DefaultContactInfoManager) |
The following is an example of an enum defining cell phone registration on the OTP registration page of a virtual authentication device:
bharosa.uio.default.userinfo.inputs.enum.mobile=0 bharosa.uio.default.userinfo.inputs.enum.mobile.name=Mobile Phone bharosa.uio.default.userinfo.inputs.enum.mobile.description=Mobile Phone bharosa.uio.default.userinfo.inputs.enum.mobile.inputname=cellnumber bharosa.uio.default.userinfo.inputs.enum.mobile.inputtype=text bharosa.uio.default.userinfo.inputs.enum.mobile.maxlength=15 bharosa.uio.default.userinfo.inputs.enum.mobile.required=true bharosa.uio.default.userinfo.inputs.enum.mobile.order=1 bharosa.uio.default.userinfo.inputs.enum.mobile.enabled=true