Saturday, October 29, 2022
HomeWordPress DevelopmentTips on how to construct a One-Time-Password(OTP) Verification API with Go and...

Tips on how to construct a One-Time-Password(OTP) Verification API with Go and Twilio


Organizations are all the time on the lookout for revolutionary methods to deal with threats and vulnerabilities on their platform. They’re continually investing in human assets and applied sciences to assist construct and ship safe functions. Two-factor authentication, Authenticator app, and Biometrics, amongst others, are among the revolutionary strategies organizations are adopting to maintain their platform protected.

On this publish, we are going to discover ways to construct APIs that authenticate customers with their cellphone numbers utilizing Go and Twilio’s Verification Service. For this publish, we might be utilizing Gin-gonic to construct our API. Nevertheless, the identical strategy additionally applies to any Go-based framework.



Conditions

To completely grasp the ideas offered on this tutorial, the next necessities apply:

  • Fundamental understanding of Go
  • Go set up ( Model 1.18 above)
  • A Twilio account; signup for a trial account is totally free.



Getting began

To get began, we have to navigate to the specified listing and run the command under in our terminal:

mkdir go-sms-verification && cd go-sms-verification
Enter fullscreen mode

Exit fullscreen mode

This command creates a go-sms-verification folder and navigates into the challenge listing.

Subsequent, we have to initialize a Go module to handle challenge dependencies by operating the command under:

go mod init go-sms-verification
Enter fullscreen mode

Exit fullscreen mode

This command will create a go.mod file for monitoring challenge dependencies.

We proceed to put in the required dependencies with:

go get github.com/gin-gonic/gin github.com/twilio/twilio-go github.com/joho/godotenv github.com/go-playground/validator/v10
Enter fullscreen mode

Exit fullscreen mode

github.com/gin-gonic/gin is a framework for constructing internet utility.

github.com/twilio/twilio-go is a Go package deal for speaking with Twilio.

github.com/joho/godotenv is a library for managing atmosphere variable.

github.com/go-playground/validator/v10 is a library for validating structs and fields.



Structuring our utility

It’s important to have challenge construction because it makes the challenge maintainable and makes it simpler for us and others to learn our codebase.
To do that, we have to create an api, cmd, and knowledge folder in our challenge listing.

project directory

api is for structuring our API-related recordsdata

cmd is for structuring our utility entry level

knowledge is for structuring our utility knowledge



Establishing Twilio

To allow OTP verification in our API, we have to signal into our Twilio Console to get our Account SID and an Auth Token. We have to hold these parameters useful as we’d like them to configure and construct our APIs.

Twilio credentials

Create a Verification Service
Twilio ships with safe and strong providers for seamlessly validating customers with SMS, Voice, and E-mail. In our case, we are going to use the SMS choice to confirm customers by means of cellphone numbers. To do that, navigate to the Discover Merchandise tab, scroll to the Account safety part, and click on on the Confirm button.

Verify account

Navigate to the Companies tab, click on on the Create new button, enter sms-service because the pleasant title, toggle-on the SMS choice, and Create.

Create new service
Input details

Upon creation, we have to copy the Service SID. It should additionally turn out to be useful when constructing our API.

Service SID

Allow geographical permission
Geographical Permissions are mechanisms put in place by Twilio to regulate using their providers. It supplies a device for enabling and disabling nations receiving voice calls and SMS messages from a Twilio account.

To allow SMS, we have to seek for SMS Geographic Permissions within the search bar, click on on the SMS Geographic Permissions consequence, after which test the nation the place the SMS supplier operates.

Search
Check country of operation



Creating OTP Verification APIs in Go

With the configuration achieved, we will begin constructing our APIs by following the steps.

Add atmosphere variables
Subsequent, we have to create a .env file within the root listing and add the snippet under:

TWILIO_ACCOUNT_SID=<ACCOUNT SID>
TWILIO_AUTHTOKEN=<AUTH TOKEN>
TWILIO_SERVICES_ID=<SERVICE ID>
Enter fullscreen mode

Exit fullscreen mode

As talked about earlier, we will get the required credentials from Twilio console and Service Setting.

twilio console
service settings

Lastly, we have to create helper capabilities for loading the atmosphere variables into our utility. To do that, we have to create a config.go file contained in the api folder and add the snippet under:

package deal api

import (
    "log"
    "os"

    "github.com/joho/godotenv"
)

func envACCOUNTSID() string {
    println(godotenv.Unmarshal(".env"))
    err := godotenv.Load(".env")
    if err != nil {
        log.Fatalln(err)
        log.Deadly("Error loading .env file")
    }
    return os.Getenv("TWILIO_ACCOUNT_SID")
}

