如何使用GO和Twilio构建一次性通信(OTP)验证API
#go #twilio #gin #otp

组织一直在寻找创新的方法来解决平台上的威胁和脆弱性。他们不断投资人力资源和技术,以帮助建立和运输安全的应用程序。两因素身份验证,身份验证器应用程序和生物识别技术等是组织采用的一些创新方法来确保其平台安全。

在这篇文章中,我们将学习如何使用GO和Twilio的验证服务来构建用户使用电话号码来验证用户的API。对于这篇文章,我们将使用杜松子酒来构建我们的API。但是,同样的方法也适用于任何基于GO的框架。

先决条件

为了完全掌握本教程中提出的概念,适用以下要求:

  • 对GO的基本理解
  • GO安装(上面版本1.18)
  • 一个Twilio帐户;试用帐户的signup是完全免费的。

入门

要开始,我们需要导航到所需的目录并在我们的终端中运行下面的命令:

mkdir go-sms-verification && cd go-sms-verification

此命令创建一个go-sms-verification文件夹并导航到项目目录。

接下来,我们需要通过运行以下命令来初始化一个GO模块来管理项目依赖项:

go mod init go-sms-verification

此命令将创建一个用于跟踪项目依赖项的go.mod文件。

我们继续使用:
安装所需的依赖项

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

github.com/gin-gonic/gin是用于构建Web应用程序的框架。

github.com/twilio/twilio-go是用于与Twilio通信的GO包。

github.com/joho/godotenv是用于管理环境变量的库。

github.com/go-playground/validator/v10是用于验证结构和字段的库。

构建我们的应用

必须拥有一个良好的项目结构,因为它使项目可以维护,并使我们和其他人更容易阅读我们的代码库。
为此,我们需要在我们的项目目录中创建一个apicmddata文件夹。

project directory

api用于构建我们的API相关文件

cmd用于构建我们的应用程序入口点

data用于构建我们的应用程序数据

设置Twilio

要在API中启用OTP验证,我们需要登录我们的Twilio Console,以获取我们的帐户SID auth Token 。我们需要在需要配置和构建API时将这些参数保持方便。

Twilio credentials

创建验证服务
Twilio船提供安全且可靠的服务,可通过短信,语音和电子邮件无缝验证用户。在我们的情况下,我们将使用SMS选项通过电话号码验证用户。为此,请导航到探索产品选项卡,滚动到帐户安全部分,然后单击验证按钮。

Verify account

导航到 services 选项卡,单击创建新按钮,输入 sms-service 作为友好名称,请切换 SMS 选项,创建

Create new service
Input details

创建后,我们需要复制服务sid。在构建我们的API时也将派上用场。

Service SID

启用 g eagraphical p emmission

地理许可是Twilio控制其服务使用的机制。它提供了一种工具,用于启用和禁用从Twilio帐户接收语音通话和SMS消息的国家。

要启用SMS,我们需要在搜索栏中搜索SMS地理权限,请单击 SMS地理权限结果,然后检查 SMS的国家提供者运营。

Search
Check country of operation

在GO中创建OTP验证API

完成配置后,我们可以按照步骤开始构建API。

添加环境变量
接下来,我们需要在根目录中创建一个.env文件,然后添加下面的摘要:

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

如前所述,我们可以从Twilio控制台和服务设置中获得所需的凭据。

twilio console
service settings

最后,我们需要创建用于将环境变量加载到应用程序的助手功能。为此,我们需要在api文件夹中创建一个config.go文件,然后添加下面的摘要:

package 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.Fatal("Error loading .env file")
    }
    return os.Getenv("TWILIO_ACCOUNT_SID")
}

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

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

上面的摘要执行以下操作:

  • 导入所需的依赖项
  • 创建一个envACCOUNTSIDenvAUTHTOKENenvSERVICESID功能,该功能检查环境变量是否正确加载并返回环境变量。

创建API模型
接下来,我们需要创建模型来表示我们的应用程序数据。为此,我们需要导航到data文件夹,在此文件夹中,创建一个model.go文件,然后添加下面的摘要:

package data

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

type VerifyData struct {
    User *OTPData  `json:"user,omitempty" validate:"required"`
    Code string `json:"code,omitempty" validate:"required"`
}

创建API路线,助手,服务和处理程序
使用用于发送和验证OTP的模型完全设置,我们需要导航到api文件夹并执行以下操作:

首先,我们需要创建一个用于配置API路由的route.go文件,并在下面添加摘要:

package api

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

type Config struct {
    Router *gin.Engine
}

func (app *Config) Routes() {
    //routes will come here
}

