Python、Flask、Twilio Verifyを使用してメールアドレスを認証する方法

May 19, 2021
執筆者
Diane Phan
Twilion
レビュー担当者

How-to-Verify-an-Email-Address-Using-Python-Flask-and-Twilio-Verify-JP

このBlogはDeveloper Voices Teamに所属するDiane Phanこちらで執筆した記事を日本語化したものです。

Python、Flask、Twilio Verifyを使用してメールアドレスを認証する方法

大半のWebアプリケーションでは、サインアッププロセス中に、ユーザーのメールアドレスを受け付けます。不正アカウントの作成を防ぐには、指定されたメールアドレスでユーザーがメールを受信できることを確認するという方法が常に有効です。

Twilio Verifyは、SMS、音声通話、メールを介して数値コードを送信する、というユーザー確認に使いやすいサービスです。このチュートリアルでは、メール認証フローをFlask、Twilio Verify、SendGridを使って実現する方法について説明します。

Flask - Verify - SendGrid

チュートリアルの要件

  • Python 3.6以降 - オペレーティングシステムにPythonインタープリターがない場合は、python.orgからインストーラーをダウンロードしてください
  • Twilioアカウント - 初めてTwilioを利用する場合は、こちらをクリックして無料アカウントを作成してください。このリンクを用いてアカウントを作成すると有料アカウントへのアップグレード時に使える10ドルのクレジットを進呈します。トライアルアカウントの制限についてはこちらを確認してください
  • SendGridアカウント。こちらをクリックして無料アカウントを作成してください。このアカウントでは1日最大100通のメールを送信できます

SendGridの設定

メール認証ソリューションを設定するには、TwilioアカウントとSendGridアカウントを接続する必要があります。このセクションでは、必要な準備をSendGrid側で行います。

動的テンプレートの作成

最初のステップとしてメールテンプレートを作成します。Twilio Verifyサービスはこのテンプレートを使用しユーザーに認証コードを送信します。

SendGridダッシュボードから、左側にあるメニューの[Email API]をクリックし、続けて[Dynamic Templates](動的テンプレート)をクリックします。

SendGrid - Dynamic Template

[Create Dynamic Template](動的テンプレートの作成)ボタンをクリックし、テンプレートを新規作成します。この新しいテンプレートに任意の名前を付けます。このチュートリアルでは「email-verification」という名前を使用します。

SendGrid - Create Dynamic Template

[Create](作成)ボタンをクリックすると、[Dynamic Templates](動的テンプレート)ページに移動し、新規作成した「email-verification」テンプレートが表示されます。テンプレート名をクリックすると展開され、次のスクリーンショットのように詳細が表示されます。

Dynamic Template - Details

[Add Version](バージョンの追加)ボタンをクリックし、テンプレートの最初のバージョンを作成すると、あらかじめ用意されたテンプレートの選択肢が表示されます。このチュートリアルでは、便宜上、[Blank Template](空白のテンプレート)を選択します。ただし、HTMLに精通している場合は、このテンプレート一覧から選択しても構いません。

次のプロンプトでは、テンプレート用のエディターを選択するよう求めてきます。[Code Editor](コードエディター)を選択し、メール本文のHTMLコードに直接アクセスできるようにします。

空白のテンプレートには会員登録を解除するリンクが記載されたフッターがあるため、実際には空白ではありません。このリンクは変更せず、メール本文に文章を挿入できるように次のHTML行を追加します。追加する場所はメール本文の上、<body>要素タグのすぐ後です。

<p>{{twilio_message}}.</p>

下の図は、この段落がメールの中にどのように収まるかをコードエディターで示しています。

dynamic template - html editor

このテンプレートにおいて{{twilio_message}}はプレースホルダであり、Twilioによりメールのテキストに置き換えられます。メールには「認証コードは、xxxxxxxxです」などの内容が記述されます。

{{twilio_message}}がニーズに合わない場合は、メール本文の設計に使用できるプレースホルダがいくつかあります。詳しくは、こちらのドキュメントを参照してください。

実際のデータを入れたメールの見た目を確認したい場合は、ページ上部の[Test Data](テストデータ)タブをクリックし、twilio_message変数のサンプルの値をJSON形式で入力します。例えば次のように入力します。

{"twilio_message": "Your verification code is XXX"}

