In these times of heightened focus on privacy, security, and password leaks don’t hesitate when an easy-to-implement security solution is right in front of you!

In this How-To Guide for Developers, we will show you, step-by-step, how easy it is to secure your authentication process with GatewayAPI. If you’re not already a user, don’t worry, you can sign up for free here!

One way to make your authentication process more secure is by adding in more authentication factors. One such factor, which is widely used due to its convenience, is a one-time SMS code. This is sent to the account owners’ phone number when, and only when, a valid password has been entered. And only by typing in that extra one-time code, can the user be authenticated. This is known as an SMS password, SMS passcode, or OneCode, and is provided by Authy and similar services.

By using GatewayAPI and a couple of neat libraries, it is straightforward to implement this additional factor, and right here we’re going to show you how!

Connecting to the API

Before we can start authenticating account owners using a two-factor process, we need to know how to send an SMS using GatewayAPI in the first place. (If you are not registered as a GatewayAPI customer yet, you can easily set up your account here!)

In this post, we will be using Python for the code examples. For Ruby and PHP developers, we have examples in our documentation for using GatewayAPI with those and many other languages.

GatewayAPI has a couple of ways to authenticate. We will focus on using OAuth1 since it is both supported and straightforward.

Go to your dashboard and in the sidebar choose settings -> OAuth keys. There should already exist a key-set, with a label saying it was created by the system. Click on the key-button on the right and you will be presented with your key and secret. These are what you will use to authenticate with the API.

Sending an SMS is now straightforward. Using Python it can be done like so:

Install dependencies with: pip install requests_oauthlib

from requests_oauthlib import OAuth1Session
key = '**********YOUR KEY**********'
secret = '**********YOUR SECRET**********'
gwapi = OAuth1Session(key, client_secret=secret)
req = {
    'recipients': [{'msisdn': 4512345678}],  # remember to change
    'message': 'First SMS sent using GatewayAPI',
}
res = gwapi.post('https://gatewayapi.com/rest/mtsms', json=req)
res.raise_for_status()

You can find examples on how to sent a SMS using more languages in our documentation.

Creating a two-factor authentication (2FA) process

Now we know how to send a text, we can move on to the next stage, setting up your two-factor SMS and password authentication!

To reiterate, this two-factor process includes two steps, the first is authenticating with a username and password, and if that succeeds, the second step is authenticating with a one-time code that will be sent to you as an SMS using GatewayAPI’s Rest API.

Since it is a two-factor process, we need a way to store some data between the two steps. For this we will use a Redis server, but it could be a session object, a MySQL database or any other way to store state between the two steps.

We also use PyOTP, which is a library that among other things makes it easy to make one-time passwords. For Ruby developers there is a library similar to PyOTP called ROTP. For PHP developers there is OTPHP.

What happens next through GatewayAPI?

Step one

We will create an AuthClient, which will do all the heavy lifting for us.

def step_one(self, username, password, phone):
    # code that authorizes username and password goes here
    # at this point, it should have been established that username and
    # password is legit and a user id should have been acquired
    user_id = 1

    # Generate a random secret for the user, recommended in a 2FA setup
    self.redis.setnx('secret:{}'.format(user_id), pyotp.random_base32(length=32))
    secret = self.redis.get('secret:{}'.format(user_id))

    # Counter that increases for each auth attempt for the particular user
    count = self.redis.incr('counter:{}'.format(user_id))

    hotp = pyotp.HOTP(secret, digits=6)
    sms_code = hotp.at(count)
    self.send_sms(sms_code, phone)
    return user_id

We use a counter in our Redis database that increments for each authentication attempt for that particular user. This count will be seeded to our PyOTP instance to ensure uniqueness. We then acquire a new code from PyOTP. We use this to authenticate the SMS code in the second step. We then send off the SMS code to the users’ phone number using a function that is almost identical to our previous SMS-sending example. Lastly, we return the ID of the user that matched the username and password.

Safety first!

