API Tutorials - TextKey APIs
How to integrate TextKey
Standard authentication involves entering a login name and password and validating those credentials. TextKey
User/Password Verification
Step 1 involves a sites standard first pass authentication which in this example is a username and a password. Most of the code in Step 1 is sample code and can be swapped out.
Polling For The TextKey
Step 2 follows once the first pass authentication has completed and a valid TextKey
User Sends TextKey
In Step 3, the user would text the TextKey
This working PHP example is available on github. The package will include instructions on what configuration options need to be changed to use to use your own API Key and local setup.
Step 1 - Handling First Level Authentication - User/Password Verification
Most of the sample code in Step 1 is being used to show how to integrate the TextKey
The three key elements in this Step are:
- Integrating getting a TextKey
after validating the username/password - This involves passing in the userid used when registering the user with TextKey
- Saving the TextKey
values for later validation (i.e. in Session Variables) - TextKey
- TextKey
Validation Code - Short Code
- Returning the TextKey
values back so that they can be used/displayed to the user - TextKey
- Short Code
NOTE: This is strickly being used an a example and you can use whatetever first pass authentication handling you prefer.
Login Form - tutoriallogin.php
This is a basic login form. It uses jQuery, Simple Modal (i.e. a jQuery plugin to dispay messaging), and login.js (i.e. a Javascript login handler specific to this code example).
NOTE: There is nothing in this form that is TextKey
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <head> <title>TextKey Tutorial Login</title> <!-- Basic styling --> <link type='text/css' rel="stylesheet" href="css/textkey.css"> <!-- jQuery --> <script src="http://code.jquery.com/jquery-latest.min.js"></script> <!-- TextKey JS Include to manage the Login Form --> <script type="text/javascript" src="js/login.js"></script> <!-- SimpleModal jQuery Include - Used for modal display --> <script type="text/javascript" src="js/jQuery.simplemodal-1.4.4.js"></script> <!-- TextKey JS Include to handle Polling --> <script type="text/javascript" src="js/textkey.js"></script> <!-- TextKey JS Include to customize the TextKey modal and callback handlers --> <script type="text/javascript" src="js/textkey_custom.js"></script> </head> <body> <form class="form-textkey" id="form-textkey"> <h1>Login</h1> <p> <label for="login">Username</label> <input type="text" name="name" id='login-name' placeholder="Username"> </p> <p> <label for="password">Password</label> <input type="password" name='password' id='login-password' placeholder="Password"> </p> <p> <input type="submit" name="submit" value="Login" class='login-send' onClick="return login.handlelogin();"> </p> </form> </body> </html>
Javascript Form Handler - login.js
login.js is a Javascript login form handler and handles both the login form and subsequent responses. The entire code sample is displayed below however here is the TextKey specific code which will trigger the display of the TextKey SMS message.
The key elements consist of a POST request to server side code that will verify the username/password are valid and then return back the key TextKey
// Submit the form $.ajax({ url: 'login.php', data: $('form').serialize(), type: 'post', cache: false, dataType: 'html', success: function (jsondata) { // Convert to an object data = eval(jsondata); if (typeof(console) !== 'undefined' && console != null) { console.log("data: " + jsondata); console.log(data); }; // Check for a valid login if (data.error == "") { // Set the flag login.loggedin = true; // Set the textkey values login.textkey = data.textkey; login.textkeyVC = data.textkeyVC; login.shortcode = data.shortcode; // Handle the textkey login if (login.loggedin) { textKeyHandler(login.textkey, login.shortcode); }; } else { // Set the error message login.message = data.error; // Show the error message login.showError(); }; }, error: login.error });
login.js
Here is the entire login.js source code.
/* ** ** login.js ** ** This is the basic framework for the login page. ** ** The key here is that the back end login code returns with a payload containing the TextKey to be displayed to the user. ** */ var login = { message: null, textkey: null, textkeyVC: null, shortcode: null, loggedin: false, init: function () { $('#form-textkey').bind('keypress', function (e) { if (e.keyCode == 13) { $('#form-textkey .login-send').trigger('click'); } }); return false; }, handlelogin: function () { // validate form if (login.validate()) { // Submit the form $.ajax({ url: 'login.php', data: $('form').serialize(), type: 'post', cache: false, dataType: 'html', success: function (jsondata) { // Convert to an object data = eval(jsondata); if (typeof(console) !== 'undefined' && console != null) { console.log("data: " + jsondata); console.log(data); }; // Check for a valid login if (data.error == "") { // Set the flag login.loggedin = true; // Set the textkey values login.textkey = data.textkey; login.textkeyVC = data.textkeyVC; login.shortcode = data.shortcode; // Handle the textkey login if (login.loggedin) { textKeyHandler(login.textkey, login.shortcode); }; } else { // Set the error message login.message = data.error; // Show the error message login.showError(); }; }, error: login.error }); } else { // Show the error message login.showError(); } return false; }, handlelogout: function (refresh) { $.ajax({ url: 'logout.php', type: 'post', cache: false, dataType: 'html', success: function (data) { if (data == "") { if (refresh) { location.reload(true); } } else { if (typeof(console) !== 'undefined' && console != null) { console.log("data: " + data); }; }; } }); return false; }, error: function (xhr) { showModal('Login Error...', xhr.statusText, closeModal); return false; }, validate: function () { login.message = ''; if (!$('#form-textkey #login-name').val()) { login.message = 'Username is required.'; return false; }; if (!$('#form-textkey #login-password').val()) { login.message = 'Password is required.'; return false; }; return true; }, showError: function () { showModal('Login Error...', login.message, closeModal); } }; login.init();
PHP Tutorial Configuration Settings - config.php
You will need to replace YOUR_API_KEY with your API Key.
/* ** config.php */ /* ** TextKey Settings */ define('TK_API', 'YOUR_API_KEY'); define('TK_DISPLAY_API', TK_API); // Whether or not the passwords are being hashed define('TK_ISHASHED', '0');
PHP Login Handler - login.php
login.php is a PHP login form handler. The key elements consist of validating the user/password, getting a valid TextKey
// Include configuration settings include_once("config.php"); // Add TextKey PHP REST Library include_once("textkey_rest.php"); // Add TextKey Session Handler require_once("textkeysite.php"); // Code to handle the login for the specific site function user_password_login($name, $password) { $userid = ""; // // NOTE: This is where you would hook into your own internal authentication handler and return back the user id to // handle assigning a TextKey // return $name; } // Validate the user login and get the TextKey dialog to display in the browser function login_user() { global $textkeysite; // Setup $error_msg = ''; // Create the textkey object $tk = new textKey(TK_API); // Get the passed in info. $name = isset($_POST["name"]) ? $_POST["name"] : ""; $password = isset($_POST["password"]) ? $_POST["password"] : ""; // HANDLE THE USER/LOGIN AUTHENTICATION HERE // NOTE: The $textkey_userid value should be the user id that was used to register the specific user in the // TextKey Database (i.e. via the TextKey Administration Application or via the registerTextKeyUser API Call // or registerTextKeyUserCSA API Call). $textkey_userid = user_password_login($name, $password); // If the username/password combination is validated then continue if ($textkey_userid != "") { // Handle setting the sesssion info. $textkeysite->setPassedLoginCheck($name, $textkey_userid); // Handle getting a valid TextKey using the user id $textkey_result = $tk->perform_IssueTextKeyFromUserId($textkey_userid, TK_ISHASHED); if ($textkey_result->errorDescr == "") { // No error so setup the return payload $reply_msg = '({"error":"", "textkey":' . json_encode($textkey_result->textKey) . ', "textkeyVC":' . json_encode($textkey_result->validationCode) . ', "shortcode":' . json_encode('81888') . '})'; // Handle setting the textkey sesssion info. $textkeysite->setTextKeyInfo($textkey_userid, $textkey_result->textKey, $textkey_result->validationCode, '81888'); // Return the valid info. return $reply_msg; } else { $error_msg = $textkey_result->errorDescr; } } else { $error_msg = "The name or password did not match. Please try again..."; }; // Handle clearing the sesssion info. $textkeysite->endSession(); // Return the error $error_msg = '({"error":' . json_encode($error_msg) . '})'; return $error_msg; } // Create the session handling object $textkeysite = new textkeysite(); // Init the session values $textkeysite->initSessionInfo(); // Login the user $login_payload = login_user(); // Return the resulting payload echo $login_payload; exit;
textkeysite.php
Here is the entire textkeysite.php source code which consists of a class that sets and holds session information.
class textkeysite { /* ** Init the session information */ function initSessionInfo() { // Start the login session if (!isset($_SESSION)) { session_start(); }; // User login Info. $_SESSION['username'] = NULL; $_SESSION['userid'] = NULL; // TextKey info. $_SESSION['textkeycheckuserid'] = NULL; $_SESSION['textkey'] = NULL; $_SESSION['textkeyvc'] = NULL; $_SESSION['shortcode'] = NULL; // 2 pass flags $_SESSION['passedcheck1'] = false; $_SESSION['passedcheck2'] = false; } /* ** Set the session info. after the user passes the first check (i.e. Login/Password) */ function setPassedLoginCheck($username, $userid) { // Start the login session if (!isset($_SESSION)) { session_start(); }; // User login Info. $_SESSION['username'] = $username; $_SESSION['userid'] = $userid; // 2 pass flags $_SESSION['passedcheck1'] = true; } /* ** Set the textkey session info. after the user passes the first check (i.e. Login/Password) */ function setTextKeyInfo($textkeycheckuserid, $textkey, $textkeyvc, $shortcode) { // Start the login session if (!isset($_SESSION)) { session_start(); }; // TextKey info. $_SESSION['textkeycheckuserid'] = $textkeycheckuserid; $_SESSION['textkey'] = $textkey; $_SESSION['textkeyvc'] = $textkeyvc; $_SESSION['shortcode'] = $shortcode; } /* ** Set the session info. after the user passes the second check (i.e. textkey authentication) */ function setPassedTKCheck($username, $userid) { // Start the login session if (!isset($_SESSION)) { session_start(); }; // 2 pass flags $_SESSION['passedcheck2'] = true; } /* ** Check to see if the first check passed (i.e. Login/Password) */ function getPassedLoginCheck() { // Start the login session if (!isset($_SESSION)) { session_start(); } // If the username/login check has not passed them return false if ($_SESSION['passedcheck1'] != true) { return false; } return true; } /* ** Check to see if the second check passed (i.e. textkey authentication) */ function getPassedTKCheck() { // Start the login session if (!isset($_SESSION)) { session_start(); } // If the TextKey check has not passed then return false if (($_SESSION['passedcheck1'] != true) || ($_SESSION['passedcheck2'] != true)) { return false; } return true; } /* ** End the entire session */ function endSession() { // Start the login session if (!isset($_SESSION)) { session_start(); } // Clear out all of the session variables $_SESSION['username'] = NULL; $_SESSION['userid'] = NULL; $_SESSION['textkeycheckuserid'] = NULL; $_SESSION['textkey'] = NULL; $_SESSION['textkeyvc'] = NULL; $_SESSION['shortcode'] = NULL; $_SESSION['passedcheck1'] = false; $_SESSION['passedcheck2'] = false; } /* ** Session get function calls */ function get_userName() { return isset($_SESSION['username'])?$_SESSION['username']:''; } function get_userId() { return isset($_SESSION['userid'])?$_SESSION['userid']:''; } function get_tkuserId() { return isset($_SESSION['textkeycheckuserid'])?$_SESSION['textkeycheckuserid']:''; } function get_textkey() { return isset($_SESSION['textkey'])?$_SESSION['textkey']:''; } function get_textkeyvc() { return isset($_SESSION['textkeyvc'])?$_SESSION['textkeyvc']:''; } function get_textkeyshortcode() { return isset($_SESSION['shortcode'])?$_SESSION['shortcode']:''; } }
Step 2 - Displaying the TextKey message.
The callback from the Login form submit now needs to handle displaying a message telling the user what TextKey
The three key elements in this Step are:
- Making a call to the textKeyHandler(textkey, shortcode) JS function with the TextKey
- Customizing how you want the modal dialog to look
- Setting up a success and failure JS callback handler
The ajax response from the Login form will trigger the modal display via the textKeyHandler(textkey, shortcode) call. This function is defined in the textkey.js file and allows for customization of the modal look and feel, time to poll and how to handle the success and failed callbacks.
success: function (jsondata) { // Convert to an object data = eval(jsondata); if (typeof(console) !== 'undefined' && console != null) { console.log("data: " + jsondata); console.log(data); }; // Check for a valid login if (data.error == "") { // Set the flag login.loggedin = true; // Set the textkey values login.textkey = data.textkey; login.textkeyVC = data.textkeyVC; login.shortcode = data.shortcode; // Handle the textkey login if (login.loggedin) { textKeyHandler(login.textkey, login.shortcode); }; } else { // Set the error message login.message = data.error; // Show the error message login.showError(); }; },
These are the 5 key JS includes that are used to both display the TextKey
The 5 JS includes in this Step are:
- jQuery - no need to include if this is already part of your application/site
- jQuery SimpleModal - jQuery plugin to handle modal dialogs
- login.js - handles the login form
- textkey_custom.js - handles customizing and displaying the TextKey
modal message and the success and failure callbacks - textkey.js - handles the TextKey
polling for a successful or failed authentication
<!-- jQuery --> <script src="http://code.jquery.com/jquery-latest.min.js"></script> <!-- TextKey JS Include to manage the Login Form --> <script type="text/javascript" src="js/login.js"></script> <!-- SimpleModal jQuery Include - Used for modal display --> <script type="text/javascript" src="js/jQuery.simplemodal-1.4.4.js"></script> <!-- TextKey JS Include to handle Polling --> <script type="text/javascript" src="js/textkey.js"></script> <!-- TextKey JS Include to customize the TextKey modal and callback handlers --> <script type="text/javascript" src="js/textkey_custom.js"></script>
textkey_custom.js has 3 functions that are used in the login flow.
The functions are:
- textKeyHandler - displays the TextKey
modal and defines the time to poll for a valid text message - loginSuccess - the callback handler for a successful login
- loginFailed - the callback handler for a failed login
The textKeyHandler function will display a modal dialog that looks something like this:
The elements that can be customized via JS functions are:
Function Name | Description |
---|---|
setTextKeyHTML | This is the HTML content displayed in the TextKey Modal. |
setTextKeyContainerCss | This is the container styling for the TextKey Modal. |
setTextKeyDataCss | This is the data styling for the TextKey Modal. |
setTextKeyOverlayCss | This is the overlay styling for the TextKey Modal. |
setPollTime | This defines the time the user is given to send the TextKey code for authentication (in Seconds - Max is 180 seconds). |
NOTE: The server side handling should validate true authentication. The callback functions should facilitate that final authentication. See Step 4 for an example.
/* ** ** textkey_custom.js ** ** These are the functions to display the TextKey message and subsequent success or failure callbacks. ** */ // Call to handle the successful authentication function loginSuccess(tkTextKeyMessage, tkTextKeyStatus) { window.location = 'index.php' }; // Call to handle the failed authentication function loginFailed(tkTextKeyMessage, tkTextKeyStatus) { }; // Simple Call to handle the login function textKeyHandler(textKey, shortcode) { // Customize the look and feel setTextKeyHTML('<div id="tkmessage-container"><h1>Mobile Authentication...</h1><div class="poweredby"><img src="images/poweredbylocked.gif" alt="Powered by TextPower" border="0" align="absmiddle"></div><div id="tkSound"></div><div id="tkTime"></div></div>'); setTextKeyContainerCss({'height':'260px', 'width':'625px', 'font': '16px/22px \'Raleway\', \'Lato\', Arial, sans-serif', 'color':'#000000', 'background-color':'#000', 'padding':'10px', 'background-color':'#F1F1F1', 'margin':'0', 'padding':'0', 'border':'4px solid #444'}); setTextKeyDataCss({'padding':'8px'}); setTextKeyOverlayCss({'background-color':'#AAA', 'cursor':'wait'}); // Set the total time to wait for TextKey to 120 seconds setPollTime(120); // Show the TextKey Modal and handle the checking showTKModal(textKey, shortcode, loginSuccess, loginFailed); };
textkey.js contains all of the polling handling code and just needs to be included. The textKeyHandler function call in textkey_custom.js will take care of everything.
/* ** ** textkey.js ** ** These are the TextKey js handling functions. They handle the interaction with the backend handler code as well as the client side interaction/integration. ** */ /* ** ** TextKey modal default options ** ** These settings define what shows up in the TextKey Modal dialog. ** ** NOTES: ** ** urlTextKeyAuth: This is the polling handler which checks to see if the TextKey has been received or not. ** pollFreq: This defines how often to poll the back end handler (in Milliseconds). ** pollTime: This defines the time the user is given to send the TextKey code for authentication (in Seconds - Max is 180 seconds). ** tkHTML: This is the HTML content displayed in the TextKey Modal. Overide this with the setTextKeyHTML call. ** tkcontainerCss: This is the container styling for the TextKey Modal. Overide this with the setTextKeyContainerCss call. ** tkdataCss: This is the data styling for the TextKey Modal. Overide this with the setTextKeyDataCss call. ** tkoverlayCss: This is the overlay styling for the TextKey Modal. Overide this with the setTextKeyOverlayCss call. ** ** See Simple Modal Documentation for more info: http://www.ericmmartin.com/projects/simplemodal/ ** ** The remaining elemtns are used to hold inforation/state. ** */ error_message = ''; tkSettings = { urlTextKeyAuth: 'textkeycheck.php?callback=?', pollFreq: 5000, pollTime: 120, tkHTML: '<div id="simplemodal-container"><h3>Waiting for TextKey Authentication...</h3><div id="tkTime"></div></div>', tkcontainerCss: {'height':'100px', 'width':'600px', 'color':'#bbb', 'background-color':'#333', 'border':'4px solid #444', 'padding':'12px'}, tkdataCss: {'padding':'8px'}, tkoverlayCss: {'background-color':'#000', 'cursor':'wait'}, tkTextKey: '', tkShortCode: '', tkTextKeyUnloackSound: 'audio/door_open.mp3', tkTextKeySuccess: false, tkTextKeyMessage: '', tkTextKeyStatus: false, tkFnSuccess: null, tkFnFail: null }; /* ** Standard modal dialog handling */ function closeModal() { $.modal.close(); }; function showModal(title, msg, md_closeProc, md_height, md_width) { if (typeof(md_height) === "undefined") { md_height = 160; }; if (typeof(md_width) === "undefined") { md_width = 625; }; // Build the hmtl to display tkModalHTML = ('<div id="basic-modal-content"><h3>'+title+'</h3><p>'+msg+'</p><div class="modal-buttons"><span><a class="modal-button" onclick="javascript:$.modal.close();">Close</a></span></div></div><!-- preload the images --><div style="display:none"><img src="./images/x.png" alt="" /></div>'); $.modal( tkModalHTML, { onClose: md_closeProc, containerCss:{ height:md_height, width:md_width }, }); return true; }; /* * TextKey Change Standard Settings * */ function setTextKeyAuth(urlTextKeyAuth) { tkSettings.urlTextKeyAuth = urlTextKeyAuth; } function setPollFreq(pollFreq) { tkSettings.pollFreq = pollFreq; } function setPollTime(newpollTime) { tkSettings.pollTime = newpollTime; pollTime = tkSettings.pollTime; } function setTextKeyHTML(tkHTML) { tkSettings.tkHTML = tkHTML; } function setTextKeyContainerCss(tkcontainerCss) { tkSettings.tkcontainerCss = tkcontainerCss; } function setTextKeyDataCss(tkdataCss) { tkSettings.tkdataCss = tkdataCss; } function setTextKeyOverlayCss(tkoverlayCss) { tkSettings.tkoverlayCss = tkoverlayCss; } /* * TextKey Globals * */ var pollTime = tkSettings.pollTime; var tkTextKeyHandled = false; var timer_is_on = 0; var t; /* * * Hide and show the scoll bar * */ function hideScrollBar() { $("body").css("overflow", "hidden"); } function showScrollBar() { $("body").css("overflow", "auto"); } /* * * Close the TextKey dialog * */ function closeTKModal() { // Set the handled flag tkTextKeyHandled = true; // Reset the poll time pollTime=tkSettings.pollTime; doTimer(1); // Clear out the messages $("#tkTime").text(""); // Hide the modal dialog $.modal.close(); // Show the scroll bar showScrollBar(); } /* * * TextKey Post Handler * */ function postRedirect(redirectURL) { var tkredirectform = $('<form id="tkredirectform" action="' + redirectURL + '" method="post">' + '<input type="text" name="textkeymessage" value="' + tkSettings.tkTextKeyMessage + '" />' + '<input type="text" name="textkeystatus" value="' + tkSettings.tkTextKeyStatus + '" />' + '</form>'); $('body').append(tkredirectform); $('#tkredirectform').submit(); } /* * * TextKey Login was succesful * */ function completeLogin() { // Set flag to success tkSettings.tkTextKeySuccess = true; // Hide the modal dialog closeTKModal(); // Handle the Client Call back or URL redirect if ($.isFunction(tkSettings.tkFnSuccess)) { tkSettings.tkFnSuccess(tkSettings.tkTextKeyMessage, tkSettings.tkTextKeyStatus); } else { postRedirect(tkSettings.tkFnSuccess); } } /* * * TextKey Login failed * */ function errorLogin() { // Handle the Session login.handlelogout(false); // Hide the modal dialog closeTKModal(); // Handle the Client Call back or URL redirect if ($.isFunction(tkSettings.tkFnFail)) { tkSettings.tkFnFail(tkSettings.tkTextKeyMessage, tkSettings.tkTextKeyStatus); } else { postRedirect(tkSettings.tkFnFail); } } /* * * Show an error modal * */ function errorShow() { showModal('Login Error...', 'The TextKey authentication did not complete. You can try again if you think this was an error.', closeModal); error_message = ""; } /* * * Show the TextKey modal dialog * */ function showTKModal(TextKey, ShortCode, fnCallSuccess, fnCallFailed) { // Check for valid call backs if (typeof(fnCallSuccess) === "undefined") { alert("Please make sure you pass in your success and failure handler parameters..."); return; }; if (typeof(fnCallFailed) === "undefined") { alert("Please make sure you pass in your success and failure handler parameters..."); return; }; // Set the tkSettings values tkSettings.tkTextKey = TextKey; tkSettings.tkShortCode = ShortCode; tkSettings.tkFnSuccess = fnCallSuccess; tkSettings.tkFnFail = fnCallFailed; // Set the status & handls variables tkSettings.tkTextKeySuccess = false; tkSettings.tkTextKeyMessage = ''; tkSettings.tkTextKeyStatus = false; tkTextKeyHandled = false; // Hide the scroll bar hideScrollBar(); // Show the dialog $.modal( tkSettings.tkHTML, { onClose: function (dialog) { dialog.data.fadeOut('slow', function () { dialog.container.slideUp('slow', function () { dialog.overlay.fadeOut('slow', function () { // Handle the user initiated close if (!(tkTextKeyHandled)) { // Set the status values tkSettings.tkTextKeyMessage = 'User cancelled the TextKey check...'; tkSettings.tkTextKeyStatus = false; // Handle the failed login errorLogin(); // Show the dialog error_message = tkSettings.tkTextKeyMessage; setTimeout("errorShow()", 1000); } else { $.modal.close(); if (error_message != "") { setTimeout("errorShow()", 1000); }; }; }); }); }); }, position: ["15%",], containerCss: tkSettings.tkcontainerCss, dataCss: tkSettings.tkdataCss, overlayCss: tkSettings.tkoverlayCss } ); // Hide the close button $('#simplemodal-container a.modalCloseImg').hide(); // Start the TextKey handler sendTextKeyCheck(); } /* * * Handle the JS timer * */ function doTimer(clr) { // Handle cancelling the timer if(clr == 1 && timer_is_on == 1) { clearTimeout(t); timer_is_on = 0; }; // Handle starting the timer if (clr == 0) { timer_is_on = 1; t = setTimeout("sendTextKeyCheck()",tkSettings.pollFreq); } } // Validate the TextKey and finalize the login function validateTextKey() { $.ajax({ url: 'loginvalidate.php', type: 'post', cache: false, dataType: 'html', success: function (jsondata) { // Convert to an object data = eval(jsondata); if (typeof(console) !== 'undefined' && console != null) { console.log("data: " + jsondata); console.log(data); }; // Check for a valid login if (data.error == "") { handleUnlock(); $("#tkTime").html('<p><span class="bigmsg colorsuccess">SUCCESS!</span></br><p>TextKey accepted. Completing login now...</p>'); tkSettings.tkTextKeyMessage = 'SUCCESS! TextKey accepted and verified.'; setTimeout("completeLogin()",3000); } else { $("#tkTime").html('<p><span class="bigmsg colorfailed">FAILED!</span></br><p>'+data.error+'</p>'); tkSettings.tkTextKeyMessage = 'FAILED! TextKey was not verified.'; error_message = 'ERROR: '+data.error; setTimeout("errorLogin()",3000); }; } }); }; function changeLock() { $(".poweredby img").attr("src", "images/poweredbyunlocked.gif"); } // Handle the unlock image/sound function handleUnlock() { // Switch to unlocked logo if there if (tkSettings.tkTextKeyUnloackSound != "") { $("#tkSound").html('<audio src="'+tkSettings.tkTextKeyUnloackSound+'" autoplay></audio>'); }; setTimeout("changeLock()",1500); } /* * * The TextKey Handler * */ function sendTextKeyCheck() { // Setup the data payload var DTO = { 'textKey': tkSettings.tkTextKey }; // Show the results in the console if (typeof(console) !== 'undefined' && console != null) { console.log(DTO); console.log(JSON.stringify(DTO)); }; try { // Made a request to check for response $.getJSON(tkSettings.urlTextKeyAuth, DTO, function(responseS) { if (typeof(console) !== 'undefined' && console != null) { console.log(responseS); }; if (responseS.errorDescr) { error_message = 'ERROR: '+responseS.errorDescr; tkSettings.tkTextKeyMessage = error_message; $("#tkTime").html('<p>'+error_message+'</p>'); doTimer(1); setTimeout("errorLogin()",3000); } else { // Look at the response tkSettings.tkTextKeyStatus = responseS.ActivityDetected; // Show the results in the console if (typeof(console) !== 'undefined' && console != null) { console.log('tkSettings.tkTextKeyStatus: ' + tkSettings.tkTextKeyStatus); }; // Check for an expired textKey if (responseS.TimeExpired) { error_message = '<span class="bigmsg colorfailed">FAILED!</span></br><p>Time for response has expired.<p>'; tkSettings.tkTextKeyMessage = error_message; $("#tkTime").html('<p>'+error_message+'</p>'); doTimer(1); setTimeout("errorLogin()",10000); } else { // Check for activity detected if (responseS.ActivityDetected) { $("#tkTime").html('<p><span class="bigmsg colorverify">TEXTKEY RECEIVED</span></br><p>Completing verification now...</p>'); tkSettings.tkTextKeyMessage = 'Verifying TextKey...'; // Validate the TextKey to make sure it was legal doTimer(1); setTimeout("validateTextKey()",3000); } else { $("#tkTime").html('<p>To complete this login, text the following code to '+tkSettings.tkShortCode+':</p><p id="tkCode">' + tkSettings.tkTextKey + '</p><p>within '+pollTime+' seconds...</p>'); pollTime -= 5; } } } }); } catch(err) { error_message = 'ERROR: '+err; tkSettings.tkTextKeyMessage = error_message; $("#tkTime").html('<p>'+error_message+'</p>'); doTimer(1); setTimeout("errorLogin()",3000); }; // Start the timer doTimer(0); }
Step 3 - User Sends the TextKey .
Once the textKeyHandler function is called, it will display a modal dialog that looks something like this:
At that point the user will need to text the TextKey
Depending on what happens, here are some of the possible outcomes:
- The user successfully sends the correct TextKey
from the verified device - An success message witll be displayed and the loginSuccess callback function will be called
- The user successfully sends the correct TextKey
from a different device - An error message witll be displayed and the loginFailed callback function will be called
- Nothing was texted and the polling timeframe completed.
- An error message witll be displayed and the loginFailed callback function will be called
Step 4 - Finalizing user authentication.
A successful TextKey
This might include a call to the server to serve the correct page, a redirect to a successful login page (i.e. like a users profile or preferences page), or a reload of the same page where the server side code will use server side SESSION variables to make sure the 2nd level authentication was indeed valid.
An example might be to redirect to the home page and let the server side SESSION variables verify that the user is logged in and then handle the logged in case for the page.
// Call to handle the successful authentication function loginSuccess(tkTextKeyMessage, tkTextKeyStatus) { // Handle the finalized login window.location = 'index.php' };
Once the dialog has been displayed, the JS code will begin polling for receipt of that TextKey every 5 seconds.
Every 5 seconds, the JS function sendTextKeyCheck() in textkey.js makes a call to textkeycheck.php and then evaluates the response to decide what to do next.
// Made a request to check for response $.getJSON(tkSettings.urlTextKeyAuth, DTO, function(responseS) { HANDLE CHECKING THE RESPONSE TO THE POLLING CHECK });
There are 3 states that can happen when polling:
- An error
- A response saying that activty has been detected
- An expiration timeout
if (responseS.errorDescr) { error_message = 'ERROR: '+responseS.errorDescr; tkSettings.tkTextKeyMessage = error_message; $("#tkTime").html('<p>'+error_message+'</p>'); doTimer(1); setTimeout("errorLogin()",3000); } else { // Look at the response tkSettings.tkTextKeyStatus = responseS.ActivityDetected; // Show the results in the console if (typeof(console) !== 'undefined' && console != null) { console.log('tkSettings.tkTextKeyStatus: ' + tkSettings.tkTextKeyStatus); }; // Check for an expired textKey if (responseS.TimeExpired) { error_message = '<span class="bigmsg colorfailed">FAILED!</span></br><p>Time for response has expired.<p>'; tkSettings.tkTextKeyMessage = error_message; $("#tkTime").html('<p>'+error_message+'</p>'); doTimer(1); setTimeout("errorLogin()",10000); } else { // Check for activity detected if (responseS.ActivityDetected) { $("#tkTime").html('<p><span class="bigmsg colorverify">TEXTKEY RECEIVED</span></br><p>Completing verification now...</p>'); tkSettings.tkTextKeyMessage = 'Verifying TextKey...'; // Validate the TextKey to make sure it was legal doTimer(1); setTimeout("validateTextKey()",3000); } else { $("#tkTime").html('<p>To complete this login, text the following code to '+tkSettings.tkShortCode+':</p><p id="tkCode">' + tkSettings.tkTextKey + '</p><p>within '+pollTime+' seconds...</p>'); pollTime -= 5; } } }
The server side polling code in textkeycheck.php relies on the pollForIncomingTextKey API Call to check for a received TextKey.
// Include configuration settings include_once("config.php"); // Add TextKey PHP REST Library include_once("textkey_rest.php"); // Get the params $textkey = $_REQUEST['textKey']; // Create a TK object $tk = new textKey(TK_API); // Handle the operation $textkey_result = $tk->perform_PollForIncomingTextKey($textkey); // Handle the results if ($textkey_result->errorDescr == "") { echo $_REQUEST['callback'].'('.json_encode($textkey_result).')'; } else { echo $_GET['callback'] . "({\"error\":\"". $textkey_result->errorDescr ."\"})"; }
Once we know that a TextKey has been received, a validation check takes place to finalize the TextKey
// Check for activity detected if (responseS.ActivityDetected) { $("#tkTime").html('<p><span class="bigmsg colorverify">TEXTKEY RECEIVED</span></br><p>Completing verification now...</p>'); tkSettings.tkTextKeyMessage = 'Verifying TextKey...'; // Validate the TextKey to make sure it was legal doTimer(1); setTimeout("validateTextKey()",3000); }
This triggers a call to validateTextKey() which ensures that the TextKey
// Validate the TextKey and finalize the login function validateTextKey() { $.ajax({ url: 'loginvalidate.php', type: 'post', cache: false, dataType: 'html', success: function (jsondata) { // Convert to an object data = eval(jsondata); if (typeof(console) !== 'undefined' && console != null) { console.log("data: " + jsondata); console.log(data); }; // Check for a valid login if (data.error == "") { handleUnlock(); $("#tkTime").html('<p><span class="bigmsg colorsuccess">SUCCESS!</span></br><p>TextKey accepted. Completing login now...</p>'); tkSettings.tkTextKeyMessage = 'SUCCESS! TextKey accepted and verified.'; setTimeout("completeLogin()",3000); } else { $("#tkTime").html('<p><span class="bigmsg colorfailed">FAILED!</span></br><p>'+data.error+'</p>'); tkSettings.tkTextKeyMessage = 'FAILED! TextKey was not verified.'; error_message = 'ERROR: '+data.error; setTimeout("errorLogin()",3000); }; } }); };
In order to verify that the received TextKey
In the code below, the following happens:
- Check to make sure that first pass authentication happened (i.e. username/pasword authentication)
- Get the TextKey
values from the SESSION variables - Make a call to perform_ValidateTextKeyFromUserId to validate the TextKey
- This is one of the class methods in the TextKey
API library - See ValidateTextKeyFromUserId for more information on this API call
- Finalize the SESSION variables if the validation was good
- Return the result payload
// Include configuration settings include_once("config.php"); // Add TextKey PHP REST Library include_once("textkey_rest.php"); // Add TextKey Session Handler require_once("textkeysite.php"); // Create the session handling object $textkeysite = new textkeysite(); // Check to make sure pass 1 worked (i.e. username/pasword authentication) $loggedIn = $textkeysite->getPassedLoginCheck(); if ($loggedIn) { // Get the session values from the textkey validation and check to make sure they are good $textkeyvc = $textkeysite->get_textkeyvc(); $tkuserId = $textkeysite->get_tkuserId(); $textkey = $textkeysite->get_textkey(); // Create the textkey object $tk = new textKey(TK_API); // Validate the TextKey to ensure it was the original one with the TextKey validation code $textkey_result = $tk->perform_ValidateTextKeyFromUserId($tkuserId, $textkey, $textkeyvc, TK_ISHASHED); if ($textkey_result->errorDescr === "") { // Check for an error $validationErrors = $textkey_result->validationErrors; foreach($validationErrors as $key => $value) { switch ($value) { case "textKeyNoError": // No error so setup the return payload $error_msg = '({"error":"", "validated":' . json_encode($textkey_result->validated) . '})'; // Handle setting the sesssion info. $textkeysite->setPassedTKCheck(); break; case "textKeyNotFound": $error_msg = '({"error": "The TextKey sent was not valid."})'; break; case "textKeyNotReceived": $error_msg = '({"error": "The TextKey was never received."})'; break; case "textKeyFraudDetect": $error_msg = '({"error": "Fraud Detected - The TextKey was not sent by the authorized device."})'; break; case "noRegistrationFound": $error_msg = '({"error": "The TextKey was received but it was not assigned to a registered user."})'; break; case "validationCodeInvalid": $error_msg = '({"error": "The TextKey was received but the validation code was invalid."})'; break; case "textKeyTooOld": $error_msg = '({"error": "The TextKey was received but had already expired."})'; break; case "textKeyError": $error_msg = '({"error": "An innternal TextKey error occured."})'; break; case "textKeyNotValidated": $error_msg = '({"error": "The TextKey was not validated."})'; break; case "pinCodeError": $error_msg = '({"error": "A Pin Code error occured."})'; break; default: $error_msg = '({"error": "An error occured while trying to verify the TextKey."})'; break; } } } else { $error_msg = $textkey_result->errorDescr; $error_msg = '({"error":' . json_encode($error_msg) . '})'; } } else { $error_msg = "Error logging in user: User/Password validation was not finalized."; $error_msg = '({"error":' . json_encode($error_msg) . '})'; } error_log('error_msg: ' . $error_msg); echo $error_msg; exit;
The getPassedLoginCheck method checks the SESSION variables states to ensure that that the 1st pass of authentication (i.e. username/pasword authentication) has completed successfully.
This method is part of the textkeysite class in textkeysite.php
/* ** Check to see if the first check passed (i.e. Login/Password) */ function getPassedLoginCheck() { // Start the login session if (!isset($_SESSION)) { session_start(); } // If the username/login check has not passed them return false if ($_SESSION['passedcheck1'] != true) { return false; } return true; }
The setPassedTKCheck method sets the SESSION variables state so that the 2nd level authentication was completed.
This method is part of the textkeysite class in textkeysite.php
/* ** Set the session info. after the user passes the second check (i.e. textkey authentication) */ function setPassedTKCheck($username, $userid) { // Start the login session if (!isset($_SESSION)) { session_start(); }; // 2 pass flags $_SESSION['passedcheck2'] = true; }
The getPassedTKCheck method checks the SESSION variables states to ensure that that both passes of authentication have completed successfully.
See the Login Check Code tab for an example.
This method is part of the textkeysite class in textkeysite.php
/* ** Check to see if the second check passed (i.e. textkey authentication) */ function getPassedTKCheck() { // Start the login session if (!isset($_SESSION)) { session_start(); } // If the TextKey check has not passed then return false if (($_SESSION['passedcheck1'] != true) || ($_SESSION['passedcheck2'] != true)) { return false; } return true; }
This sample code is a server site check before rendering any page to ensure that the user is logged in and that both passes of authentication have completed successfully.
include_once("config.php"); require_once("textkeysite.php"); // Setup $loggedIn = false; $userName = ""; // Create the session handling object $textkeysite = new textkeysite(); // Check to see if the user has fully logged in by passing both checks and then handle the custom code $loggedIn = $textkeysite->getPassedTKCheck(); if ($loggedIn) { $userName = $textkeysite->get_userName(); };
Those variables can be used to decide what to render or not render for the page. For example, the index.php page shows this in effect. It will do a check for a logged in user and either display a button to the login page or show that the user is logged in.
<?php if (!($loggedIn)): ?> <form class="form-textkey" id="form-textkeygotologin" action="tutoriallogin.php"> <p> <input type="submit" name="submit" value="Go To Login Page" class='login-send'> </p> </form> <?php else: ?> <header> <h1><a href="index.php">TextPower - TextKey Demo Registraton</a></h1> <div class="tagdesc"> <br /> <h1 align="center"><strong>SUCCESS!</strong></h1> <br /> <h3 align="center">You have logged in with the user name <strong><?php echo $userName; ?></strong></h3> <br /> <h2 align="center">If this was your company`s website you would now be successfully logged in.</h2> </div> </header> <form class="form-textkey" id="form-textkey"> <p> <input type="submit" name="submit" value="Logout" class='logout-send' onClick="return login.handlelogout(true);"> </p> </form> <div class="tagdesc"> <h3 align="center">Want to try this again? Click the <strong>"Logout"</strong> button to return to the login screen.</h3> <br /> </div> <?php endif; ?>