作成したテンプレートに問題がなければ、ページ左上の[Settings](設定)ボタンをクリックし、メールの件名を入力します。

dynamic template - subject

ナビゲーションバーの[Save](保存)ボタンをクリックし、ページ左上の左矢印ボタンを押して[Dynamic Templates](動的テンプレート)ページに戻ります。

新しいメールテンプレートには「テンプレートID」が割り当てられます。このIDは、後でTwilioアカウントを設定する際に必要になります。IDが表示される場所を次のスクリーンショットで確認してください。

dynamic template - id

APIキーの作成

SendGrid設定の第2のステップはAPIキーの作成です。TwilioではAPIキーを使用してユーザーに認証メールを送信できます。

ダッシュボードから[Settings](設定)、[API Keys](APIキー)の順に選択します。[API Keys](APIキー)ページで[Create API Key](APIキーの作成)ボタンをクリックします。

APIキーに名前を付けます。今回も任意の名前を付けることができますが、ここでは「email-verification」という名前を使用しています。名前の下の3つのオプションから[Full Access](フルアクセス)を選択します。

SendGrid - Create API Key

[Create & View](作成と表示)ボタンをクリックしてキーを作成すると、次のページにAPIキーが表示されます。作成したキーを確認できるのはこのときだけです。キーをコピーしてテキストとして保存しておきましょう。次のセクションで必要になるまですぐに利用できるようにしておきます。

Twilioの設定

Twilio Verifyサービスでは、SendGrid APIキーと前のセクションで設定した動的テンプレートを用いてメールを送信します。今からTwilioアカウントに移動して設定を完了し、TwilioアカウントとSendGridアカウントをリンクします。

メール連携の作成

Twilioコンソールから[All Products & Services]ボタンをクリックすると[Verify]メニューがあります。その下の[Email Integration]をクリックします。

[Create Email Integration]ボタンをクリックし、新規のメール連携を作成します。すでにメール連携が存在する場合は、[+]印をクリックしてもう1つ追加します。

Verify - Email Integration

メール連携に名前を付けるように促すプロンプトが表示されます。ここでは「email-verification」という名前にしています。

Create New Email Integration

名前を付けた後に、SendGridアカウントの詳細を入力します。次の情報を指定する必要があります。

  • SendGrid APIキー(前のセクションで作成したもの)
  • 動的テンプレートのテンプレートID(前のセクションで作成したもの)
  • 認証メールの[From]フィールドで使用するメールアドレス
  • 認証メールの[From]フィールドで使用する名前。このフィールドには自社のWebサイト名や会社名を入力

Email Integration - details

上記のフィールドに入力した後、[Save]をクリックしてこのメール連携を保存します。

Verifyサービスの作成

[Verify]メニューから[Services]を選択します。次に[Create Service Now]ボタンをクリックします。アカウントにすでにVerifyサービスが存在する場合は、[+]ボタンをクリックして新しいサービスを追加します。

Verify Service

サービスに分かりやすい名前をつけます。ここで付けた名前はユーザーに送信される認証メールで表示されます。そのため、自社のWebサイト名や会社名を入力することをお勧めします。このチュートリアルでは「My Company」という名前を付けています。

Create New Verify Service

次に、このサービスで編集可能な設定項目の画面について説明します。ページの上方に認証コードで使用する数字の桁数を設定するドロップダウンがあります。ここでは下の図のように8桁を選択しました。

digits used in verify service

下の[Email Integration]セクションまでスクロールし、さきほど作成したメール連携をドロップダウンで選択します。

integrate verify and email

一番下までスクロールすると、[Delivery Channels]セクションがあります。この例ではメールのみを使用するため、他の2つのチャネルは無効化しておくと良いでしょう。

verify channels

[Save]ボタンをクリックして変更を保存します。これでメール認証サービスの設定が完了し、FastAPIアプリケーションのコーディングを開始する準備が整いました。

プロジェクトのセットアップ

この章では、新しいFlaskプロジェクトをセットアップします。手順を整然と進められるよう、ターミナルまたはコマンドプロンプトを開き、新たなディレクトリの作成に適した場所を見つけます。ここでこれから作成するプロジェクトを実行します。

mkdir python-verify-email 
cd python-verify-email

仮想環境の作成

Pythonのベストプラクティスに従い、ここで仮想環境を作成します。仮想環境には、このプロジェクトで必要なPythonの依存関係をインストールします。

UnixまたはmacOSシステムを使用している場合は、ターミナルを開き、次のコマンドを入力します。

python3 -m venv venv 
source venv/bin/activate

Windowsでチュートリアルを実行する場合には、コマンドプロンプトウィンドウに以下のコマンドを入力します。

python -m venv venv 
venv\Scripts\activate

仮想環境を有効にすると、このプロジェクトに必要なPythonの依存関係をインストールできます。

pip install python-dotenv twilio flask

このプロジェクトで使用するPythonパッケージは次のとおりです。

  • Python-dotenv - アプリケーションの設定を.envファイルからインポートするライブラリ
  • Twilio APIと連携するためのTwilio Python Helperライブラリ
  • Flaskフレームワーク - Twilioからメッセージ通知を受け取るWebアプリケーションを作成

開発用Flaskサーバーのセットアップ

プロジェクトディレクトリの仮想環境を開いていることを確認します。プロジェクト全体でFlaskを使用するため、開発サーバーのセットアップが必要です。.flaskenvファイル(先頭のピリオドを忘れずに)をプロジェクトに追加し、次のコードをコピーします。

FLASK_APP=app.py
FLASK_ENV=development

このコードは大変便利です。プロジェクトのテストとデバッグの際に大幅に時間を短縮できます。

  • FLASK_APP - Flaskフレームワークにアプリケーションの場所を伝えます。
  • FLASK_ENV - デバッグモードで実行するようFlaskを構成します。

これらの行を入れることにより、ソースファイルを保存するたびにサーバーが再読み込みをして変更を反映するため非常に便利です。

次に、ターミナルにflask runと入力してFlaskフレームワークを開始します。

Run Flask App

上のスクリーンショットは、flask runコマンドの実行後のコンソールを示しています。このサービスはコンピューターのポート5000でインターネットには公開されていない状態で実行しており、着信接続を待機しています。またデバッグモードが有効化されており、このモードではFlaskサーバーが自動的に再起動を行い、ソースコードの変更を取り込みます。

アプリケーション設定の定義

Twilio Verifyを用いて認証メールを送信するには、FlaskアプリケーションでTwilioアカウントの資格情報にアクセスし、認証する必要があります。また、このアプリケーションに先ほど作成したTwilio VerifyサービスのIDを設定することも必要です。

こうした設定値を最も安全に定義する方法は設定値の環境変数を設定することであり、Flaskアプリケーションで環境変数を管理する最も便利な方法は.envファイルを使用することです。

テキストエディターで新規ファイルを開き、「.env」(先頭に点があります)という名前を付けて次の内容を入力します。

TWILIO_ACCOUNT_SID=xxxxxxxxx 
TWILIO_AUTH_TOKEN=xxxxxxxxx 
TWILIO_VERIFY_SERVICE=xxxxxxxxx

すべてのxxxxxxxxxは、対応する正しい値に置き換えてください。最初の2つの変数は、Twilioの[Account SID](アカウントSID)と[Auth Token](認証トークン)です。これらの値はTwilio Consoleのダッシュボードで確認できます(下図)。

AccountSid and AuthToken in Twilio Console

TWILIO_VERIFY_SERVICE変数は、サービスに割り当てられた[SERVICE SID]の値です。この値はサービスの設定ページで確認できます。

Verify Service SID

プロジェクトのロジックを計画する

プロジェクトのロジックのフローは次のとおりです。

  • ユーザーがWebサイトのホームページにメールアドレスを入力する。
  • Flaskアプリケーションが、ホームページに入力されたメールアドレスにワンタイムパスコードを送信する。
  • メールで受信した認証コードを入力して認証するように促すプロンプトをユーザーに表示する。

では、コーディングを始めましょう。

作業中のディレクトリでファイルを作成し、app.pyという名前を付け、次のコードをコピーします。

import os
from dotenv import load_dotenv
from flask import Flask, request, render_template, redirect, session, url_for
from twilio.rest import Client

load_dotenv()
app = Flask(__name__)
app.secret_key = 'secretkeylol'

TWILIO_ACCOUNT_SID = os.environ.get('TWILIO_ACCOUNT_SID')
TWILIO_AUTH_TOKEN= os.environ.get('TWILIO_AUTH_TOKEN')
TWILIO_VERIFY_SERVICE = os.environ.get('TWILIO_VERIFY_SERVICE')
SENDGRID_API_KEY= os.environ.get('SENDGRID_API_KEY') 

client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)

ファイルの先頭で、必要なPythonモジュールとライブラリをインポートしました。これでプロジェクトに環境変数を読み込めます。

Flaskアプリケーションはsecret_keyも備えており、数段階のsecurityレベルに対応します。「'secretkeylol'」は適当な文字列に置き換えることができます。今回のプロジェクトでは、ユーザーのアカウント情報を保管し、Flaskのセッションを使用して他のルートに渡す必要があるため、文字列も必要です。

HTMLページで使用するテンプレートフォルダの作成

このプロジェクトで使用するUIの構築には、Flaskテンプレートを使用します。作業中のディレクトリにtemplatesという名前のフォルダを作成し、ここに次のファイルを作成します。

  • index.html - ユーザー用のランディングページ。メールアドレスを入力し、認証トークンをリクエスト
  • verifypage.html - ユーザーがプロンプトに従い認証コードを入力するページ  
  • success.html - ユーザーアカウントの保護が成功したときに表示されるページ

ユーザーログインページの作成

このプロジェクトでは、ユーザーはWebサイトでメールアドレスを入力します。次のコードをコピーし、作成したapp.pyファイルに貼り付けます。

@app.route('/', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        to_email = request.form['email']
        session['to_email'] = to_email
        send_verification(to_email)
        return redirect(url_for('generate_verification_code'))
    return render_template('index.html')

@app.route(‘/’)デコレーターはアプリケーションのルートURLにマッピングされているエンドポイントを定義します。エンドポイントを実装すると、index.htmlという名前の静的ファイルから読み込まれたレスポンスが返されます。

POSTリクエストが出され、参加者のメールがFlaskセッションに保存されます。そのメールは現在実行中のFlaskセッションに保存され、入力されたメールアドレスに認証トークンが送信されます。参加者は他のルートにリダイレクトされ、2つ目のフォームが表示されます。このフォームで認証コードを送信できます。

参加者からテキストを取得するための、やりとりが可能で適切なHTMLフォームを作成する必要があります。フォームを作成します。このフォームでemailの入力を受け付けます。フォームには送信ボタンも必要です。この必要最小限のHTMLフォームをコピーし、index.htmlファイルに貼り付けて自由に利用してください。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <h1>Login</h1>
</head>
<body>         
  <form method="POST">
    <div class="field">
    <label class="label">Email</label>
      <input class="input" type="text" name="email" placeholder="Email">
    </div>
    <div class="field">
      <p class="control">
        <button type="submit" class="button is-success">
          Request verification code
        </button>
      </p>
    </div>
  </form>
</body>
</html>

フォームを設定した後、send_verification関数を構築します。この関数は、ユーザーがフォームを送信した後で実行されます。Twilio VerifyTimeを使用して認証コードを生成します。Twilio Verify APIをコールします。

login関数と同じルートにあるapp.pyファイルに次のコードを追加します。

@app.route('/', methods=['GET', 'POST'])

# ...

def send_verification(to_email):
    verification = client.verify \
        .services(TWILIO_VERIFY_SERVICE) \
        .verifications \
        .create(to=to_email, channel='email')
    print(verification.sid)

上記のコードを実行するとTwilio Clientは認証トークンをFlaskセッションに保存されているメールアドレス(to_email)に送信します。今回はメールを使用していますが、送信チャネルとしてSMSや音声通話も利用できるため、ユーザーが利用しやすいチャネルに後で変更可能です。

これは認証用のパスコードを送信するシンプルな関数であることにご注意ください。現時点でエラー処理はできません。

テストをしてみましょう。Webページで、認証コード送信用の個人用のメールアドレスを入力します。メールを開き、Twilio Verifyにより送信された認証コードのお知らせを確認します。

ユーザーのメールアドレスの検証

次に新しいユーザーフォームから受信したコードを取り出し、指定されたメールアドレスにTwilioが送信した認証コードと完全に同一であることを確認します。

HTMLでフォームを作成してプロジェクトを仕上げましょう。HTMLをコピーし、verifypage.htmlファイルの本文にコピーします。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Verify your account</title>
  </head>
  <body>
    <h1 class="title">
      Please verify your account {{email}}
    </h1>
    {% if error %}
      <p class=error><strong>Error:</strong> {{ error }}
    {% endif %}
    <form method="POST">
      <div class="field">
        <label class="label">Enter the code sent to your email.</label>
          <input class="input" type="text" name= "verificationcode" placeholder="verificationcode">
        </p>
      </div>
      <div class="field">
        <p class="control">
          <button type="submit" class="is-success", value = "submitcode">
            Submit Verification Code
          </button>
        </p>
      </div>
    </form>
  </body>
</html>

よくできました!これで、ユーザーは8桁の数値コードのIDを確認できるようになりました。コードメールアドレスに送信されました。

さて、Twilioがコードを送信する場合に8桁の数値コードの確認はどのようにすればよいでしょうか。/verifymeルートと、適切な関数を定義する必要があります。この定義により、ユーザーはパスコードを検証できます。

次のコードをコピーし、app.pyファイルの一番下に貼り付けます。

@app.route('/verifyme', methods=['GET', 'POST'])
def generate_verification_code():
    to_email = session['to_email']
    error = None
    if request.method == 'POST':
        verification_code = request.form['verificationcode']
        if check_verification_token(to_email, verification_code):
            return render_template('success.html', email = to_email)
        else:
            error = "Invalid verification code. Please try again."
            return render_template('verifypage.html', error = error)
    return render_template('verifypage.html', email = to_email)

verify_passcode_input()コードのすぐ下に、check_verification_token()関数を定義してください。これで、この関数を次のルートでコールできます。

def check_verification_token(phone, token):
    check = client.verify \
        .services(TWILIO_VERIFY_SERVICE) \
        .verification_checks \
        .create(to=phone, code=token)    
    return check.status == 'approved'

check_verification_token()関数がFlaskセッションに保管されているメールアドレスとverification_codeを取得します。認証コードはユーザーがテキストボックスに入力した値です。次にVerify APIをコールし、ワンタイムパスコードが正しく入力されたことを確認します。

ユーザーがフォームを送信した後、/verifymeルートにPOSTリクエストが出され、verify_passcode_input()関数がコールされます。パスコードが正しければ成功を通知するページが表示されます。ログインページのロジックと同様に、不正な検証コードが入力されるとページが更新され、エラーメッセージが表示されます。このページでもユーザーは認証コードの再入力を求められます。

成功のメッセージの表示

ユーザーは資格情報と認証コードを正しく入力しました。ここでユーザーを任意の場所にリダイレクトできますが、このチュートリアルでは成功を知らせるページにリダイレクトします。これはverify_passcode_input()関数で実装します。

このHTMLをtemplatesディレクトリにあるsuccess.htmlファイルに直接コピーします。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Successful Verification!</title>
  </head>
  <body>
    <div class="container">
      <h1 class="title">
        {{email}}'s Profile
      </h1>
      <h2 class="subtitle">
        Thanks for verifying your email! 
      </h2>
    </div>
  </body>
</html>

Twilio Verifyを使用したアカウント認証

ここでアプリをテストします。flask runを使用し、ターミナルでFlaskが実行されていることを確認します。http://localhost:5000/に移動し、個人用のメールアドレスを入力してください。

「mycompany@example.com」からのメールの受信を確認し、Twilio Verifyが提供する認証コードを確認します。

そのコードを正確に入力すると、確認に成功したことを伝える次のメッセージが表示されます。

email verification test

Twilio Verifyを使用したユーザー認証の次のステップ

これで、プロジェクトに安全性を高めるセキュリティが実装されました。  

Twilio Verifyでは、ユーザーの認証に使用できるチャネルを複数ご用意しています。使用できるチャネルには、SMSや音声通話などがあります。ここで紹介した認証方法やその他の手段について詳しくは、ドキュメントを参照してください。

さらに、Twilio VerifyとPythonを使用した、ワンタイムパスコードで保護された会議通話を実現する方法や、2要素認証によるブログの保護に関する記事をご覧ください。

Twilio Verify APIを使用してユーザーの保護に成功したプロジェクトの事例があれば、ぜひメールでお知らせください。

Diane Phanは開発者の声チームの開発者です。プログラミングの初心者による、楽しい大衆文化に寄与する創造的なプロジェクトの立ち上げを支援しています。Dianeへのご連絡は、dphan [at] twilio.comまたはLinkedInまでどうぞ。