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.
Parameters
- pAccount: (optional) 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.
Parameters
- pChallenge: 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.
Parameters
- pUserID: (optional) 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.
Parameters
- pUserID: (optional) 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