[Twilio+Node.js] 認証コードを読み上げる音声通話認証の実装

[Twilio+Node.js] 認証コードを読み上げる音声通話認証の実装

SMS が使えない環境や固定電話ユーザー向けに、Twilio Programmable Voice を使って 6 桁の認証コードを自動音声で読み上げる電話認証機能を実装します。TwiML を直接 API に渡すだけで完結する最小構成です。

対象読者

  • SMS が使えない環境での認証手段を探している人
  • Twilio の Programmable Voice を活用したい開発者
  • 高齢者や視覚障害者向けの UI/UX を検討している Web サービス提供者
  • 簡単な通話通知・読み上げ機能を API 経由で実装したい人
検証環境

バージョン

node: 22.13.1
npm: 10.9.2
node_modules/express: 5.1.0
node_modules/dotenv: 16.5.0
node_modules/twilio: 5.5.2

概要

この記事では、Twilio Programmable Voice を活用し、Web アプリからのリクエストをトリガーにして音声通話を発信し、 6 桁の認証コードを自動音声で読み上げる仕組みを実装する方法を紹介します。SMS 認証が使えない環境や、音声による案内が適している高齢者ユーザー、固定電話利用者などにとって、音声通話を用いた認証は有効な代替手段です。

今回は、Twilio Voice API の /calls エンドポイントに直接 TwiML を渡して完結させる最小構成での実装にフォーカスします。TwiML について詳しくは こちら

システム構成の流れ

  1. ユーザーが Web アプリで電話番号を入力して「認証コードを送信」ボタンを押す
  2. サーバー側で 6 桁の認証コードを生成し、DB またはメモリに保存
  3. Twilio の /calls API を呼び出し、twiml パラメータで読み上げ内容を直接指定
  4. ユーザーの電話に Twilio から着信し、自動音声でコードが読み上げられる

1

実装

環境構築

今回の実装は、Node.js を用いて Express サーバーを立ち上げ、Twilio の Voice API を呼び出す形で構成します。以下の手順でローカル環境を準備してください。

  1. Node.js プロジェクトの作成

任意の作業ディレクトリを作成し、初期化します。

mkdir twilio-voice-auth
cd twilio-voice-auth
npm init -y
  1. 必要なパッケージのインストール

Twilio API を操作するための SDK(twilio)と、サーバー用の Express をインストールします。

npm install express twilio dotenv
  1. .env ファイルの作成

ルートディレクトリに .env ファイルを作成し、Twilio のアカウント情報を記述します。

TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_CALLER_ID=+81XXXXXXXXXX
  • TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKEN は Twilio コンソールで確認できます。
  • TWILIO_CALLER_ID には Twilio で取得した発信元の電話番号(E.164 形式)を設定してください。
  1. app.js の作成

以降で紹介する /start-call および /verify-code のコードを app.js に実装します。

認証コードを生成し音声通話で読み上げる

ユーザーが「電話番号を入力 → 認証コード送信ボタンを押す」と、サーバー側で 6 桁の認証コードが自動生成され、Twilio の /calls API によってユーザーの電話へ音声通話が発信されます。このとき、コードは1桁ずつ読み上げられるよう、工夫して TwiML を作成しています。

まずは、認証コードの生成と音声通話の発信を行う /start-call API のコードです。

// app.js
const twilio = require('twilio');
const express = require('express');
require('dotenv').config();
const app = express();
app.use(express.json());

const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);

// 簡易的な in-memory ストレージ(実運用では DB や Redis を使用)
const codeStorage = new Map();

app.post('/start-call', async (req, res) => {
  const { phoneNumber } = req.body;

  // 6桁のコードを生成
  const code = Math.floor(100000 + Math.random() * 900000).toString();

  // 保存(5分間だけ有効:setTimeoutで自動削除)
  codeStorage.set(phoneNumber, code);
  setTimeout(() => codeStorage.delete(phoneNumber), 5 * 60 * 1000); // 5分

  // 読み上げやすく1桁ずつ区切る
  const spaced = code.split('').join(', ');

  const twiml = `
    <Response>
      <Say language="ja-JP" voice="alice">
        こんにちは。あなたの認証コードは、
      </Say>
      <Pause length="1"/>
      <Say language="ja-JP" voice="alice">
        ${spaced}
      </Say>
      <Say language="ja-JP" voice="alice">
        です。
      </Say>
    </Response>`;

  try {
    const call = await client.calls.create({
      to: phoneNumber,
      from: process.env.TWILIO_CALLER_ID,
      twiml
    });

    res.json({ success: true, callSid: call.sid });
    // res.json({ success: true, callSid: call.sid, code }); // ← DEV 時だけ表示しても良い
  } catch (error) {
    console.error(error);
    res.status(500).json({ success: false, error: error.message });
    // res.status(500).json({ success: false, error: error.message, code }); // ← DEV 時だけ表示しても良い
  }
});

認証コードを照合する

通話後、ユーザーが受け取った認証コードを Web アプリ上のフォームに入力した場合、そのコードとサーバー側に保存されたコードを照合する API を用意できます。

app.post('/verify-code', (req, res) => {
  const { phoneNumber, code } = req.body;
  const expected = codeStorage.get(phoneNumber);

  if (!expected) {
    return res.status(400).json({ success: false, message: 'コードが期限切れまたは存在しません。' });
  }

  if (code === expected) {
    codeStorage.delete(phoneNumber); // 認証成功後は削除
    return res.json({ success: true, message: '認証成功' });
  } else {
    return res.status(401).json({ success: false, message: '認証コードが一致しません。' });
  }
});
in-memory の注意点

この例では Map を使って簡易的に認証コードを保存していますが、以下のような制限があります。

注意点 内容
スケールしない サーバーをスケールアウトすると別インスタンス間で共有できない
プロセスが落ちると消える メモリ上のため、Node.js プロセスの再起動でコード情報は失われる
本番運用には不向き Redis や RDB による永続化+TTL付き保存が推奨される

サーバーの起動

最後に、 Express サーバーを起動するための行を追加します。

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

動作確認

この節では、今回紹介した認証コードの音声通話発信機能を実際に動かして確認する手順を紹介します。

  1. サーバーを起動

    node app.js
    
  2. 別のターミナルや Postman、curl を使って /start-call にリクエストを送信

    curl -X POST http://localhost:3000/start-call \
      -H "Content-Type: application/json" \
      -d '{"phoneNumber": "+81XXXXXXXXXX"}'
    
  3. 数秒後、指定した電話番号に Twilio から着信があり、6桁のコードが自動音声で読み上げられます。

  4. 読み上げられたコードを使って照合します。

curl -X POST http://localhost:3000/verify-code \
  -H "Content-Type: application/json" \
  -d '{"phoneNumber": "+81XXXXXXXXXX", "code": "読み上げられた6桁"}'
  1. 成功すると次のようなレスポンスが返されます
{ "success": true, "message": "認証成功" }
失敗時

例えば次のようなレスポンスが返されます。

{ "success": false, "message": "認証コードが一致しません。" }

おわりに

本記事では、Twilio の Voice API を使って 音声による認証コード読み上げを最小構成で実装する方法を紹介しました。この方式は、以下のような用途に応用可能です:

  • 高齢者向けの電話による本人確認
  • SMS が使えない固定電話環境での通知
  • ユーザー体験の向上を目的とした音声インターフェースの導入

今後は、音声認識や DTMF 入力を使った双方向通話への展開、再生音声のカスタマイズ、スケーラブルな構成への移行なども検討の余地があります。ぜひこの例を出発点に、より豊かなユーザー体験を設計してみてください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.