When you get a new registration on your site you want to make sure it’s a real person. That’s the reason Captchas were invented, as well as email confirmations. A recent addition to the authentication mix is the ability to send a text message with a code. This code then has to be typed into a verification form. This is more reliable than email because a script can read email too. Yes, a phone app can read text messages if someone installs the app and allows that permission. But it’s easier for someone to fabricate a list of email addresses than SMS enabled phone numbers. In this article, we will walk through how to verify SMS with an API.
What this means is that using an API call you can have a code sent to a phone through a text message. Then you can look for that code to be submitted in a form within a certain time (like 15 minutes). After that period you can ignore that registration. You could even do that for blog comments but that seems like overkill to me. 🙂
For this article we will follow a five-step process:
- Get an API Key
- Register for the Telesign SMS API
- Create a registration form
- Integrate with the API
- Put it all together
How to Verify SMS Messages with an API
Step 1. Get an API Key
First, you’ll need a rapidapi key. This is free, just sign up using an email address. You can use the same key for any of the thousands of APIs available.
Step 2. Register for the Telesign SMS API
According to the pricing page, the most you will pay for one test is one dollar. This depends on the country of your phone number though. In the phone number field, make sure you include the country code (in the United States it is 1). Otherwise, you will get an error from the API. The country code determines how many credits you use per API call. When I first saw this on the about page I thought I would pay a dollar for one test:
But if you click the “More…” link you can see the Credit Cost for most countries is less than 1000. I was happy to see that for US phone numbers the Credit Cost Per Verification Sent was only 51. This means I can use it 19 times (19*51=969) for the lowest monthly subscription. To use it more, the cost per verification goes down an order of magnitude. So for 10 dollars a month I could use it for US phone numbers, not 190 times but 1960 times (51*1960=99960).
The Telesign SMS Verify API has three input parameters. This includes the recipient phone number, a verification code, and your app name (so they know it’s from you). See below for how it looks on my phone when I tried it myself. I put “Really Popular App” as the name of my app and 12345 as the verification code:
The text came from a short code, which may be different for different countries. This likely explains the different pricing per country.
Step 3. Create a registration form
Now we get to the actual implementation of the API. We’ll start with a simple registration for something called “Really Popular App”. Since we will only verify the phone number every other piece of information may not be correct. Let’s start by requesting a name and phone number. That could look something like this:
Step 4. Verify SMS with an API
Once we submit the initial registration form, we save the phone number into the session. Then we generate a numeric verification code like this: $thisVCode = rand(10000,99999);
Save the name, phone, code, and timestamp into a local database. The next step is using the SMS Verify API to send a code to the person’s phone. There is sample code for that step on the API Endpoints page in many different languages. After sending the code through SMS, show a form where the user can enter the code they received:
Once the user submits the verification form, the code can be compared with what was saved in the database. This is the point where we pull the verification code from the database. We pull the code using the phone number saved in the session. If it doesn’t match, we show a message explaining that and ask them to try again. If it does, then we can address the user by name and consider them registered!
Behind the scenes, we are using a database to track registrations. We use this to store the generated verification codes with the entered phone numbers. We also use the timestamp when each phone number is added to make sure the codes have not expired. Also, there is a number_of_attempts field in this table. This field tracks how many times incorrect verification codes have been entered. This way people can’t use scripts to brute force their way into an automated registration. The database table looks like this:
The Create Table statement for that table is as follows:
CREATE TABLE `registration_verifications` ( `verification_id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(255) NOT NULL, `phone` varchar(30) NOT NULL, `code` int(11) NOT NULL, `time_sent` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `number_of_attempts` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`verification_id`), KEY `phone` (`phone`) );
Step 5. Put it all together
The code used for the complete process above is below so you can use it as a reference. You’ll need to add your own rapidapi key and create your own table in a local database. Here is the full code:
<?php /* File: reallyPopularAppRegistration.php Description: This shows a registration form for an app called Really Popular App. The phone number verification happens using an SMS Verification API. Then we save the phone number and generated verification code in a local database. The user has 15 minutes to enter the verification code. They get 5 attempts before they have to try again with a new code. */ session_start(); // phone number is stored in the session to match with verification code global $debug; $debug=false; $showVCodeInput=false; // Use this to determine whether we show the verification input form $appName = urlencode("Really Popular App"); // Create a mysqli database connection in a parameter named $dbLink include("include/db.php"); // Check what page we should be on $currentPage="start"; if (!empty($_GET['page'])) { if ($_GET['page']=="verify" and !empty($_SESSION['phoneNumber'])) { $currentPage="verify"; } else if ($_GET['page']=="verified" and !empty($_SESSION['name'])) { $currentPage="verified"; } } if ($currentPage==="start") { // Clear out the session $_SESSION['name']=$_SESSION['phoneNumber']=''; } //Check for posted data if (!empty($_POST['phoneNumber'])) { if (empty($_POST['firstName'])) { $msg = "Please enter both your first name and phone including country code"; } else { // Remove all but numbers from the phoneNumber $thisPhone = preg_replace('/D/','',$_POST['phoneNumber']); // Remove all but numbers, letters, spaces, periods and dashes from the name $thisName = preg_replace('/[^a-zA-Z0-9.- ]/','',$_POST['firstName']); //Make sure the number is at least 7 digits and name is not empty after filtering if (strlen($thisPhone)<7) { $msg = "Please enter a valid phone number including country code."; } else if (empty($thisName)) { $msg = "Please enter your first name."; } } if (empty($msg)) { // Now we can generate a verification code $thisVCode = rand(10000,99999); //Now we attempt to send a verification code $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => "https://telesign-telesign-send-sms-verification-code-v1.p.rapidapi.com/sms-verification-code?appName=$appName&phoneNumber=$thisPhone&verifyCode=$thisVCode", CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_ENCODING => "", CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => "", CURLOPT_HTTPHEADER => array( "content-type: application/x-www-form-urlencoded", "x-rapidapi-host: telesign-telesign-send-sms-verification-code-v1.p.rapidapi.com", "x-rapidapi-key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { $msg = "cURL Error #:" . $err; } // Check the API response for messages $responseObj = json_decode($response); if (!empty($responseObj->message)) { $msg = $responseObj->message; } if ($msg==="Message in progress"){ // Verification sent! // Save the verification code into the database with the phone number $sql = "insert into registration_verifications (first_name,phone,code) values ('".$thisName."','".$thisPhone."','".$thisVCode."')"; if (!mysqli_query($dbLink, $sql)) { if ($debug) { die("sql: $sql, error: ".mysqli_error($dbLink)."n"); } else { $msg = "There was a database error. Please request a new verification code."; } } else { // Phone number is captured, now redirect to the verify page $_SESSION['phoneNumber'] = $thisPhone; header("location: reallyPopularAppRegistration.php?page=verify"); exit; } } } } // end check for phone number submitted else if (!empty($_POST['verificationCode']) and !empty($_SESSION['phoneNumber'])) { $thisVerificationCode = preg_replace('/D/','',$_POST['verificationCode']); // Get the verification code from the database for this phone number $sql = "select * from registration_verifications where phone='".$_SESSION['phoneNumber']."' and time_sent > NOW() - INTERVAL 15 MINUTE order by time_sent desc limit 1"; $rs = mysqli_query($dbLink, $sql); if (!$rs) { if ($debug) { die("sql: $sql, error: ".mysqli_error($dbLink)."n"); } else { $msg = "There was a database error. Please request a new verification code."; } } else { if ($row = mysqli_fetch_assoc($rs)) { // Check number of attempts if ($row['number_of_attempts']>5) { $msg = "Please request a new verification code, you have made too many attempts at this one"; } else if ($row['code']==$thisVerificationCode) { // Verification succeeded, redirect to the verified page $_SESSION['name'] = $row['first_name']; header("location: reallyPopularAppRegistration.php?page=verified"); exit; } else { // Add an attempt for this verification code $sql = "update registration_verifications set number_of_attempts=number_of_attempts+1 where verification_id='".$row['verification_id']."'"; mysqli_query($dbLink, $sql); $msg = "Verification code is not correct."; } } } } ?> <!DOCTYPE html> <html> <head> <style> .form-wrapper{ width:200px; margin:auto; } .field-wrapper{padding:10px;} h1 {width:450px;margin:auto} .information { width:400px; margin:auto; } </style> <script type="text/javascript"> // Make sure we have valid inputs before submitting the form function validateForm() { // Name and phone need to have a value - use more validation server side if (document.getElementById("firstName").value.length<1) { alert("Please enter a first name."); return false; } else if (document.getElementById("phoneNumber").value.length<7) { alert("Please enter a phone number including country code."); return false; } } </script> </head> <body> <h1>Really Popular App Registration</h1> <div class="information"> <?php // Show a message if needed if (!empty($msg)) { echo "<h3>".$msg."</h3><br>"; echo "<a href='reallyPopularAppRegistration.php'>Start Over</a>"; } // Now show one of 3 possible pages - start, verify, or verified if ($currentPage==="start"){ ?> <h3>Note: Both fields are required. Please include the <a href="https://countrycode.org/" target="new">Country Code</a> with your phone number.</h3> </div> <div class="form-wrapper"> <form method="post" onsubmit="return validateForm();"> <div class="field-wrapper"> <input type="text" name="firstName" placeholder="First Name" id="firstName" /> </div> <div class="field-wrapper"> <input type="text" name="phoneNumber" placeholder="Phone Number" id="phoneNumber" /> </div> <div class="field-wrapper"> <input type="submit" name="submitPhone" value="Submit" /> </div> </form> </div> <?php } else if ($currentPage==="verify") { ?> <h3>Please enter the verification code you received on your phone below</h3> </div> <div class="form-wrapper"> <form method="post"> <div class="field-wrapper"> <input type="text" name="verificationCode" placeholder="Verification Code" /> </div> <div class="field-wrapper"> <input type="submit" name="submitCode" value="Submit" /> </div> </form> </div> <?php } else if ($currentPage==="verified"){ ?> <h3>Welcome <?php echo $_SESSION['name']; ?>! You are now SMS Verified.</h3> </div> <?php } // end of check for which view to display ?> </body></html>
The include/db.php file sets database credentials and creates a database link: $dbLink = mysqli_connect($host, $user, $pwd, $database);
Leave a Reply