func envAUTHTOKEN() string {
    err := godotenv.Load()
    if err != nil {
        log.Deadly("Error loading .env file")
    }
    return os.Getenv("TWILIO_AUTHTOKEN")
}

func envSERVICESID() string {
    err := godotenv.Load()
    if err != nil {
        log.Deadly("Error loading .env file")
    }
    return os.Getenv("TWILIO_SERVICES_ID")
}
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependencies
  • Create an envACCOUNTSID, envAUTHTOKEN, and envSERVICESID capabilities that test if the atmosphere variable is accurately loaded and returns the atmosphere variable.

Create the API fashions
Subsequent, we have to create fashions to characterize our utility knowledge. To do that, we have to navigate to the knowledge folder and, on this folder, create a mannequin.go file and add the snippet under:

package deal knowledge

sort OTPData struct {
    PhoneNumber string `json:"phoneNumber,omitempty" validate:"required"`
}

sort VerifyData struct {
    Consumer *OTPData  `json:"consumer,omitempty" validate:"required"`
    Code string `json:"code,omitempty" validate:"required"`
}
Enter fullscreen mode

Exit fullscreen mode

Create the API routes, helpers, service, and handlers
With the fashions to ship and confirm OTP totally arrange, we have to navigate to the api folder and do the next:

First, we have to create a route.go file for configuring the API routes and add the snippet under:

package deal api

import "github.com/gin-gonic/gin"

sort Config struct {
    Router *gin.Engine
}

func (app *Config) Routes() {
    //routes will come right here
}
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependency
  • Creates a Config struct with a Router property to configure the applying strategies
  • Creates a Routes perform that takes within the Config struct as a pointer

Secondly, we have to create a helper.go file and add the snippet under:

package deal api

import (
    "web/http"

    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
)

sort jsonResponse struct {
    Standing  int    `json:"standing"`
    Message string `json:"message"`
    Information    any    `json:"knowledge"`
}

var validate = validator.New()

func (app *Config) validateBody(c *gin.Context, knowledge any) error {
    //validate the request physique
    if err := c.BindJSON(&knowledge); err != nil {
        return err
    }
    //use the validator library to validate required fields
    if err := validate.Struct(&knowledge); err != nil {
        return err
    }
    return nil
}

func (app *Config) writeJSON(c *gin.Context, standing int, knowledge any) {
    c.JSON(standing, jsonResponse{Standing: standing, Message: "success", Information: knowledge})
}

func (app *Config) errorJSON(c *gin.Context, err error, standing ...int) {
    statusCode := http.StatusBadRequest
    if len(standing) > 0 {
        statusCode = standing[0]
    }
    c.JSON(statusCode, jsonResponse{Standing: statusCode, Message: err.Error()})
}
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependencies
  • Creates a jsonResponse struct and validate variable to explain the API response and to validate the API fields
  • Creates a validateBody perform that takes within the Config struct as a pointer and returns an error. Contained in the perform, we validate that request knowledge within the appropriate format and in addition use the validator library to additionally validate and test for the required fields
  • Creates a writeJSON perform that takes within the Config struct as a pointer and makes use of the jsonResponse struct to assemble API response when there’s no error
  • Creates a errorJSON perform that takes within the Config struct as a pointer and makes use of the jsonResponse struct to assemble API response when there’s an error

Thirdly, we have to create a service.go file for abstracting the applying logic and add the snippet under:


package deal api

import (
    "github.com/twilio/twilio-go"
    twilioApi "github.com/twilio/twilio-go/relaxation/confirm/v2"
)

var consumer *twilio.RestClient = twilio.NewRestClientWithParams(twilio.ClientParams{
    Username: envACCOUNTSID(),
    Password: envAUTHTOKEN(),
})

func (app *Config) twilioSendOTP(phoneNumber string) (string, error) {
    params := &twilioApi.CreateVerificationParams{}
    params.SetTo(phoneNumber)
    params.SetChannel("sms")

    resp, err := consumer.VerifyV2.CreateVerification(envSERVICESID(), params)
    if err != nil {
        return "", err
    }

    return *resp.Sid, nil
}

func (app *Config) twilioVerifyOTP(phoneNumber string, code string) error {
    params := &twilioApi.CreateVerificationCheckParams{}
    params.SetTo(phoneNumber)
    params.SetCode(code)

    resp, err := consumer.VerifyV2.CreateVerificationCheck(envSERVICESID(), params)
    if err != nil {
        return err
    } else if *resp.Standing == "authorised" {
        return nil
    }

    return nil
}
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependencies
  • Creates a consumer variable to configure Twilio consumer utilizing the Account SID and Auth Token
  • Creates a twilioSendOTP perform that accepts a phoneNumber, takes within the Config struct as a pointer, and returns both a string or an error. Contained in the perform, we created a params variable by including the phoneNumber and setting the channel for sending the OTP as sms. Lastly, we use the consumer variable to create verification by utilizing the Service SID and params after which return the suitable response
  • Creates a twilioVerifyOTP perform that accepts a phoneNumber and code, takes within the Config struct as a pointer, and returns an error. Contained in the perform, we created a params variable by including the phoneNumber and code. Lastly, we use the consumer variable to test authenticity of the OTP by utilizing the Service SID and params after which return the suitable response

Fourthly, we have to create a handler.go file for modifying the incoming request and add the snippet under:

package deal api

import (
    "context"
    "go-sms-verification/knowledge"
    "web/http"
    "time"

    "github.com/gin-gonic/gin"
)

const appTimeout = time.Second * 10

func (app *Config) sendSMS() gin.HandlerFunc {
    return func(c *gin.Context) {
        _, cancel := context.WithTimeout(context.Background(), appTimeout)
        var payload knowledge.OTPData
        defer cancel()

        app.validateBody(c, &payload)

        newData := knowledge.OTPData{
            PhoneNumber: payload.PhoneNumber,
        }

        _, err := app.twilioSendOTP(newData.PhoneNumber)
        if err != nil {
            app.errorJSON(c, err)
            return
        }

        app.writeJSON(c, http.StatusAccepted, "OTP despatched efficiently")
    }
}

func (app *Config) verifySMS() gin.HandlerFunc {
    return func(c *gin.Context) {
        _, cancel := context.WithTimeout(context.Background(), appTimeout)
        var payload knowledge.VerifyData
        defer cancel()

        app.validateBody(c, &payload)

        newData := knowledge.VerifyData{
            Consumer: payload.Consumer,
            Code: payload.Code,
        }

        err := app.twilioVerifyOTP(newData.Consumer.PhoneNumber, newData.Code)
        if err != nil {
            app.errorJSON(c, err)
            return
        }

        app.writeJSON(c, http.StatusAccepted, "OTP verified efficiently")
    }
}
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependencies
  • Creates an appTimeout variable to set request timeout
  • Creates a sendSMS perform that returns a Gin-gonic handler and takes within the Config struct as a pointer. Contained in the returned handler, we outlined the API timeout, used the helper capabilities and the service created earlier to confirm the request physique and ship the OTP
  • Creates a verifySMS perform that returns a Gin-gonic handler and takes within the Config struct as a pointer. Contained in the returned handler, we outlined the API timeout, used the helper capabilities and the service created earlier to confirm the request physique and the OTP

Lastly, we have to replace the routes.go recordsdata the API route and corresponding handler

package deal api

import "github.com/gin-gonic/gin"

sort Config struct {
    Router *gin.Engine
}

//modify under
func (app *Config) Routes() {
    app.Router.POST("/otp", app.sendSMS())
    app.Router.POST("/verifyOTP", app.verifySMS())
}
Enter fullscreen mode

Exit fullscreen mode

Placing all of it collectively
With our API totally arrange, we have to create the applying entry level. To do that, we have to navigate to the cmd folder and, on this folder, create a principal.go file and add the snippet under:

package deal principal

import (
    "go-sms-verification/api"
    "github.com/gin-gonic/gin"
)

func principal() {
    router := gin.Default()

    //initialize config
    app := api.Config{Router: router}

    //routes
    app.Routes()

    router.Run(":80")
}
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependencies
  • Creates a Gin router utilizing the Default configuration
  • Initialize the Config struct by passing within the Router
  • Provides the route and run the applying on port :80

With that achieved, we will begin a improvement server utilizing the command under:

go run cmd/principal.go 
Enter fullscreen mode

Exit fullscreen mode

send OTP
OTP
Verifying OTP

We will additionally confirm the message logs by navigating to the Confirm tab of the Logs on Twilio

Message log



Conclusion

This publish mentioned methods to create APIs that test and confirm customers with their cellphone numbers utilizing Go and Twilio’s Verification Service. Past SMS-based verification, Twilio ships a number of providers to seamlessly combine authentication into a brand new or current codebase.

These assets could be useful:

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments