第10回 SMS/通話のMFAを外部テレフォニープロバイダに接続
目次
- SMS / 通話と Okta テレフォニーインラインフック
- SMS / 通話による認証の流れ
- テレフォニープロバイダ連携のための前提の整理
- ユースケース1:日本語 SMS のみの簡易なフロー
- ユースケース2:SMS と通話、多言語に対応
- サンプルフローのダウンロード
- 最後に
※手軽にサンプルフローの適用・実行をされる場合は、ユースケース1またはユースケース2の「サンプルフローを使用する」にジャンプしてください。サンプルフローファイルは「サンプルフローのダウンロード」から取得できます。
SMS / 通話と Okta テレフォニー インラインフック
本記事では、Okta Workforce Identity(以下 Okta )の SMS / 通話認証で、Okta Workflows を使い外部テレフォニープロバイダへ接続して SMS 送信と通話発信を行う手順を解説します。これを応用して、API 経由で複数サービスを接続・中継する汎用ミドルウェアとしての活用まで広げることも可能です。
Okta では認証方式の一つとして SMS / 通話認証を搭載しています。Okta が発行したワンタイムパスワード(以下 OTP)を SMS テキストまたは音声通話でユーザーに送信し、認証ウィジェットへ入力することで認証が成立します。これにより、ユーザーが実際に使用可能な電話番号を所有していることを検証する、所有要素としての認証を実現します。
SMS / 通話認証では、実際の SMS 送信や通話発信を担うテレフォニーサービスをお客様が選定・手配したプロバイダで行う必要があります。Okta は、その外部テレフォニープロバイダと連携するための「テレフォニーインラインフック」機能を提供します。
本ブログでは、Okta Workflows をミドルウェア(中継ノード)として構築し、Okta のテレフォニーインラインフックが生成する発信要求を外部テレフォニープロバイダのフォーマットに変換して送信する手順を解説します。外部テレフォニープロバイダは RESTful API をサポートしていたとしても Okta テレフォニーインラインフックとはデータフォーマットが一致しないことが多く、適切に変換処理を行う必要があります。その役割を今回は Okta Workflows で行おう、ということになります。
今回用意した Okta Workflows フローは2つで、「シンプルな SMS のみ」「SMS と通話を両方サポートし、さらにメッセージは多言語に対応、自動切り替え」です。外部テレフォニープロバイダは Twilio を用いています。
Okta テレフォニーインラインフック機能提供の経緯
Okta ではかつて SMS / 通話の発信機能、いわゆるテレフォニーサービスまで含めて SMS / 通話のMFAを提供しておりました。しかしながら、テレフォニーサービスの提供を以下のように段階的に停止して参りました。
- 2023年8月 新規の Okta の契約において、テレフォニーサービス提供停止
- 2024年9月 Okta の契約更新において、テレフォニーサービス提供停止
セキュリティ強度の観点での位置づけの変化により、 SMS / 通話が認証方式として積極的に推奨されなくなっています。 SMS / 通話による認証のセキュリティに関する考え方については以下をご参照ください。
https://help.okta.com/oie/ja-jp/content/topics/telephony/about-telephony.htm
https://www.okta.com/jp/blog/2020/10/sms-authentication/
Okta ではより強力な認証方式の機能提供へ開発をシフトする方針から、 SMS / 通話による認証は引き続きサポートするものの、 SMS / 通話の実際の電話へのデリバリー部分はお客様にテレフォニープロバイダを契約するなどしてご用意いただく形としました。
Okta はテレフォニープロバイダとの接続ための「テレフォニーインラインフック」を代替として提供しております(下記リンク参照)。テレフォニーインラインフックにより、 SMS / 通話の認証のために生成した OTP を API を通じてテレフォニープロバイダからの発信が可能となりました。
インラインフックとは - ふたつの Webhook
SMS / 通話認証において、Okta では「テレフォニーインラインフック」という機能で外部のテレフォニープロバイダと連携する形となっています。Okta では、Okta 外の外部サービスとの連携のための機能として、「イベントフック」と「インラインフック」とがあります。どちらも一般的には Webhook と呼ばれる機能に相当します。
Webhook とは、特定のイベントが発生した際に、あらかじめ指定されたURLへ動的に HTTP リクエストを送信する仕組みのことを指します。多くの場合、この HTTP リクエストはREST API のエンドポイントに対して行われ、外部サービスとの連携や自動処理のトリガーとして機能します。たとえば、ユーザーの登録完了や注文の確定といったイベントを検知すると、 Webhook は即座に通知用の HTTP POST リクエストを送信し、他のシステムがその情報を受け取って処理を開始する、といった流れが一般的です。
Okta のイベントフックは一般的に言われる文字通りの Webhook であり、外部のAPIエンドポイントにリクエストを送信したら、その応答を待たずに次の処理を行う、いわゆるリクエスト投げっぱなしの動作です。一般的な Webhook では外部の API エンドポイントからの応答がエラーであった場合になんらかのエラーハンドリング処理を追加で実施することはありますが、Okta のイベントフックにはありません。
対してOkta のインラインフックはいわば「同期型のWebhook」と言えるものです。外部のAPIエンドポイントにリクエストを送信し、その応答の結果次第で次の処理を決定する、あるいは応答に含まれるデータを取り出し次の処理を行うといった動作となります。いわば外部サービスと同期的に動作する、外部サービスの処理結果が Okta の動作に介入するような形です。
Okta テレフォニーインラインフック不成功時のフォールバック
認証時に SMS / 通話をテレフォニープロバイダにリクエスト送信する、という目的においてはイベントフックでも良さそうではありますが、インラインフックの形をとっているのには理由があります。Okta テレフォニーインラインフックは、テレフォニープロバイダの処理の結果に応じた動作をします。
端的には、テレフォニープロバイダに対して SMS / 通話の発信の指示が不調に終わったとき、Okta インラインフックは代替の動作をします。 SMS / 通話の代替通信を Okta プラットフォーム自体が行う、というものです。HTTP ステータスコード 400 番台が返される、あるいはタイムアウトするなど、インラインフックとして不成功に終わると、当該のテレフォニープロバイダを経由せずに SMS /通話がユーザーに着信し、決まった定型文でワンタイムパスワードが提供される、というフォールバックの動作です。
ただしこれは、通常のユーザー認証に用いられるためのものではありません。Okta 管理者が SMS /通話の認証や登録を設定する過程で、設定ミスや設定未完了状態で SMS /通話の認証ができないまま Okta 管理コンソール自体からロックアウトされる、という事態に陥らないよう、いわゆる Breakglass バックドアの手段として提供されているものです。
このフォールバック動作の SMS /通話は、その用途に見合うだけのきわめて制限された実行レートが設定されており、通常のエンドユーザーの認証にかなうものではありません。設定においてはくれぐれもこのフォールバック動作に依拠することにならないようにする必要があります。
SMS / 通話による認証の流れ
エンドユーザー目線での実際の SMS / 通話による認証(所有要素)の動作について、整理します。
注意: SMS / 通話で用いられる電話番号は、Okta ユーザー属性の「携帯電話番号(mobilePhone)」や「有線電話番号(primaryPhone)」とは関係がありません。認証方式の1つである「電話」に電話番号を登録する必要があります。
エンドユーザーの事前準備:電話番号の登録
まず事前準備として、ユーザーは、SMS /通話が着信する自身の電話番号の登録を行います。その際に早速、電話番号確認のための OTP 受信が行われますが、このプロセスで本記事のテレフォニープロバイダ連携が使われます。
ログイン済みの Okta ダッシュボード画面右上のアカウントのメニューより、[設定] - [セキュリティ方式] - [電話 + セットアップ]から電話番号を登録します。
[SMS でコードを受信][通話でコードを受信]をクリックすると、即座に OTP を知らせるメッセージが着信します。
SMS / 通話で受け取ったOTPをあらためて入力することで、電話番号の登録が完了します(ここではSMSを選択した例)。SMSの場合は6桁、通話の場合は5桁の数字です。
電話 Authenticator の登録の完了です。ユーザー毎、電話 Authenticator、すなわち有効な電話番号はひとつまでです。
SMS / 通話による認証を実行
実際に SMS / 通話で認証をします。
管理者によって「電話 - SMS」「電話 - 音声」の少なくとも一方が認証方法として許可されたルールを有する認証ポリシーにアタッチしたアプリケーションにアクセスし、認証ウィジェットの[他の方法で検証する]をクリックします。認証方法の選択オプションが表示されます。
ここで電話の[選択]をクリックします。 SMS / 通話の認証を開始するウィジェットに移行します。
[SMS でコードを受信]、または[代わりに通話を受信する]のいずれかをクリックします。
SMS または通話にて OTP を知らせるメッセージが着信します。「コードを入力します」で受け取った6桁 (SMS) または5桁(通話)の OTP を入力すると SMS / 通話の認証となります。
認証ポリシーに応じて他の認証方式も行い、MFA を完了します。
テレフォニープロバイダ連携のための前提の整理
Okta Workflows のフロー構築の前提として、Okta から SMS / 通話の認証をテレフォニープロバイダからSMSや音声で発信させる仕組みを整えるために必要な技術情報の整理を行います。ここでは採用するテレフォニープロバイダを Twilio とします。
前述の通り、Okta の SMS / 通話の認証にあたってテレフォニーインラインフックが動作します。
(図中1〜3) Okta 上で SMS / 通話認証がユーザーによって開始されると、OTPが生成されます。OTP はSMSの場合6桁、通話の場合5桁です。Okta 管理コンソール上で設定されたテレフォニーインラインフックの機構が外部サービス「メッセージ中継・整形用ミドルウェア」を REST API を通じて呼び出し、OTP と認証ユーザーの電話番号を含む情報を送信します。
(図中4〜6) メッセージ中継・整形用ミドルウェアは Okta テレフォニーインラインフックから REST API を通じて OTP、電話番号を含む情報を受信します。それら情報をしかるべく整形し、あらためてテレフォニープロバイダに REST API 等を通じて OTP、電話番号を含むメッセージが渡されます。
(図中7〜10) テレフォニープロバイダ (Twilio) は REST API 等を通じて OTP、電話番号を含むメッセージを受信すると、電話番号を SMS / 通話の宛先とし、本文にOTPを含むメッセージとして発信します(通話においては、メッセージに従って生成された音声が自動再生されます)。ユーザーの電話-番号宛に SMS / 通話が着信し、ユーザーはテキストまたは通話の OTP を確認した上で、Okta の認証ウィジェットに入力します。
メッセージ中継・整形用ミドルウェアの役割
「メッセージ中継・整形用ミドルウェア」とはどのような役割で、なぜ必要なのでしょうか。テレフォニープロバイダがREST APIを通じたOTPメッセージの受信をするのに、Okta テレフォニーインラインフックから直接送信せず、一度メッセージ中継・整形用ミドルウェアを経由させるのは理由があります。
Okta テレフォニーインラインフックで提供されるデータフォーマットは以下のような JSON オブジェクトです。
対してテレフォニープロバイダは、例えば後述する Twilio の例では URL エンコードされた1行の文字列としての入力を受け付ける形となるなど、それぞれの対応形式が定義されています。Okta テレフォニーインラインフックからの出力、テレフォニープロバイダにおける入力のフォーマットの違いを埋めるための変換の処理を、両者を仲介しながら受け持つのがここでのメッセージ中継・整形用ミドルウェアの役割となります。
実行プラットフォームの検討
Okta インラインフックとテレフォニープロバイダを仲介しリクエストデータを整形する、という役割のメッセージ中継・整形用ミドルウェアが必要であることがわかりました。そのために備えるべき機能として以下があります。
- REST API エンドポイントを備え、リクエストを受信する
- JSON オブジェクトからデータを抽出し配置し必要に応じてエンコードなどする
- 外部の REST API エンドポイントに対しリクエストを送信する
これらを実現できるクラウド提供型のプラットフォームの例として以下のようなものが考えられます。
- Twilio Functions (Twilio )
- AWS Lambda (Amazon Web Service)
- Glitch (Glitch, Inc.)
- Okta Workflows
Twilio Functions は Twilio が提供する Node.js ベースのコード実行環境で、Twilio の SMS / 通話サービスの API と緊密に連携します。AWS Lambda は Amazon Web Services が提供する汎用的なサーバーレスコンピューティングサービスで、任意のアプリケーションロジックを高いスケーラビリティで実行できる、柔軟で拡張性の高いプラットフォームです。高い性能を求められる条件においては、この2者が適しています。
Glitch は、主にプロトタイピングや教育用途に特化したオンラインの開発環境で、Node.jsベースのアプリケーションをブラウザ上で手軽に作成・公開できます。一方で、スケーラビリティや耐障害性といったプロダクション向け要件には対応していません。商用利用や高トラフィック処理を前提としたシステムには不向きです。参考として、以下では Glitch を利用し Twilio をテレフォニープロバイダとして発信する例が紹介されています。
https://developer.okta.com/docs/guides/telephony-inline-hook/nodejs/main/
Okta Workflows の考慮点
本記事の対象である Okta Workflows をメッセージ中継・整形用ミドルウェアのプラットフォームとして利用する場合、とくに考慮すべきなのは秒間リクエストの上限からくるスケーラビリティです。以下リンク先によると、フロー実行において、フロー当たり1秒間に10 回の呼び出しの制限があり、超過すると HTTP ステータスコード 429 のエラーが返されます。
https://help.okta.com/wf/ja-jp/content/topics/workflows/workflows-system-limits.htm#Workflow
ここから、メッセージ中継・整形用ミドルウェアとして Okta Workflows のフローが安全に動作するには、認証の発生が秒間 10 を安全に下回るようなユースケースのみである、ということになります。
通常は Okta FastPass や FIDO2 / Webauthn などをおもな MFA の手段とする、加えて限定的な例外対応、あるいは限られた管理用途の二次的手段としての認証方式として、経過措置的に SMS / 通話の認証を維持する、といった形が考えられます。
ユースケース1:日本語 SMS のみの簡易なフロー
本項では、日本語かつ SMS に限定した簡易なフローを紹介します。日本国内の企業・組織の多くのケースはこのパターンでカバーされるものと想定されます。
サンプルフローを使用する
添付のサンプルフローをすぐに利用する場合には、以下の手順を完了してください。
Okta Workflows コンソール上
- telephonyTwilio Sms.folder ファイルをインポート
- Twilio API接続用の API Connector Connectionを作成
- Auth Type は Basicを選択
- Username に Twilio Account SID、Password に Twilio Auth Token を入力
- 「Telephony Inline Hook SMS Japanese Only - Twilio 」フロー内の情報を2箇所書き換え
- Compose (Text) 関数カード内の「”From”:」に続く電話番号をTwilio で確保している送信用電話番号に書き換え
- Post (API Connector) アクションカード内の「URL」内の「Acconts/ACXXXXXXXXX/Messages.json」の「ACXXXXXXXXX」の文字列を Twilio Account SID に書き換え
上記に必要な情報は、Twilio 管理コンソールのトップページ https://console.twilio.com/ で取得できます。送信用電話番号をまだ取得していない場合は Twilio 管理コンソール上で手続きをしてください。
- Compose (Text) アクションカード内のSMS本文を好みの文に適宜書き換え
- Post (API Connector) アクションカードのConnectionを定義したものに変更
- API Endpoint (On Demand) イベントカードの下部「</>」をクリックし、「Invoke URL」「Alias」「Client token (Secure with client tokenを選択した上で)」をメモしておく
Okta 管理コンソール上
- Workflow (ワークフロー) - Inline Hooks (インラインフック)とたどり、「Add Inline Hook (インラインフックを追加)」をクリック、「Telephony (TL:テレフォニー)」を選択
- Name(名前)は任意の名前、URL には上記の Invoke URL を入力
- 認証で「HTTP Header(HTTPヘッダー)」を選択、Authentication field(認証フィールド)に上記の Alias、Authentication Secret (認証シークレット)に上記の Client Token を入力、「Save (保存)」をクリック
実際の動作のために、Security (セキュリティ) - Authenticator で電話を追加し、Security (セキュリティ) - Authentication Policy (認証ポリシー) で、Okta Dashboard やその他対象となるアプリケーションに対する認証方法に「電話 - SMS」が含まれるようにしてください。
Okta Workflows フロー解説
フローの構成におけるポイントは以下です。
- API エンドポイントで Okta テレフォニーインラインフックのリクエストを受ける
- Twilio に発信要求する SMS 本文を構成し、その中にOTPの数字6桁を組み込む
- Twilio APIに向けてREST APIでリクエストを送信する
フローはAPI Endpoint (On Demand) イベントカードで開始し、Twilio へのSMS発信リクエストを含む一連の処理の後、Return Raw (Flow Control) アクションカードでAPIのアクセス元へ結果を返信しています。これにより、Okta テレフォニーインラインフックからのREST APIを通じたリクエストを受け、処理結果をOkta テレフォニーインラインフックに返信します。
別項で述べているようにインラインフックは処理結果の返信に待機しているので、返信の実装が必要となる上に、タイムアウトにかからないよう処理を完了することが大事です。Return Raw (Flow Control) アクションカードでは処理成功のみが定義されていますが、いずれかのアクションカードで処理エラーによる停止が起きると、API Endpoint (On Demand) イベントカードがアクセス元に HTTP 400 エラーを返すので、エラー発生時も Okta テレフォニーインラインフックは返信を受信することができます。ただしこの動作は前述の特殊なフォールバック動作であり、この動作に依存することがないように設定を行ってください。
注意:より厳密なエラーハンドリング処理は各自で適宜加えていただくようお願いします。
カード間の連携の関係を示したのが次の図です。
API Endpoint(On Demand)イベントカードによって用意された API エンドポイントに Okta テレフォーニーインラインフックからのリクエストで受信した際の Body は、以下のような JSON オブジェクトです。
data.messageProfileの階層に認証対象のユーザーの電話番号、OTPである数字6桁があるので、次のGet Multiple(Object)アクションカードでこれらの値を取り出します。
Compose (Text)関数カードでは、Twilio のフォーマットにあわせSMS送信に必要な要素を構成しています。これらの元となる Twilio の要件については事項の「 SMS / 通話発信を実現するための技術情報の収集 - テレフォニープロバイダ:Twilio API 情報の整理」を参照してください。取り出した電話番号と OTP を挿入します。
Encode Query (URL)関数カードでは、構成したものを1行の文字列で送るための URL エンコード処理を行っています。このはたらき・役割についても次項で説明しています。
{"From":"+11111111111","To":"+819088888888","Body":"あなたのワンタイムパスワードは338267です。"}
このような JSON オブジェクトが以下のように変換されます。
From=%2B11111111111&To=%2B819088888888&Body=%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AE%E3%83%AF%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AF338267%E3%81%A7%E3%81%99%E3%80%82
これにより、日本語のようなマルチバイトや各種の記号を含む内容を確実に1行の文字列として扱えるようになります。
Post (API Connector) アクションカードでは、実際に Twilio API に向けたSMSの送信のリクエストを行います。API エンドポイント、ヘッダに含めるべき要素はそれぞれ以下です。Twilio の API エンドポイントの特定については次項を参考にしてください。
https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json
{"content-type": "application/x-www-form-urlencoded"}
Body として URL エンコード済みのものを入れます。
Twilio API へのリクエストが完了したら、Return Raw(Flow Control) 関数カードでOkta テレフォニーインラインフックに向けて HTTP ステータスコード 200 を返します。ここでは簡略化のため、Twilio 側での処理の成否に関わらずに返していることに留意してください。エラーハンドリングは、ユースケース2のフローを参考にしてください。
ユースケース2:SMSと通話、多言語に対応
前項では、SMS のみで日本語のメッセージを送信する簡素なフローを紹介しました。この項では SMS および通話の双方、およびユーザーの言語に合わせてテキストまたは音声の言語を日本語、英語の切り替えを行う仕組みをもつフローについて解説します。
サンプルフローを使用する
添付のサンプルフローをすぐに利用する場合には、以下の手順を完了してください
Okta Workflows コンソール
- telephonyTwilio .folder ファイルをインポート
- Twilio API接続用のAPI Connector Connectionを作成
- Auth Type は Basic を選択
- Username に Twilio Account SID、Password に Twilio Auth Token を入力
- 主フロー「Telephony Inline Hook with Twilio 」内の情報を2箇所書き換え
- Get Multiple (Object)関数カード内の「”accountSid”:」に続く「ACXXXXXXXXX」の文字列を Twilio Account SID に、「”twilioFromNumber”:」に続く電話番号を Twilio で確保している電話番号に書き換え
上記に必要な情報は、Twilio 管理コンソールのトップページ https://console.twilio.com/ で取得できます。送信用電話番号をまだ取得していない場合はTwilio 管理コンソール上で手続きをしてください。
- Post (API Connector)アクションカードの Connection を、定義したものに変更
- API Endpoint (On Demand)イベントカードの下部「</>」をクリックし、「Invoke URL」「Alias」「Client token(Secure with client tokenを選択した上で)」をメモ
- Helperフロー「Helper:Twilio_SMS」「Helper:Twilio_Voice」それぞれで、If/Else (Branching)関数カード内の Compose (Text)内の SMS / 通話本文を好みの文に適宜書き換え
Okta 管理コンソール上
- Workflow (ワークフロー) - Inline Hooks (インラインフック)とたどり、「Add Inline Hook (インラインフックを追加)」をクリック、「Telephony (TL:テレフォニー)」を選択
- Name (名前)は任意の名前、URL には上記のInvoke URLを入力
- 認証で「HTTP Header(HTTPヘッダー)」を選択、Authentication field (認証フィールド)に上記の Alias、英語(認証シークレット)に上記の Client Token を入力、「Save (保存)」をクリック
実際の動作のために、Security (セキュリティ) - Authenticator で電話を追加し、Security(セキュリティ) - Authentication Policy (認証ポリシー) で、Okta Dashboard やその他対象となるアプリケーションに対する認証方法に「電話 - SMS」および「電話 - 音声」が含まれるようにしてください。
SMS / 通話発信を実現するための技術情報の収集 - テレフォニープロバイダ:Twilio API 情報の整理
サンプルフローの適用によって SMS /通話による認証は動作することができました。ここからは他のテレフォニープロバイダへの利用やより一般的な Okta Workflows でのミドルウェアの構築など、Okta Workflows のフローを一から構築し応用できるように、必要な情報の整理手順を述べます。
Twilio で SMS と通話の双方、日本語と、英語など日本語以外の言語との対応を想定して、技術情報についてあらためて調査してみます。
Twilio では SMS は「Messaging」であり、Twilio のMessagingのAPI に関するリファレンスは以下です。
https://www.twilio.com/docs/messaging
また、Twilio では通話は「Voice」であり、Twilio のVoiceのAPI に関するリファレンスは以下です。
https://www.twilio.com/docs/voice
まず今回のユースケースは生成されたOTPを含む文言をSMSまたは通話により送る、というシンプルなものであり、最も単純な送信機能を想定して上記のドキュメント上で情報を拾います。
Messaging (SMS)においては、「Create a Message resource」が該当します。
https://www.twilio.com/docs/messaging/api/message-resource#create-a-message-resource
APIの基本情報としては、以下が把握できます。
URL (エンドポイント) :
https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json
メソッド: POST
ヘッダ に含む要素: Encoding type:application/x-www-form-urlencoded
Body:
To string<phone-number>
From string<phone-number>
Body string
Voice (通話)においては、「Create a Call resource」が該当します。
https://www.twilio.com/docs/voice/api/call-resource#create-a-call-resource
APIの基本情報としては、以下が把握できます。
https://www.twilio.com/docs/voice/api/call-resource#code-create-a-call-resource-with-twiml
URL (エンドポイント) :
https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Calls.json
メソッド: POST
ヘッダ に含む要素: Encoding type:application/x-www-form-urlencoded
Body:
To string<phone-number>
From string<phone-number>
Twiml string<twiml>
TwiML(Twilio Markup Language)は、Twilio が通話やメッセージ処理に用いるXML形式の命令文です。
https://www.twilio.com/docs/voice/twiml
<Say> タグを使うことで、指定したテキストを音声合成に読み上げさせることができます。locale属性により、言語や発音(例:ja-JP)を制御できます。
https://www.twilio.com/docs/voice/twiml/say
以上の情報を元に、テレフォニープロバイダTwilio に対する API 要求を構成します。
ここでヘッダの要素「Encoding type:application/x-www-form-urlencoded」に着目すると、これが意味するところは以下です。
- メッセージボディは1行のURLエンコード文字列である
- URLエンコード(%xx)形式でエンコードされている
URL エンコードは HTML フォームでの標準に含まれるエンコード形式であり、たとえば「山田 太郎」は「%E5%B1%B1%E7%94%B0+%E5%A4%AA%E9%83%8E」のようになります。これにより、非 ASCII 文字(0x00〜0x7F以外)の、日本語などマルチバイト文字を送ることができるようになります。このエンコード処理は Okta Workflows 上で行います。
Okta テレフォニーインラインフックの要素の確認
次に、Okta 側において、 SMS / 通話の区別や言語の種類を特定するようなテレフォニーインラインフックのAPI出力について確認します。 SMS / 通話は、認証ウィジェットで「SMSコードを受信」と「代わりに通話を受信する」で切り替わります。言語は、認証ユーザーがOkta ダッシュボードで登録されてる表示言語で、デフォルトではブラウザの言語が指定され、ダッシュボードの設定で明示的に指定、変更することができます。
実際にユーザーが SMS / 通話の認証方式を開始し、Okta Workflows のExecution History等を利用して Okta テレフォニーインラインフックの送信内容を捕捉するなどして、送信された(Okta Workflows API エンドポイントが受信した) body 上の JSON オブジェクトの内容が以下です。ここでは日本語と英語の2言語とします。日本語/英語の切り替えは、Okta ダッシュボードのユーザーによる言語設定が反映されます。
認証方式=SMS、ユーザーの表示言語=日本語の例 (JSONは抜粋です)。
まとめると以下となります。
- SMS-日本語
- “data.messageProfile.deliveryChannel” : “SMS”
- “data.messageProfile.locale” : “ja”
- SMS-英語
- “data.messageProfile.deliveryChannel” : “SMS”
- “data.messageProfile.locale” : “en”
- 通話-日本語
- “data.messageProfile.deliveryChannel” : “voice call”
- “data.messageProfile.locale” : “ja”
- 通話-英語
- “data.messageProfile.deliveryChannel” : “voice call”
- “data.messageProfile.locale” : “en”
これらの要素が JSON オブジェクトの中に含まれることを前提として、Okta Workflows から Twilio に送信する内容を切り替えることとします。
Okta Workflows フローの概観
フローの構成におけるポイントは以下です。
- API エンドポイントで Okta テレフォニーインラインフックのリクエストを受ける。
- 認証が SMS か通話かによって、各々 Helperフローに分岐する。
- 各 Helper フローにて、認証対象ユーザーの言語設定が日本語かそれ以外の言語かによって、構成するSMSまたは通話の本文の言語を日本語か英語かに切り替える。
- Twilio に発信要求するSMSまたは通話の本文を構成し、その中に OTP の数字(SMS は6桁、通話は5桁)を組み込む。
- 通話においては、聞き取りやすい自然な音声生成を指示するための本文構成でTwiML を利用する。そのために必要な文字列操作を行う。
- Twilio API に向けて REST API でリクエストを送信する。
本件の Okta Workflows での処理は1つの主フローと2つのヘルパーフローで構成されています。以下が全体の概観です。
主フロー「Telephony Inline Hook with Twilio 」:Okta テレフォニーインラインフックから SMS / 通話のリクエストを受信し、認証方式を判定して適切なヘルパーフローへ処理を分岐します。ヘルパーフローが生成した Twilio 向けメッセージを整形し、Twilio へ送信。Twilio からの応答に基づき、Okta テレフォニーインラインフックに結果を返します。
ヘルパーフロー「Helper:Twilio_SMS」:リクエストの認証方式がSMSであった場合に呼び出されます。言語が日本語かそれ以外かによって、Okta が生成した OTP を含むテキストメッセージを日本語または英語で生成し、主フローに返します。
ヘルパーフロー「Helper:Twilio_Voice」:リクエストの認証方式が通話であった場合に呼び出されます。言語が日本語かそれ以外かによって、Okta が生成した OTP を含む音声メッセージを日本語または英語で生成し、主フローに返します。
それでは実際のフローの流れを順に追います。
主フロー「Telephony Inline Hook with Twilio 」
フローは API Endpoint (On Demand) イベントカードで開始します。 SMS / 通話 Authenticator を契機とした、Okta テレフォニーインラインフックからの REST API リクエストを受信し、その Body に JSON オブジェクトを処理します。
次の Get Multiple (Object) 関数カードでは、設定値として Twilio Account SID と Twilio で確保している発信用電話番号を与えています。
If/ElseIf (Branch) 関数カードは、この主フローの核となる部分です。ここで通信方式が SMS、通話それぞれで各ヘルパーフローを呼び出し、ヘルパーフローからの出力結果を下処理して続く関数カード群に渡します。
まず、Okta テレフォニーインラインフックのJSONオブジェクト内のキー deriveryChannel の値が「SMS」であった場合、ヘルパーフロー「Twilio_SMS」を呼び出します。ヘルパーフローへの入力はJSONオブジェクトからotpCode、userLocaleの値です。ヘルパーフローからは SMS の本文が body として返されます。Construct (Object) 関数カードでJSONオブジェクトからユーザーの電話番号、発信用電話番号とSMSの本文をTwilio へのリクエストボディとしてあらたな JSON オブジェクトに組み立てます。SMS 用の Twilio REST API エンドポイントである「Message.json」とともに出力します。
キー deriveryChannel の値が「voice call」であった場合、ヘルパーフロー「Twilio_Voice」を呼び出します。ヘルパーフローへの入力はSMSの場合と同様です。ヘルパーフローからはS通話で音声出力される文が body として返されます。Construct関数カードで JSON オブジェクトからユーザーの電話番号、発信用電話番号と通話の本文を Twilio へのリクエストボディとしてあらたなJSONオブジェクトに組み立てます。通話用の Twilio REST API エンドポイントである「Calls.json」とともに出力します。
念のため、キーderiveryChannelの値が上記にあてはまらなかった場合にはエラーメッセージを仮出力していますが、この発生は想定されないため、これ以降とくに処理は定義していません。
If/ElseIf (Branch) 関数カードからの、 SMS / 通話のリクエストボディを Encode Query (URL) 関数カードで URL エンコードします。Concatenate (Text) 関数カードで Twilio API のエンドポイントのフルパスを構成します。ここに、既定の設定値である Account SID が組み込まれます。Post (API Connector) アクションカードで、URL エンコード処理後のリクエストボディを、Twilio API に HTTP POST します。
Twilio API に SMS / 通話の HTTP POST リクエストを送信し、成功したレスポンスのボディには、キーstatus、値「queued」が格納されています。それをもとに If/Else (Branch) 関数カードで Okta テレフォニーインラインフックへの返り値を構成します。成功の場合にはステータスコード 200 、それ以外の場合にはステータスコード 400 とし、メッセージボディを含めて Return Raw 関数カードで送信しています。
Okta テレフォニーインラインフックはこのステータスコードを判断し、送信成功とするか、不成功として前述のような SMS / 通話のフォールバック処理に入るかの分岐を行います。
ヘルパーフロー「Helper:Twilio_SMS」 (SMS 本文作成)
Helper Flow (On Demand) イベントカードにおいて、主フローから oktaOptCode (ワンタイムパスワード数字 6桁)、userLocale (ユーザーの言語) が渡されます。
まず、 If/Else (Branch) 関数カードで userLocale の値が「ja」(日本語) かそれ以外かにより 条件分岐します。SMS の本文を Compose 関数カードで用意し、日本語の場合は日本語の文、それ以外の場合は英語の文を作ります。それぞれに oktaOtpCode を置き、messageBody として主フローに返します。
ヘルパーフロー「Helper:Twilio_Voice」 (通話本文作成)
この通話の本文をつくるためのヘルパーフローは、SMS のものに比べてやや複雑です。前述のように自動音声による読み上げをより自然に聞き取りやすくするために、 TwiML を用いた構造にするためです。
Helper Flow (On Demand) イベントカードで主フローから oktaOptCode (ワンタイムパスワード数字 5桁)、userLocale (ユーザーの言語) が渡されますが、まず最も重要なワンタイムパスワードの読み上げで一桁ずつ間隔を空ける TwiML の指示「<break time="0.2s"/>」を文字毎に挿入するため、Split 関数カードと List to Text 関数カードを用いたややトリッキーな処理をしています。ワンタイムパスワードの文字列をいったん1文字毎のリストにし、区切り文字として「<break time="0.2s"/>」を置いてあらためてリストを文字列に変換することで、例えば「12345」を「1<break time="0.2s"/>2<break time="0.2s"/>3<break time="0.2s"/>4<break time="0.2s"/>5」となります。
次に If/Else (Branch) 関数カードで userLocale の値が「ja」(日本語) かそれ以外かにより 条件分岐します。通話の読み上げ用の本文を Compose (Text) 関数カードで用意し、日本語の場合は日本語の文、それ以外の場合は英語の文を作ります。TwiML で修飾し、先に処理済みのワンタイムパスワードを配置、messageBody として主フローに返します。
サンプルフローのダウンロード
今回解説したフローは、こちらから Flow folder ファイルとしてダウンロードし、Okta Workflows にインポートすることで試すことができます。ただし、あくまでサンプルですので、動作の保証をするものではなく、フロー全体としての動作やロジックはサポート対象にはなりませんのでご了承ください。
最後に
Okta Workflows により Okta と Twilio との接続を可能にし、 SMS / 通話の認証を可能にする例をご紹介しました。ミドルウェアとしてサービス間を接続する用途で Okta Workflows を利用するユースケースとなるかと存じます。ただ、Okta Workflows は本文で述べたようにレート制限が厳しいのと同時に、処理の種類によっては大きな遅延を強いられることもあります。ミドルウェアとしてのユースケースについては、ぜひ Okta Japan にお問い合わせください。