上面的摘要执行以下操作:

  • 导入所需的依赖项
  • 使用Router属性创建一个Config结构,以配置应用程序方法
  • 创建一个Routes函数,该函数将Config struct作为指针

其次,我们需要创建一个helper.go文件,然后添加下面的摘要:

package api

import (
    "net/http"

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

type jsonResponse struct {
    Status  int    `json:"status"`
    Message string `json:"message"`
    Data    any    `json:"data"`
}

var validate = validator.New()

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

func (app *Config) writeJSON(c *gin.Context, status int, data any) {
    c.JSON(status, jsonResponse{Status: status, Message: "success", Data: data})
}

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

上面的摘要执行以下操作:

  • 导入所需的依赖项
  • 创建一个jsonResponse struct和validate变量来描述API响应并验证API字段
  • 创建一个validateBody函数,该函数将Config struct作为指针作为指针并返回error。在函数内部,我们以正确格式验证该请求data,并使用验证库验证并检查所需字段
  • 创建一个writeJSON函数,该函数将Config struct作为指针作为指针,并使用jsonResponse struct在没有错误的情况下构造API响应
  • 创建一个errorJSON函数,该函数将Config struct作为指针作为指针,并在存在错误时使用jsonResponse struct构造API响应

第三,我们需要创建一个用于抽象应用程序逻辑的service.go文件,并在下面添加摘要:


package api

import (
    "github.com/twilio/twilio-go"
    twilioApi "github.com/twilio/twilio-go/rest/verify/v2"
)

var client *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 := client.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 := client.VerifyV2.CreateVerificationCheck(envSERVICESID(), params)
    if err != nil {
        return err
    } else if *resp.Status == "approved" {
        return nil
    }

    return nil
}

上面的摘要执行以下操作:

  • 导入所需的依赖项
  • 创建一个client变量,以使用帐户SID auth Token 配置Twilio客户端
  • 创建一个接受phoneNumbertwilioSendOTP函数,将Config struct作为指针接收,并返回stringerror。在函数内部,我们通过添加phoneNumber并设置将OTP作为sms的频道创建了一个params变量。最后,我们使用client变量通过使用服务sid params创建验证,然后返回适当的响应
  • 创建一个接受phoneNumbercodetwilioVerifyOTP函数,将Config struct作为指针接收,并返回error。在函数内部,我们通过添加phoneNumbercode创建了一个params变量。最后,我们使用client变量通过使用服务sid params来检查OTP的真实性,然后返回适当的响应

第四,我们需要创建一个handler.go文件,以修改传入请求并添加下面的摘要:

package api

import (
    "context"
    "go-sms-verification/data"
    "net/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 data.OTPData
        defer cancel()

        app.validateBody(c, &payload)

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

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

        app.writeJSON(c, http.StatusAccepted, "OTP sent successfully")
    }
}

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

        app.validateBody(c, &payload)

        newData := data.VerifyData{
            User: payload.User,
            Code: payload.Code,
        }

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

        app.writeJSON(c, http.StatusAccepted, "OTP verified successfully")
    }
}

上面的摘要执行以下操作:

  • 导入所需的依赖项
  • 创建一个appTimeout变量来设置请求超时
  • 创建一个sendSMS函数,该函数返回杜松子酒处理程序,并将Config结构作为指针接收。在返回的处理程序内部,我们定义了API超时,使用了助手功能,并较早创建的服务来验证请求主体并发送OTP
  • 创建一个verifySMS函数,该功能返回杜松子酒处理程序,并将Config结构作为指针接收。在返回的处理程序内部,我们定义了API超时,使用了助手功能,并较早创建的服务来验证请求主体和OTP

最后,我们需要更新routes.go文件API路由和相应的处理程序

package api

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

type Config struct {
    Router *gin.Engine
}

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

将它们全部放在一起
通过完全设置API,我们需要创建应用程序输入点。为此,我们需要导航到cmd文件夹,在此文件夹中,创建一个main.go文件,然后添加下面的摘要:

package main

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

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

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

    //routes
    app.Routes()

    router.Run(":80")
}

上面的摘要执行以下操作:

  • 导入所需的依赖项
  • 使用Default配置创建杜松子路由器
  • 通过传递Router初始化Config结构
  • 添加路线并在端口:80上运行应用程序

完成此操作,我们可以使用以下命令启动开发服务器:

go run cmd/main.go 

send OTP
OTP
Verifying OTP

我们还可以通过导航到Twilio

上的 logs 选项卡来验证消息日志

Message log

结论

这篇文章讨论了如何创建使用GO和Twilio的验证服务来检查和验证用户的API。除了基于SMS的验证之外,Twilio还会发货多个服务以将身份验证无缝整合到新的或现有的代码库中。

这些资源可能会有所帮助: