One-Time Password Authentication

The authentication library incorporates two-factor authentication using OTP (currently TOTP conforming to RFC6238 TOTP standards is supported) and QR-codes. In case you would like to do without all the library code unrelated to OTP you can load the OTP helper instead and deal with the helper handlers.

Note: OTP authentication requires LiveCode's QR Code Generator Library. Please read about integration of the QR code library in chapter OTP Helper.

Activation

To activate two-factor authentication set sAuthenticationConf["otpEnabled"] in application/config/authentication.lc to TRUE.

OTP Authentication Table

In order to use two-factor authentication you need one more table in addition to the 4 tables defined in chapter Authentication Tables. This is a child table of the "users" table. Name this table "otp" according to default settings otherwise you need to adjust the authentication settings.

The MySQL version:

# Table structure for otp
# ------------------------------------------------------------

CREATE TABLE `otp` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `userId` mediumint(8) unsigned NOT NULL,
  `key` tinytext character set utf8 collate utf8_unicode_ci NOT NULL,
  `time` int(11) unsigned default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

The PostgreSQL version:

# Table structure for otp
# ------------------------------------------------------------

CREATE TABLE "otp" (
  "id" SERIAL NOT NULL,
  "userId" integer NOT NULL REFERENCES "users" ON UPDATE CASCADE ON DELETE CASCADE,
  "key" text NOT NULL,
  "time" int,
  PRIMARY KEY("id"),
  CONSTRAINT "check_id" CHECK(id >= 0),
  CONSTRAINT "check_userId" CHECK("userId" >= 0),
);

The SQLite version:

# Table structure for otp
# ------------------------------------------------------------

CREATE TABLE "otp" (
  "id" integer NOT NULL ON CONFLICT ABORT PRIMARY KEY AUTOINCREMENT,
  "userId" integer(8) NOT NULL,
  "key" text NOT NULL,
  "time" integer(11) DEFAULT NULL,
  CONSTRAINT "Foreign_UsersID" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);

OTP Authentication Handler Reference

rigAuthGenerateQR(pAccount)

Use this function to create a QR code based on the otpauth:// URI scheme and get the associated base32 encoded shared secret.

The parameter is optional. It is a string which identifies the user. Usually this is an email address or a username. If no value is provided the identity of the currently logged in user is used.

The function returns an array with indices: "key" containing the base32 encoded shared secret, and "qr" containing the QR code ready to be displayed in a web page. If QR code generation fails empty array values are returned.

Example:

put rigAuthGenerateQR() into tQRa
put tQRa["key"] into gData["key"]
put tQRa["qr"] into gData["qrCode"]

rigAuthOTPkeysMatch(pChallenge)

This function allows you to compare user supplied authentication code (OTP) with a generated code based on the user's database values.

The parameter is the one-time password supplied by the user.

The function returns TRUE in case both codes match, otherwise it returns FALSE.

Example:

put rigVarPost("authcode") into tAuthCode

if tAuthCode <> False then
  # COMPARE tAuthCode WITH GENERATED CODE
  if rigAuthOTPkeysMatch(tAuthCode) is TRUE then
    # REDIRECT TO SUCCESS PAGE
    rigRedirect "otpCheckPassed"
  else
    -- your code to display the OTP authentication form again
  end if
end if

rigAuthUserHasOTP(pUserID)

Use this function to check if a user has set up OTP authentication.

The parameter is optional. It is a user ID (integer) which is stored in the "id" field of the "users" table. If no value is provided the ID of the currently logged in user is used.

The function returns "pendingOTPsetup" in case the user has requested a QR code to set up two-factor authentication, but has not yet transferred a valid one-time password. In case the setup was successfully finished the function returns TRUE. If there is no OTP entry for the particular user the function returns FALSE.

Example:

# FORM VALIDATION
if rigFormValidRun("auth/login") is TRUE then

  # CHECK IF USER IS LOGGING IN
  put FALSE into tRemember
  # CHECK IF THERE IS A POST VARIABLE remember
  put rigVarPost("remember[]") into tPostRemember -- remember check box

  if tPostRemember <> FALSE then
    # CHECK VALUE
    if tPostRemember[1] is 1 then
      put TRUE into tRemember
    end if
  end if

  get rigAuthLogin(rigVarPost("identity"), rigVarPost("password"), tRemember)

  # IF LOGIN IS SUCCESSFUL CHECK IF USER HAS SET UP TWO-FACTOR AUTHENTICATION
  if it is TRUE then
    if rigAuthUserHasOTP() is TRUE then
      # REDIRECT TO OTP FORM
      rigRedirect "otp"
    else
      # REDIRECT TO PROTECTED PAGE
      put rigAuthMessages() into tMessages
      rigSetSessFlashdata "message", tMessages
      rigRedirect "/protected"
    end if

  else
    # IF LOGIN IS UNSUCCESSFUL REDIRECT BACK TO THE LOGIN PAGE
    put rigAuthErrors() into tErrors
    rigSetSessFlashdata "message", tErrors
    rigRedirect "auth/login"
  end if

else
  # THE USER IS NOT LOGGING IN, SHOW THE LOGIN PAGE
  # SHOW VALIDATION ERRORS OR FLASH DATA IF THERE IS ANY

end if

rigAuthClearOTP(pUserID)

This function deletes a user's OTP database entry.

The parameter is optional. It is a user ID (integer) which is stored in the "id" field of the "users" table. If no value is provided the ID of the currently logged in user is used.

The function returns TRUE in case the operation was successful, otherwise it returns FALSE.

rigAuthLoggedIn()

Check to see if a user is logged in.

This function returns TRUE if the user is logged in, otherwise it returns FALSE.

In case two-factor authentication is activated the function may return "pending" if the user logged in successfully but has still not transferred a valid one-time password. If the user logged in successfully and requested a QR code to set up two-factor authentication but still not transferred a valid one-time password the function returns "pendingOTPsetup".

Example:

# CHECK IF USER IS LOGGED IN, CAN BE "pending" IF USER HAS SETUP OTP
# AND HAS NOT YET SUPPLIED A VALID OTP
put rigAuthLoggedIn() into tLoggedIn

if tLoggedIn is FALSE then
  # REDIRECT TO LOGIN PAGE AND KEEP FLASHDATA IN CASE THERE IS ANY
  if rigSessFlashdata("message") <> FALSE then
    rigKeepSessFlashdata "message"
  end if

  rigRedirect "auth/login"

else if rigAuthIsAdmin() is FALSE then

  if (tLoggedIn <> "pending") and (tLoggedIn <> "pendingOTPsetup") then
    # REDIRECT TO HOME PAGE BECAUSE USER IS NOT ALLOWED TO VIEW ADMIN CONTENT
    rigRedirect "/"
  else
    # USER HAS NOT YET SUPPLIED A VALID OTP
    if tLoggedIn is "pendingOTPsetup" then
      # OTP AUTHENTICATION SETUP INCOMPLETE
      rigRedirect "otp/setup"
    else
      rigRedirect "otp/"
    end if
  end if

else -- if tLoggedIn is FALSE then
  # ADMIN CONTENT
  #
  if tLoggedIn <> "pending" then
  -- your code . . .
  end if
end if