Thursday, January 28, 2016

Creating REST API to generate the OTP -part 1

Now a days Most of the sign in, registration or any bank transactions happen over a OTP security (Over the air pin). So in this article we will see how to create a REST API for generating the OTP.

Going forward, we can use this APIs for the registration purpose from an Android APP. Before you go through this blog, you make sure that you have the basic knowledge of PHP, MYSQL  and Apache on windows or Linux. If you are going to work on windows you need to install WAMP or on linux you install LAMP.

I am going to use WAMP i.e Window-Apache-MySql-PHP. Before we start, we need to have the third party SMS gateway information to send the SMS as the OTP. You can use MSG91 or sms server basket or any third party sms gateways. I tried with both the above mentioned gateways.

Work Flow 

  1. User Mobile Number will be sent to our Server. If you are using this for the website, the user mobile will be sent from the website. Or if you are using this API in Mobile Application, App sends the user mobile number to our hosted Server.
  2. Our Server requests the SMS Gateway for an SMS to the Mobile number with a verification Code (OTP)
  3. SMS Gateway send the SMS with the verification code to the mobile number
  4. Verification code will be sent back to the server again for verification( If it's website, user has to type it in the mentioned edit box. or if it's a app, app reads the SMS and sends back the code to the Server)
  5. Our sever verifies the code And activates the user or take appropriate action if the code matches with the generated one.
Building the REST API

Before we start building the OTP API, We need to have the API's to send the SMS using one of the third party SMS gateway. I consider you have all the APIs ready to send the SMSs. Let's start building the PHP project. Let us build the simple REST API for managing the Users using the OTP. You can use the same API's for building the registration part of the Android or any mobile application.  At the end f this project, we should be able to build two end points:
  1. Register user which sends a SMS with the verification code
  2. Verify the code and complete the registration
Download And Install the Wamp Server

First Install the wamp. You can refer to our previous blog- Installing wamp to install the wamp. 
You can test your server by opening the address http://localhost/ in your browser or the http://IPaddress of the server
Also you can check phpmyadmin by opening http://localhost/phpmyadmin. Phpmyadmin is required to create and manage the mysql database. You can create and manage the databases using the command prompt also. But Phpmyadmin will give a easy and nice User Interface. 


Create MySql Database 

Open http://localhost/phpmyadmin and execute below queries to create required database and tables.
Create a database named user_registration.




Once database is created, you can go to that database and click on SQL tab:





Now you can execute the below command in the sql dialog box.

CREATE TABLE `sms_codes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `code` varchar(6) NOT NULL,
  `status` int(1) NOT NULL DEFAULT '0',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `email` varchar(250) NOT NULL,
  `mobile` varchar(10) NOT NULL,
  `apikey` varchar(32) NOT NULL,
  `status` int(1) NOT NULL DEFAULT '0',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=64 ;
ALTER TABLE `sms_codes`
  ADD CONSTRAINT `sms_codes_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;



Press Go button. You get the below screen. You can execute these commands individually or even through the UI with the above mentioned structure. You get the below screen after successful creation



You can check the tables created by going to structures:



After creating the database successfully. We will create the PHP project. Go to the wamp directory c:\wamp\www. Create a directory OTP_SMS. This is the main directory of the project.

Inside this, create a directory called as "include" to create all the configuration files.  Inside this include directory create config.php.

You can use any editor or notepad++ to edit the file. I use PhpDesigner
Config.php
<?php
/**
 * Database configuration
 */
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', 'localhost');
define('DB_NAME', 'user_registration');
/**
 * MSG91 configuration
 */
//auth key
define('MSG91_AUTH_KEY', "");
// sender id should 6 character long
define('MSG91_SENDER_ID', '');
define('USER_CREATED_SUCCESSFULLY', 0);
define('USER_CREATE_FAILED', 1);
define('USER_ALREADY_EXISTED', 2);
?>
Now create another php file named DbConnect.php This file handles the database connection:
<?php
/**
 * Handling database connection
  */
class DbConnect {
    private $conn;
    function __construct() {       
    }
    /**
     * Establishing database connection
     * @return database connection handler
     */
    function connect() {
        include_once dirname(__FILE__) . '/Config.php';
        // Connecting to mysql database
        $this->conn = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);
        // Check for database connection error
        if (mysqli_connect_errno()) {
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
            exit;
        }
        // returing connection resource
        return $this->conn;
    }
}
?>
Create another php file named DbHandler.php under include folder. This file contains the major functions to perform the read, write operations onto database
<?php
/**
 * Class to handle all db operations
 * This class will have CRUD methods for database tables
 */
class DbHandler {
    private $conn;
    function __construct() {
        require_once dirname(__FILE__) . '/DbConnect.php';
        // opening db connection
        $db = new DbConnect();
        $this->conn = $db->connect();
    }
    /* ------------- `users` table method ------------------ */
    /**
     * Creating new user
     * @param String $name User full name
     * @param String $email User login email id
     * @param String $mobile User mobile number
     * @param String $otp user verificaiton code
     */
    public function createUser($name, $email, $mobile, $otp) {
        $response = array();
        // First check if user already existed in db
        if (!$this->isUserExists($mobile)) {
            // Generating API key
            $api_key = $this->generateApiKey();
            // insert query
            $stmt = $this->conn->prepare("INSERT INTO users(name, email, mobile, apikey, status) values(?, ?, ?, ?, 0)");
            $stmt->bind_param("ssss", $name, $email, $mobile, $api_key);
            $result = $stmt->execute();
            $new_user_id = $stmt->insert_id;
            $stmt->close();
            // Check for successful insertion
            if ($result) {
                $otp_result = $this->createOtp($new_user_id, $otp);
                // User successfully inserted
                return USER_CREATED_SUCCESSFULLY;
            } else {
                // Failed to create user
                return USER_CREATE_FAILED;
            }
        } else {
            // User with same email already existed in the db
            return USER_ALREADY_EXISTED;
        }
        return $response;
    }
    public function createOtp($user_id, $otp) {
        // delete the old otp if exists
        $stmt = $this->conn->prepare("DELETE FROM sms_codes where user_id = ?");
        $stmt->bind_param("i", $user_id);
        $stmt->execute();
        $stmt = $this->conn->prepare("INSERT INTO sms_codes(user_id, code, status) values(?, ?, 0)");
        $stmt->bind_param("is", $user_id, $otp);
        $result = $stmt->execute();
        $stmt->close();
        return $result;
    }
     
    /**
     * Checking for duplicate user by mobile number
     * @param String $email email to check in db
     * @return boolean
     */
    private function isUserExists($mobile) {
        $stmt = $this->conn->prepare("SELECT id from users WHERE mobile = ? and status = 1");
        $stmt->bind_param("s", $mobile);
        $stmt->execute();
        $stmt->store_result();
        $num_rows = $stmt->num_rows;
        $stmt->close();
        return $num_rows > 0;
    }
    public function activateUser($otp) {
        $stmt = $this->conn->prepare("SELECT u.id, u.name, u.email, u.mobile, u.apikey, u.status, u.created_at FROM users u, sms_codes WHERE sms_codes.code = ? AND sms_codes.user_id = u.id");
        $stmt->bind_param("s", $otp);
        if ($stmt->execute()) {
            // $user = $stmt->get_result()->fetch_assoc();
            $stmt->bind_result($id, $name, $email, $mobile, $apikey, $status, $created_at);
             
            $stmt->store_result();
            if ($stmt->num_rows > 0) {
                 
                $stmt->fetch();
                 
                // activate the user
                $this->activateUserStatus($id);
                 
                $user = array();
                $user["name"] = $name;
                $user["email"] = $email;
                $user["mobile"] = $mobile;
                $user["apikey"] = $apikey;
                $user["status"] = $status;
                $user["created_at"] = $created_at;
                 
                $stmt->close();
                 
                return $user;
            } else {
                return NULL;
            }
        } else {
            return NULL;
        }
        return $result;
    }
     
    public function activateUserStatus($user_id){
        $stmt = $this->conn->prepare("UPDATE users set status = 1 where id = ?");
        $stmt->bind_param("i", $user_id);
         
        $stmt->execute();
         
        $stmt = $this->conn->prepare("UPDATE sms_codes set status = 1 where user_id = ?");
        $stmt->bind_param("i", $user_id);
         
        $stmt->execute();
    }
    /**
     * Generating random Unique MD5 String for user Api key
     */
    private function generateApiKey() {
        return md5(uniqid(rand(), true));
    }
}
?>
Continued to part -2

No comments: