Twilio Verifyを使用したFastAPIのメールアドレス認証
読む所要時間: 27 分
大半のWebアプリケーションでは、サインアッププロセス中に、ユーザーのメールアドレスを受け付けます。不正アカウントの作成を防ぐには、指定されたメールアドレスでユーザーがメールを受信できることを確認するという方法が常に有効です。
Twilio Verifyは、SMS、音声通話、メールを介して数値コードを送信しユーザー確認を行えるサービスです。このチュートリアルでは、メールを使用した検証フローをFastAPIアプリケーションに実装する方法について説明します。
チュートリアルの要件
- Python 3.6以降 - オペレーティングシステムにPythonインタープリターがない場合は、python.orgからインストーラーをダウンロードしてください
- Twilioアカウント - 初めてTwilioを利用する場合は、こちらをクリックして無料アカウントを作成してください。このリンクを用いてアカウントを作成すると有料アカウントへのアップグレード時に使える10ドルのクレジットを進呈します。トライアルアカウントの制限についてはこちらを確認してください
- SendGridアカウント。こちらをクリックして無料アカウントを作成してください。このアカウントでは1日最大100通のメールを送信できます
SendGridの設定
メール認証ソリューションを設定するには、TwilioアカウントとSendGridアカウントを接続する必要があります。このセクションでは、必要な準備をSendGrid側で行います。
動的テンプレートの作成
最初のステップとしてメールテンプレートを作成します。Twilio Verifyサービスはこのテンプレートを使用しユーザーに認証コードを送信します。
SendGridダッシュボードから、左側にあるメニューの[Email API]をクリックし、続けて[Dynamic Templates](動的テンプレート)をクリックします。
[Create Dynamic Template](動的テンプレートの作成)ボタンをクリックし、テンプレートを新規作成します。この新しいテンプレートに任意の名前を付けます。このチュートリアルでは「email-verification」という名前を使用します。
[Create](作成)ボタンをクリックすると、[Dynamic Templates](動的テンプレート)ページに移動し、新規作成した「email-verification」テンプレートが表示されます。テンプレート名をクリックすると展開され、次のスクリーンショットのように詳細が表示されます。
[Add Version](バージョンの追加)ボタンをクリックし、テンプレートの最初のバージョンを作成すると、あらかじめ用意されたテンプレートの選択肢が表示されます。このチュートリアルでは、便宜上、[Blank Template](空白のテンプレート)を選択します。ただし、HTMLに精通している場合は、このテンプレート一覧から選択しても構いません。
次のプロンプトでは、テンプレート用のエディターを選択するよう求めてきます。[Code Editor](コードエディター)を選択し、メール本文のHTMLコードに直接アクセスできるようにします。
空白のテンプレートには会員登録を解除するリンクが記載されたフッターがあるため、実際には空白ではありません。このリンクは変更せず、メール本文に文章を挿入できるように次のHTML行を追加します。追加する場所はメール本文の上、<body>
要素タグのすぐ後です。
下の図は、この段落がメールの中にどのように収まるかをコードエディターで示しています。
このテンプレートにおいて{{twilio_message}}
はプレースホルダであり、Twilioによりメールのテキストに置き換えられます。メールには「認証コードは、xxxxxxxxです」などの内容が記述されます。
{{twilio_message}}
がニーズに合わない場合は、メール本文の設計に使用できるプレースホルダがいくつかあります。詳しくは、こちらのドキュメントを参照してください。
実際のデータを入れたメールの見た目を確認したい場合は、ページ上部の[Test Data](テストデータ)タブをクリックし、twilio_message
変数のサンプルの値をJSON形式で入力します。例えば次のように入力します。
作成したテンプレートに問題がなければ、ページ左上の[Settings](設定)ボタンをクリックし、メールの件名を入力します。
ナビゲーションバーの[Save](保存)ボタンをクリックし、ページ左上の左矢印ボタンを押して[Dynamic Templates](動的テンプレート)ページに戻ります。
新しいメールテンプレートには「テンプレートID」が割り当てられます。このIDは、後でTwilioアカウントを設定する際に必要になります。IDが表示される場所を次のスクリーンショットで確認してください。
APIキーの作成
SendGrid設定の第2のステップはAPIキーの作成です。TwilioではAPIキーを使用してユーザーに認証メールを送信できます。
ダッシュボードから[Settings](設定)、[API Keys](APIキー)の順に選択します。[API Keys](APIキー)ページで[Create API Key](APIキーの作成)ボタンをクリックします。
APIキーに名前を付けます。今回も任意の名前を付けることができますが、ここでは「email-verification」という名前を使用しています。名前の下の3つのオプションから[Full Access](フルアクセス)を選択します。
[Create & View](作成と表示)ボタンをクリックしてキーを作成すると、次のページにAPIキーが表示されます。作成したキーを確認できるのはこのときだけです。キーをコピーしてテキストとして保存しておきましょう。次のセクションで必要になるまですぐに利用できるようにしておきます。
Twilioの設定
Twilio Verifyサービスでは、SendGrid APIキーと前のセクションで設定した動的テンプレートを用いてメールを送信します。今からTwilioアカウントに移動して設定を完了し、TwilioアカウントとSendGridアカウントをリンクします。
メール連携の作成
Twilioコンソールから[All Products & Services]ボタンをクリックすると[Verify]メニューがあります。その下の[Email Integration]をクリックします。
[Create Email Integration]ボタンをクリックし、新規のメール連携を作成します。すでにメール連携が存在する場合は、[+]印をクリックしてもう1つ追加します。
メール連携に名前を付けるように促すプロンプトが表示されます。ここでは「email-verification」という名前にしています。
名前を付けた後に、SendGridアカウントの詳細を入力します。次の情報を指定する必要があります。
- SendGrid APIキー(前のセクションで作成したもの)
- 動的テンプレートのテンプレートID(前のセクションで作成したもの)
- 認証メールの[From]フィールドで使用するメールアドレス
- 認証メールの[From]フィールドで使用する名前。このフィールドには自社のWebサイト名や会社名を入力
上記のフィールドに入力した後、[Save]をクリックしてこのメール連携を保存します。
Verifyサービスの作成
[Verify]メニューから[Services]を選択します。次に[Create Service Now]ボタンをクリックします。アカウントにすでにVerifyサービスが存在する場合は、[+]ボタンをクリックして新しいサービスを追加します。
サービスに分かりやすい名前をつけます。ここで付けた名前はユーザーに送信される認証メールで表示されます。そのため、自社のWebサイト名や会社名を入力することをお勧めします。このチュートリアルでは「My Company」という名前を付けています。
次に、このサービスで編集可能な設定項目の画面について説明します。ページの上方に認証コードで使用する数字の桁数を設定するドロップダウンがあります。ここでは下の図のように8桁を選択しました。
下の[Email Integration]セクションまでスクロールし、さきほど作成したメール連携をドロップダウンで選択します。
一番下までスクロールすると、[Delivery Channels]セクションがあります。この例ではメールのみを使用するため、他の2つのチャネルは無効化しておくと良いでしょう。
[Save]ボタンをクリックして変更を保存します。これでメール認証サービスの設定が完了し、FastAPIアプリケーションのコーディングを開始する準備が整いました。
プロジェクトのセットアップ
このセクションでは、新しいFastAPIプロジェクトをセットアップします。手順を整然と進められるよう、ターミナルまたはコマンドプロンプトを開き、新たなディレクトリの作成に適した場所を見つけます。ここでこれから作成するプロジェクトを実行します。
Windowsでチュートリアルを実行する場合には、コマンドプロンプトウィンドウに以下のコマンドを入力します。
仮想環境を有効にすると、このプロジェクトに必要なPythonの依存関係をインストールできます。
このプロジェクトで使用する6つのPythonパッケージは次のとおりです。
- FastAPIフレームワーク - Webアプリケーションの作成に使用
- python-dotenv - アプリケーションの設定を.envファイルからインポート
- aiofiles - FastAPIで静的ファイルを提供
- python-multipart - FastAPIでフォームデータを処理する機能を提供
- uvicorn - FastAPIアプリケーションを提供
- Twilio APIと連携するためのTwilio Python Helperライブラリ
アプリケーション設定の定義
Twilio Verifyを用いて認証メールを送信するには、FastAPIアプリケーションでTwilioアカウントの資格情報にアクセスし、認証する必要があります。また、このアプリケーションに先ほど作成したTwilio VerifyサービスのIDを設定することも必要です。
こうした設定値を最も安全に定義する方法は設定値の環境変数を設定することであり、FastAPIアプリケーションで環境変数を管理する最も便利な方法は.envファイルを使用することです。
テキストエディターで新規ファイルを開き、「.env」(先頭に点があります)という名前を付けて次の内容を入力します。
すべてのxxxxxxxxx
は、対応する正しい値に置き換えてください。最初の2つの変数は、Twilioの[Account SID](アカウントSID)と[Auth Token](認証トークン)です。これらの値はTwilioコンソールのダッシュボードで確認できます(下図)。
TWILIO_VERIFY_SERVICE
変数は、サービスに割り当てられた[SERVICE SID]の値です。この値はVerifyサービスの設定ページで確認できます。
上記3つの変数をFastAPIアプリケーションに取り込むために、config.pyという名前のファイルを作成し、次の内容を入力します。
FastAPIはpydantic
のBaseSettings
クラスを使用して設定を管理します。BaseSettings
のサブクラスは、属性として定義された変数を環境変数から自動的にインポートします。または、dotenvを用いることにより.envファイルから直接読み込むこともできます。
次のセクションでは、Settings
クラスの使い方について説明します。
FastAPIを用いたメール認証
ここでFastAPIアプリケーションのコードを記述します。
ベースとなるFastAPIアプリケーション
下の図は、FastAPIアプリケーションの最初のイテレーションです。このバージョンではメインページのみを返します。メインページにはメールアドレスを入力して検証できるWebフォームがあります。
テキストエディターまたはIDEで新規ファイルを開き、app.pyという名前を付けて次のコードを入力します。
settings
変数は前のセクションで.envファイルに保存した設定変数をインポートします。app変数はFastAPIアプリケーションを表します。client
変数はTwilioクライアントライブラリのインスタンスであり、Twilio APIへのリクエストの送信に使用します。Twilio Clientは設定から読み込まれる資格情報で初期化されます。資格情報はリクエスト送信時の認証で必要になります。
@app.get(‘/’)
デコレーターはアプリケーションのルートURLにマッピングされているエンドポイントを定義します。エンドポイントを実装すると、index.htmlという名前の静的ファイルから読み込まれたレスポンスが返されます。
このエンドポイントを機能させるには、HTMLファイルを作成する必要があります。エディターまたはIDEで新規ファイルを開き、index.htmlという名前を付けて次のHTMLコードを入力します。
このHTMLページでは、メールアドレスの入力を促すフィールドが1つあるフォームが作成されます。
サーバーの実行
アプリケーションはまだ完成していませんが、テスト用の機能は十分に備えています。これまでにプロジェクトディレクトリに作成したapp.py、index.html、.envの各ファイルがあることを確認し、次のコマンドでアプリケーションを起動します。
UvicornはFastAPIアプリケーションの実行で推奨されるサーバーです。--reload
により、uvicornはソースファイルを監視し、ファイルの変更のたびにサーバーを自動的に再起動します。残りのチュートリアルを参照する間、サーバーを実行したままで問題ありません。
アプリケーションが実行中であることを確認するには、Webブラウザを開き、アドレスバーにhttp://localhost:8000と入力します。ブラウザにはアプリケーションのメインページが読み込まれ、次のように表示されます。
フォームデータの処理
フォームを送信しようとすると、FastAPIは「Method not allowed」(メソッドは許可されていません)というエラーメッセージを返します。これは、フォームの送信がまだ実装されていないためです。
index.htmlの<form>
要素では、フォームはmethod
属性(post
に設定)により定義され、action
属性はありません。つまり、フォームはPOSTリクエストにより発信元のURLに送信されます。この例ではアプリケーションのルートURLです。
フォームの送信を処理するエンドポイントは、次のような構造になります。これをapp.pyの一番下に追加できますが、# TODO
で始まる行に留意してください。この行はまだ構築されていない関数の一部を示しています。
この2番目のエンドポイントでは、@app.post(‘/’)
デコレーターを使用してPOST
リクエストのハンドラーを定義します。このWebフォームはemail
という1つのフィールドを備えており、このフィールドが関数に渡される引数となります。FastAPIはフォームデータを解析し、クライアントから渡されたフィールドの値を抽出し、この引数の関数に送信します。
この後のセクションでこのエンドポイントを終了します。
認証コードの送信
handle_form()
関数が呼び出されると、アプリケーションは検証するメールアドレスを受け取り、前に作成したTwilioのclient
インスタンスを使用してユーザーにメールを送信できます。ただし、小さな問題が1つあります。
Python用のTwilio Helperライブラリが非同期アプリケーションに対応していないことです。このライブラリはTwilioサーバーへのネットワークリクエストを作成することになるため、非同期関数で直接使用した場合、asyncioループをブロックします。ループのブロックを避けるために、Twilioに関するすべての作業は、executor内で実行する関数の中でカプセル化できます。これにより、アプリケーションをスムーズに実行できます。
次のコードは、handle_form()
関数をapp.pyから更新したバージョンです。executor内でsend_verification_code()
関数を実行するロジックを用いています。
asyncioループのrun_in_executor()
メソッドを使用すると、別のスレッドやプロセスでブロッキング関数を実行できるため、ループはブロックされません。最初の引数は使用したいexecutorになります。または、既定のスレッドexecutorを問題なく使用できる場合はNone
になります。残りの引数は実行する関数と、そのポジショナル引数です。
ここで、send_verification_code()
関数の実装を見てみましょう。なお、これは標準的な同期コードであるため、この関数はasync
キーワードで定義されていません。この関数をapp.pyに追加します。
この関数はTwilio Clientのインスタンスを使用します。このインスタンスはグローバルスコープで作成されており、認証リソースを作成します。新たに作成された認証リソースは「pending」のステータスになります。そこで関数はassert文を使用し、オブジェクトが予期された状態であることを確認します。
この段階で、TwilioはSendGrid APIキーとテンプレートを使用して指定されたアドレスにメールを送信します。メールにはランダムに生成された数値コードが記載されています。
レスポンスの送信
前のセクションで、handle_form()
関数は不完全な状態で残されていました。executorにより認証コードが送信された後、サーバーはクライアントのWebブラウザにレスポンスを返す必要があります。
フォーム送信を処理する際に最も一般的な処理は、リダイレクトを伴う応答です。これにより、例えばフォームの二重送信や、ブラウザからユーザーに提示される分かりにくい警告など、潜在的な多くの問題を回避できます。これは、PRGパターン(Post/Redirect/Getの略)として知られています。
このアプリケーションの場合、ここでブラウザは2つ目のフォームを表示する必要があります。このフォームには、ユーザーがメールで受信した認証コードを入力します。2番目のフォームは、/verifyエンドポイントの下に実装されます。そのため、ここでリダイレクトが発生する必要があります。handle_form()
関数を完全に実装すると次のようになります。app.pyのバージョンを更新してください。
レスポンスにより、ブラウザは/verifyのURLにすぐにリダイレクトするよう指示されます。このアプリケーションの興味深い点は、ユーザーから提供されるメールアドレスが、コードの検証時にもう1度必要になるということです。アドレスが喪失しないよう、関数はセッションcookieを追加し、レスポンスを返す前にアドレスを保管します。
コードの受付
ユーザーが数値コードの書かれたメールを受信しました。ここでブラウザは2つ目のWebフォームを表示する必要があります。このフォームでコードを入力し、メールアドレスを確認します。
/verifyエンドポイントへのGETリクエストにより、フォームを備えたHTMLページが返されます。このエンドポイントをapp.pyに追加します。
このエンドポイントはverify.htmlファイルを参照します。この参照ファイルをプロジェクトディレクトリに作成し、次の内容を入力します。
コードの検証
コードを検証するロジックでは、Twilio APIにもう1度コールする必要があります。そのためexecutor内で実行する補足的な同期関数がもう1度必要になります。次の関数をapp.pyに追加します。
check_verification_code()
関数がメールアドレスと数値コードを受け取り、これらをTwilio Verifyサービスに送信して確認します。確認に成功すると、返された認証チェックリソースのステータスはapproved
になります。コードが正しくない場合、返されるリソースのステータスはpending
になります。関数はステータスを確認し、入力されたコードが正しい場合はTrue
を返し、正しくない場合はFalse
を返します。
ユーザーが確認のためにコードを送信すると、ブラウザでは/verifyエンドポイントへのPOSTリクエストが発行されます。次の図は、このリクエストで使用できるハンドラーです。この関数をapp.pyに追加します。
このエンドポイントは2つの引数を受け付けます。email
引数は、さきほど設定したcookieから取得しています。一方、code
引数は送信されたものです。これらの引数はcheck_verification_code()
関数に渡されます。この関数はasyncループをブロックしないようにexecutor内で実行する必要があります。
コードの確認に成功した場合、このエンドポイントからのレスポンスは/success URLにリダイレクトされます。コードを確認できない場合は、/verifyエンドポイントへのリダイレクトが発生します。これはコードを受け付けるフォームを表示するエンドポイントであり、ユーザーはこのフォームに新しいコードを入力できます。
次の図は/successエンドポイントの実装です。このハンドラーはapp.pyの最後に動作します。
これはHTMLページを表示する短いハンドラーです。success.htmlファイルを作成し、次の内容をコピーしてください。
このページはユーザーに認証が成功したことを通知します。他のメールアドレスの検証を行う場合に備えて、通知にはメインページへのリンクが記載されています。
アプリケーションのテスト
いよいよお待ちかねの瞬間です。アプリケーションが完成し、テストの準備ができました。app.pyが更新され、ここまでに紹介したすべての関数があることを確認してください。config.py、.env、index.html、verify.html、success.htmlの各ファイルがプロジェクトディレクトリ内にあることも確認します。
チュートリアルの開始時からuvicorn
を実行し続けている場合、ソースファイルに変更が加えられるたびにサーバーが自動的に再起動しているはずであるため、そのまま続行できます。現在サーバーを実行中でない場合は、次のコマンドにより起動できます。
Webブラウザを開いてhttp://localhost:8000に移動してください。フォームにメールアドレスを入力します。送信ボタンをクリックすると、認証コードを含むメールがすぐに送信されます。コードを入力するとメールアドレスが検証されます。
まとめ
これで、FastAPIとTwilio Verifyを使用してメールアドレスを検証する方法の説明を終わります。この記事で学んだテクニックは他のフレームワークにも適用できます。特に適しているのは、同様にasyncioをベースにしているQuart、Sanic、Tornadoなどです。非同期アプリケーションでTwilioを使用する方法について詳しくは、こちらのブログの「Using the Twilio Python Helper Library in your Async Applications(非同期アプリケーションでTwilio Python Helperライブラリを使用する方法)」をご覧ください。FastAPI向けのTwilio活用のヒントについて詳しくは、こちらのSMSの送信方法に関する記事をご覧ください。
TwilioとFastAPIを使用して何を構築されるか、とても楽しみです。
Miguel Grinbergは、Twilioのテクニカルコンテンツ担当プリンシパルソフトウェアエンジニアです。このブログでは皆様のプロジェクトを紹介します。ぜひ、mgrinberg [at] twilio [dot] comまでご連絡ください。