Validating Time-Based One-Time Passwords

There are various techniques to enforce multi-factor authentication in web applications. One approach is to use Time-Based One-Time Passwords (TOTPs) as an additional authentication factor. I did a write-up on how to implement Google Authenticator to enforce this style of multi-factor authentication in PeopleSoft. In that article, I use a custom Java class on the app server to implement the TOTP validation algorithm. The problem with this approach is that it causes an undesirable app server dependency. I recently came across an article that demonstrates a JavaScript implementation of the TOTP algorithm. Since we have the ability to run JavaScript on the app server using Java’s built-in JavaScript interpreter, then we can make use of this JavaScript implementation of the TOTP algorithm to validate user submitted TOTPs. I would like to share a handy project that demonstrates this dependency-less TOTP validation technique.

This project demonstrates TOTP validation in the context of a two-factor authentication (2FA) enrollment process. This 2FA enrollment utility can be used in a self service manner to allow users to setup and enable 2FA enforcement on their accounts. If you are interesting in using this project, then perform the following installation steps.

  • Download and Import Project into App Designer
  • Build Project (Create Table)
  • Assign PSM_2FA Permission List to User Role

Login as a privileged user and navigate to Main Menu > PSM Projects > 2FA Enrollment. You will be presented with the following page:

2FA Enrollment

On this page, the user can configure a mobile authenticator application by scanning a QR code. Once configured, the mobile device will generate TOTPs for the user. The user can input a generated TOTP into the input box to verify that their application is generating proper TOTPs. When the user submits the TOTP, the server validates the submitted TOTP with a JavaScript-based algorithm.

Since JavaScript is doing all of the leg work there is not really much to see PeopleCode-wise, but below is the PeopleCode that handles the TOTP validation. The real magic happens in the PSM_SHA and PSM_TOTP HTML Objects.

Function IScript_ValidateTotp()
   
   /* Respond with error if the totp parameter is blank */
   Local string &sTotpInput = %Request.GetParameter("totp");
   If None(&sTotpInput) Then
      %Response.Write("{""status"": ""error"", ""message"": ""No Token Provided""}");
      Return;
   End-If;
   
   /* Get the user's secret ket from the DB */
   Local string &sEncryptedKey, &sKey;
   SQLExec("SELECT PSM_ENCRYPTED_KEY FROM PS_PSM_TOTP_USER WHERE PSM_USERID = :1", %UserId, &sEncryptedKey);
   
   /* Respond with error if the user does not have a secret key in the DB */
   If None(&sEncryptedKey) Then
      %Response.Write("{""status"": ""error"", ""message"": ""No Key Exists""}");
      Return;
   End-If;
   
   /* Decrypt the user's secret key */
   &sKey = Decrypt("", &sEncryptedKey);
   
   /* Use the Java ScriptEngineManager to run the JavaScript program to validate the totp value */
   Local JavaObject &manager = CreateJavaObject("javax.script.ScriptEngineManager");
   Local JavaObject &engine = &manager.getEngineByName("JavaScript");
   Local string &program = GetHTMLText(HTML.PSM_SHA) | GetHTMLText(HTML.PSM_TOTP);
   
   /* Provide the JS program with the required parameters */
   &engine.put("userKey", &sKey);
   &engine.put("userOtp", &sTotpInput);
   &engine.put("w", 5); /* Window: 30s * 5 = 2.5min */
   &engine.eval(&program);
   
   /* Respond with error if the totp value is invalid */
   Local string &result = &engine.get("result").toString();
   If &result <> "true" Then
      %Response.Write("{""status"": ""error"", ""message"": ""Invalid Token Provided""}");
      Return;
   End-If;
   
   /* Generate the url to redirect the user to */
   Local string &sUrl = GenerateScriptContentURL(%Portal, %Node, Record.WEBLIB_PTBR, Field.ISCRIPT1, "FieldFormula", "IScript_StartPage");
   
   /* Respond with success message */
   %Response.Write("{""status"": ""success"", ""message"": ""Token is Valid"", ""redirectUrl"": """ | &sUrl | """}");
   
End-Function;

By using Java’s built-in JavaScript interpreter, we are able to run JavaScript on the app server to validate TOTPs rather than relying on a custom Java class to do this. This technique reduces server side dependencies and results in a more easily manged solution. I look forward to discovering and sharing the integration of more JavaScript-based tools into PeopleSoft using this technique.

Comments

raj

Hi Colton,

thank you for this innovative idea. i have a question , is there any license involved for google authenticator? can oracle authenticator be used here? is it safe to use this functionality in production environment? please let me know.

Colton Fischer

I have not had to deal with any sort of licensing when I have used Google Authentication to generate Time-Based One-Time Passwords. My understanding of Oracle Authenticator is that it implements the same algorithm as other TOTP generating applications. This means that Oracle Authenticator should work the same as other apps like Google Authenticator, FreeOTP, Authy, etc.

The safety of using the TOTP technique for enforcing multi-factor authentication really depends on your implementation. The biggest risk that I have found with TOTP MFA is losing the shared secret key on the client or server. If the client “loses” their secret key, then you will need to have a secure process to verify the user’s identity and provision them a new secret key. If the server reveals/loses a user’s secret key, then the user’s second factor for authentication should be considered compromised and a new secret key should be provisioned.

When it comes to application security, there is never really a silver bullet and using TOTPs for MFA is no exception. Please let me know if you have any other questions and I will be happy to answer.

Leave a comment

Your email address will not be published. Required fields are marked *

Loading...