如何用Express和Twilio Verify通过短信验证一个用户

785 阅读8分钟

许多应用程序通过语音电话或短信向用户的电话号码发送一个数字代码,称为一次性密码(OTP),来验证和授权他们的用户。

在这篇文章中,你将学习如何通过Twilio Verify通过短信进行用户验证。你将使用JavaScript和HTML建立一个精简的前端,你将使用Express建立一个Node.js后端。

前提条件

要开始学习本教程,你需要以下条件:

  • 在你的机器上安装了Node.js,以及一个软件包管理器,如 [npm](https://www.npmjs.com/)[yarn](https://classic.yarnpkg.com/en/)
  • 一个免费的Twilio账户(用这个链接注册,升级账户后可获得10美元的免费积分)
  • 一个可以接收短信的电话

构建你的后端

在你的终端或命令提示符窗口,导航到你的主项目或开发文件夹。在那里,运行以下命令,创建一个新的项目文件夹,初始化一个新的Node.js应用程序,并安装Twilio Node Helper Library和其他依赖项:

mkdir verify-sms-express
cd verify-sms-express
npm init -y
npm install express twilio dotenv

用你的Twilio凭证创建一个.env文件

在新的verify-sms-express应用中,创建一个名为*.env的新文件。你将在这个文件中存储你的Twilio账户SIDAuth Token ,以及其他一个凭证。复制并粘贴以下文本到你的.env*文件中:

TWILIO_ACCOUNT_SID=XXXX
TWILIO_AUTH_TOKEN=XXXX
VERIFY_SERVICE_SID=XXXX

XXXX 代表占位值,你很快就会替换掉,所以现在让这个文件打开。

导航到Twilio控制台。你可以在控制台的主仪表板上找到你的账户SIDAuth Token。复制这些值并把它们粘贴到你的*.env*文件中,分别作为TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKEN 的值。

为了使用Twilio Verify,你还需要设置一个新的Twilio Verify服务。服务是一组配置,你可以用来管理你的应用程序或网站上的验证。

创建一个新的Verify服务

导航到Twilio控制台Verify部分,从左边的菜单中选择服务。点击蓝色的加号(+)来创建一个新的服务。一个模式对话框会弹出,你可以为你的服务输入一个名称。称之为 "node-demo",或任何其他你选择的简短描述性名称。

创建你的服务后,你将被带到你的新服务的一般设置页面:

Twilio Service General Settings page

在你的服务名称下面,你会看到你的SERVICE SIDVERIFY_SERVICE_SID 复制这个值并把它粘贴到你的*.env*文件中,替换掉XXXX 这个变量的占位符。

保存并关闭你的*.en*v文件。

回到你的浏览器中,继续向下滚动验证服务常规设置页面到底部,找到交付渠道部分。因为你在这个例子中只打算使用短信,所以最好禁用其他两个渠道:

screenshot of verify service settings page showing call enabled and sms and email disabled

点击 "保存"按钮,记录你的改变。恭喜你,你的短信验证服务现在已经完全配置好了,你可以开始编写应用程序的代码了。

建立前台

在这一部分,你将设置你的应用程序的前端。

从你的verify-sms-express文件夹中运行以下命令来创建你的前端文件结构:

mkdir views
cd views
touch index.html

在你喜欢的文本编辑器中打开你的新index.html文件,添加以下HTML:

<!DOCTYPE html>
<html>
  <head>
    <title>Verify SMS Demo</title>
  </head>

  <body>
    <form id="phone-form">
      <h2>Enter your phone number with country code:</h2>
      <input type="tel" id = "phone-number-input" placeholder="e.g. 15551235555"/>
      <input id="phone-submit" type="submit"/>
    </form>

    <form id="verify-form">
      <h2>Enter your verification code:</h2>
      <input type="number" id="otp-input" placeholder="e.g. 123456"/>
      <input id="verify-submit" type="submit"/>
    </form>

    <div id="response-text"></div>
  </body>

  <script type="text/javascript" src = "script.js"></script>
</html>

这个HTML创建了两个表单:一个供用户输入他们的电话号码,另一个供用户输入他们在提交电话号码后将通过短信收到的验证码。它还创建了一个空的<div></div> 元素,最终将向用户显示一个成功或失败的信息。

你不希望这两个表单同时可见,所以在<head></head> 标签中,在<title> 元素下面添加以下CSS,以便在页面加载时隐藏#verify-form (和空的<div></div> ):

<style>
  #verify-form,
  #response-text {
    display: none;
  }
</style>

编写代码来处理表单提交

在你的终端或命令提示符中,回到你的项目的根目录,验证-短信-表达,并创建一个新的文件夹,名为public:

mkdir public

在这个文件夹中,创建一个名为script.js的新文件。

这个文件是你写代码的地方,用来处理表单提交并与Twilio API连接。

script.js的顶部,粘贴以下代码:

const phoneForm = document.getElementById('phone-form');
const verifyForm = document.getElementById('verify-form');
const responseText = document.getElementById('response-text');

let phoneNumber;

这段代码声明了一些你将在脚本中使用的变量。

在变量声明的下面,添加以下代码,以处理任何提交到你的#phone-form:

phoneForm.addEventListener('submit', async e => {
  e.preventDefault();

  phoneNumber = document.getElementById('phone-number-input').value;

  const response = await fetch('/send-notification', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({phoneNumber : phoneNumber })
  }).catch(e => console.log(e));

  if (response.ok) {
    phoneForm.style.display = 'none';
    verifyForm.style.display = 'block';
  }
});

这段代码监听#phone-form 上的一个submit 事件。每当这个submit 事件发生时,就会运行一个回调函数,处理以下任务:

  • 防止表格的正常提交
  • 捕获用户在#phone-number-input 字段中输入的电话号码的值
  • 向应用程序的后端端点*/send-notification*(如上面的代码中所强调的)提出一个POST ,其中包含电话号码的值
  • 在请求的成功响应后切换可见的表单

在所有现有的代码下面,添加以下代码来处理#verify-form 提交:

verifyForm.addEventListener('submit', async e => {
  e.preventDefault();

  const otp = document.getElementById('otp-input').value;

  const data = {
    phoneNumber: phoneNumber, 
    otp: otp
  };

  const response = await fetch('/verify-otp', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
    body: JSON.stringify(data)
  }).catch(e => console.log(e));

  const check = await response.json();

  const text = response.ok ? check.status : response.statusText;
  responseText.innerHTML = text;

  verifyForm.style.display = 'none';
  responseText.style.display = 'block';  
});

这段代码的工作原理与你在上一步添加的代码相似,但它是在验证阶段使用的。主要的区别是,这段代码将电话号码和用户提交的OTP的值都发送到应用程序的后端端点*/verify-otp*。

当它收到来自POST 请求的响应时,它通过#response-text 元素向用户显示验证状态。

保存并关闭你的script.js文件。现在是建立后台的时候了!

建立后端

第一步是在你的项目目录根部创建一个名为index.js的新文件。这个文件是你所有后端代码的位置。

在文本编辑器中打开你的新文件并添加以下代码:

const express = require('express');
const path = require('path');
require('dotenv').config();
const client = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);

这段代码将加载你的依赖性,并初始化Twilio客户端。

在你刚刚添加的代码下面,粘贴以下代码:

const app = express();
const port = process.env.PORT || 3000;

app.use(express.static(__dirname + '/public'));
app.use(express.urlencoded({extended: true}));
app.use(express.json()); 

接下来,添加当你访问*http://localhost:3000*,加载你的前端所需的代码。

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/views/index.html'));
});

在上面的前端代码中,你向两个后端端点提出了POST/send-notification和*/verify-otp*。你现在要创建这些端点。

向用户发送验证码

/send-notification端点接收用户的电话号码,并使用Twilio Verify API,向该电话号码发送短信,并将OTP发送给收件人。这就启动了验证过程。

要创建这个端点,请在index.js的所有现有代码下面添加以下内容:

app.post('/send-verification', async (req, res) => {
  client.verify.services(process.env.VERIFY_SERVICE_SID)
    .verifications
    .create({to: `+${req.body.phoneNumber}`, channel: 'sms'})
    .then(verification => console.log(verification.status))
    .catch(e => {
      console.log(e)
      res.status(500).send(e);
    });

  res.sendStatus(200);
});

验证是在上面的代码中突出显示的那一行启动的。在传递给create() 方法的对象中,你可以添加其他属性来进一步定制验证。

例如,如果你想让电话以另一种语言传递(Twilio支持超过30种语言的短信和语音验证),你可以添加添加locale 属性:

.create({to: `+${{req.body.phoneNumber}`, channel: 'sms', locale: 'es'})

验证用户提交的OTP

/verify-otp端点同时接收用户的电话号码,以及用户提交的OTP。使用Twilio的Verify API,这个端点检查以确认提交的OTP是正确的,并返回一个包含该验证检查数据的响应,包括一个状态。

一个验证检查可以有三种状态:待定、批准取消:

  • 待定的验证检查是指任何已经启动但尚未被批准、取消或过期的验证。
  • 批准的验证检查是指OTP已经被确认为匹配的验证。
  • 取消 验证检查是指被取消,或者如果验证码被发送给用户后已经过了10分钟或更长时间,则是过期。

在你刚刚添加的/send-verification端点下面,粘贴以下代码:

app.post('/verify-otp', async (req, res) => {
  const check = await client.verify.services(process.env.VERIFY_SERVICE_SID)
    .verificationChecks
    .create({to: `+${req.body.phoneNumber}`, code: req.body.otp})
    .catch(e => {
      console.log(e)
      res.status(500).send(e);
    });

  res.status(200).send(check);
});

这段代码对用户提交的电话号码和OTP进行了验证检查。然后,它发送一个新的响应,将验证检查数据作为响应的主体。

为了完成你的index.js文件,在所有的代码下面,添加这两行:

app.listen(port);
console.log('Server started at http://localhost:' + port);

这段代码负责在文件运行时启动你的本地服务器。

测试你的应用程序

现在是测试应用程序的时候了!在你的命令行中,如果你还没有找到verify-sms-express目录,请导航到该目录。运行以下命令来启动你的本地开发服务器:

node index.js

这将启动一个本地服务器,端口为3000

在你的浏览器中,导航到*http://localhost:3000/*,查看你的应用程序。

Screenshot of app showing phone number input field

在字段中输入你的电话号码,从国家代码开始,然后点击提交的按钮。

一旦你点击提交按钮,电话号码表格将被替换为验证码表格。几分钟后,你会通过短信收到一个OTP。

回到你的浏览器中,提交你在验证表中收到的验证码。

Screenshot of app showing OTP in input field

当你点击提交按钮时,你创建的*/verify-otp*端点将被调用。如果你在表格中输入了正确的代码,一旦你的应用程序的前端收到来自后台的响应,你将看到 "批准 " 状态消息。

screenshot showing approved verification check status

如果你输入了错误的代码,信息将显示 "待定"。在一个生产应用中,你可能会给用户一个机会,让他再次输入代码,第二次显示验证表格,而不是 "待定 "信息。