We store a random secret for each user on demand in Redis, but you can generate and store this secret in any way you like, just make sure it’s secure. More on management of secrets.

Step two

def step_two(self, auth_user_id, sms_code):
    secret = self.redis.get('secret:{}'.format(user_id))
    hotp = pyotp.HOTP(secret, digits=6)
    count = self.redis.get('counter:{}'.format(auth_user_id))
    if not hotp.verify(sms_code, count):
        raise Exception('Not a valid SMS code for user')

Step two is even simpler. We provide the obtained user_id that was returned from step_one and the SMS code that was sent to the users’ phone number. We look up the current counter in our Redis and make sure the SMS code matches.

Using all of this our flow in code could look something like this for step one:

try:
    ac = AuthClient()
    user_id = ac.step_one('username', 'password', 4521324354)
    print("SMS code sent")
except:
    print("Failed to sent SMS code")

And for step two:

try:
    ac = AuthClient()
    token = ac.step_two(user_id, sms_code)
    print("Authentication succeeded")
except Exception as e:
    print("Authentication failed")

If the authentication is a success, the user was authenticated using a two-factor process, and an authentication token was obtained.

The example code is focused on showcasing the technique of using GatewayAPI and how to use one-time passwords. In a production setting, modern security measures such as brute-force prevention and encryption of all sensitive data should be applied to your solution to fend off attackers. Take a look at RFC4226s consideration section for what you should be wary of when using PyOTP or similar libraries and techniques.

There are many possibilities when including SMS in your applications. Two-factor authentication consisting of a password and an SMS code is only one of them. With our easy and convenient APIs, it is easy to make your authentication processes more secure without making it complicated for all of your SMS-receiving users.

Code example

We have included the full client for the examples in Python as a reference, but in any case, customization will be needed for your solution.

Python AuthClient

import pyotp
import redis
from requests_oauthlib import OAuth1Session


class AuthClient(object):

    def __init__(self):
        self.redis = redis.StrictRedis(host='localhost', port=6379, db=0)

    def send_sms(self, sms_code, phone):
        key = '**********YOUR KEY**********'
        secret = '**********YOUR SECRET**********'
        gwapi = OAuth1Session(key, client_secret=secret)
        req = {
            'recipients': [{'msisdn': phone}],
            'message': 'Your code is {}'.format(sms_code),
            'sender': 'SMS Verify',
        }
        res = gwapi.post('https://gatewayapi.com/rest/mtsms', json=req)
        res.raise_for_status()

    def step_one(self, username, password, phone):
        # code that authorizes username and password goes here
        # at this point, it should have been established that username and
        # password is legit and a user id should have been acquired
        user_id = 1

        # Generate a random secret for the user, recommended in a 2FA setup
        self.redis.setnx('secret:{}'.format(user_id), pyotp.random_base32(length=32))
        secret = self.redis.get('secret:{}'.format(user_id))

        # Counter that increases for each auth attempt for the particular user
        count = self.redis.incr('counter:{}'.format(user_id))

        hotp = pyotp.HOTP(secret, digits=6)
        sms_code = hotp.at(count)
        self.send_sms(sms_code, phone)
        return user_id

    def step_two(self, auth_user_id, sms_code):
        secret = self.redis.get('secret:{}'.format(user_id))
        hotp = pyotp.HOTP(secret, digits=6)
        count = self.redis.get('counter:{}'.format(auth_user_id))
        if not hotp.verify(sms_code, count):
            raise Exception('Not a valid SMS code for user')

Global SMS Gateway

We have made it simple to implement SMS services into your business! We offer some of the lowest prices in the world, easy integration, world-class customer support, an intuitive interface, and a rock-solid uptime of 99.99%. If you don’t have an account yet, you can create a FREE account in less than two minutes! Simply go to GatewayAPI or contact sales@gatewayapi.com.

If you have a concept or business that could benefit from employing SMS communication, contact us. We will help you get started, contact us today!