次の方法で共有


ワンタイム パスコード送信イベント用にカスタム メール プロバイダーを構成する (プレビュー)

適用対象: 灰色の X 記号がある白い円。 従業員テナント 白いチェック マーク記号がある緑の円。 外部テナント (詳細情報)

この記事では、ワンタイム パスコード (OTP) 送信イベントの種類用にカスタム メール プロバイダーを構成および設定する方法について説明します。 このイベントは、OTP メールがアクティブになるとトリガーされます。これにより、REST API を呼び出して独自のメール プロバイダーを使用する REST API を呼び出すことができます。

ヒント

今すぐ試す

この機能を試すには、Woodgrove Groceries のデモにアクセスし、"Use a custom Email Provider for One Time code" (ワンタイム コード用のカスタム メール プロバイダーを使用する) のユース ケースを開始します。

前提条件

手順 1: Azure 関数アプリを作成する

このセクションでは、Azure portal で Azure 関数アプリを設定する方法について説明します。 関数 API は、メール プロバイダーへのゲートウェイです。 HTTP トリガー関数をホストする Azure 関数アプリを作成し、関数の設定を構成します。

ヒント

この記事の手順は、開始するポータルによって若干異なる場合があります。

  1. アプリケーション管理者および認証管理者以上の権限で Azure portal にサインインします。

  2. Azure portal のメニューまたは [ホーム] ページから [リソースの作成] を選択します。

  3. [関数アプリ] を検索して選択し、[作成] を選択します。

  4. [関数アプリの作成] ページで [従量課金] を選択し、次に [選択] を選択します。

  5. [関数アプリの作成 (従量課金)] ページの [基本] タブで、次の表に示す設定を使用して関数アプリを作成します。

    設定 提案された値 説明
    サブスクリプション 該当するサブスクリプション この新しい関数アプリが作成されるサブスクリプション。
    リソース グループ myResourceGroup 前提条件の一部として、Azure Communications Service および Email Communication Service リソースの設定に使用するリソース グループを選択します
    関数アプリ名 グローバルに一意の名前 新しい関数アプリを識別する名前。 有効な文字は、a-z (大文字と小文字の区別をしない)、0-9、および -です。
    コードまたはコンテナー イメージのデプロイ コード コード ファイルまたは Docker コンテナーの発行オプション。 このチュートリアルでは、[コード] を選びます。
    ランタイム スタック .NET 好みのプログラミング言語。 このチュートリアルでは、[.NET] を選びます。
    Version 8 (LTS) インプロセス .NET ランタイムのバージョン。 インプロセスは、ポータルで関数の作成と変更ができることを意味します。このガイドでは、これが推奨されます
    リージョン 優先リージョン 自分の近く、または関数がアクセスできる他のサービスの近くのリージョンを選択します。
    オペレーティング システム Windows オペレーティング システムは、ランタイム スタックの選択に基づいて、事前に自動的に選択されます。
  6. [確認および作成] を選択してアプリ構成の選択を確認した後、[作成] を選択します。 デプロイには、数分かかります。

  7. デプロイされたら、[リソースに移動] を選択して、新しい関数アプリを確認します。

1.1 HTTP トリガー関数を作成する

Azure 関数アプリが作成されたら、HTTP トリガー関数を作成します。 HTTP トリガーでは、HTTP 要求で関数を呼び出すことができます。 この HTTP トリガーは、Microsoft Entra カスタム認証拡張機能によって参照されます。

  1. [関数アプリ] 内で、メニューから [関数] を選びます。
  2. [関数の作成] を選択します。
  3. [関数の作成] ウィンドウの [テンプレートの選択] で、[HTTP トリガー] テンプレートを検索して選択します。 [次へ] を選択します。
  4. [テンプレートの詳細] で、[関数名] プロパティに「CustomAuthenticationExtensionsAPI」と入力します。
  5. [認証レベル][関数] を選択します。
  6. [作成] を選択します

1.2 関数を編集する

コードは、受信した JSON オブジェクトの読み取りから始まります。 Microsoft Entra ID は、JSON オブジェクトを API に送信します。 この例では、メール アドレス (識別子) と OTP を読み取ります。 その後、コードによって通信サービスに詳細が送信され、ダイナミック テンプレートを使ったメールが送られます。

このハウツー ガイドでは、Azure Communication Services と SendGrid を使用した OTP 送信イベントについて説明します。 タブを使用して実装を選択します。

  1. メニューから [コードとテスト] を選択します。

  2. コード全体を次のコード スニペットに置き換えます。

    using System.Dynamic;
    using System.Text.Json;
    using System.Text.Json.Nodes;
    using System.Text.Json.Serialization;
    using Azure.Communication.Email;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Http.HttpResults;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Extensions.Logging;
    
    namespace Company.AuthEvents.OnOtpSend.CustomEmailACS
    {
        public class CustomEmailACS
        {
            private readonly ILogger<CustomEmailACS> _logger;
    
            public CustomEmailACS(ILogger<CustomEmailACS> logger)
            {
                _logger = logger;
            }
    
            [Function("OnOtpSend_CustomEmailACS")]
            public async Task<IActionResult> RunAsync([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req)
            {
                _logger.LogInformation("C# HTTP trigger function processed a request.");
    
                // Get the request body
                string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                JsonNode jsonPayload = JsonNode.Parse(requestBody)!;
    
                // Get OTP and mail to
                string emailTo = jsonPayload["data"]!["otpContext"]!["identifier"]!.ToString();
                string otp = jsonPayload["data"]!["otpContext"]!["onetimecode"]!.ToString();
    
                // Send email
                await SendEmailAsync(emailTo, otp);
    
                // Prepare response
                ResponseObject responseData = new ResponseObject("microsoft.graph.OnOtpSendResponseData");
                responseData.Data.Actions = new List<ResponseAction>() { new ResponseAction(
                    "microsoft.graph.OtpSend.continueWithDefaultBehavior") };
    
                return new OkObjectResult(responseData);
            }
    
            private async Task SendEmailAsync(string emailTo, string code)
            {
                // Get app settings
                var connectionString = Environment.GetEnvironmentVariable("mail_connectionString");
                var sender = Environment.GetEnvironmentVariable("mail_sender");
                var subject = Environment.GetEnvironmentVariable("mail_subject");
    
                try
                {
                    if (!string.IsNullOrEmpty(connectionString))
                    {
                        var emailClient = new EmailClient(connectionString);
                        var body = EmailTemplate.GenerateBody(code);
    
                        _logger.LogInformation($"Sending OTP to {emailTo}");
    
                        EmailSendOperation emailSendOperation = await emailClient.SendAsync(
                        Azure.WaitUntil.Started,
                        sender,
                        emailTo,
                        subject,
                        body);
                    }
                }
                catch (System.Exception ex)
                {
                    _logger.LogError(ex.Message);
                }
            }
        }
    
        public class ResponseObject
        {
            [JsonPropertyName("data")]
            public Data Data { get; set; }
    
            public ResponseObject(string dataType)
            {
                Data = new Data(dataType);
            }
        }
    
        public class Data
        {
            [JsonPropertyName("@odata.type")]
            public string DataType { get; set; }
            [JsonPropertyName("actions")]
            public List<ResponseAction> Actions { get; set; }
    
            public Data(string dataType)
            {
                DataType = dataType;
            }
        }
    
        public class ResponseAction
        {
            [JsonPropertyName("@odata.type")]
            public string DataType { get; set; }
    
            public ResponseAction(string dataType)
            {
                DataType = dataType;
            }
        }
    
        public class EmailTemplate
        {
            public static string GenerateBody(string oneTimeCode)
            {
                return @$"<html><body>
                <div style='background-color: #1F6402!important; padding: 15px'>
                    <table>
                    <tbody>
                        <tr>
                            <td colspan='2' style='padding: 0px;font-family: "Segoe UI Semibold", "Segoe UI Bold", "Segoe UI", "Helvetica Neue Medium", Arial, sans-serif;font-size: 17px;color: white;'>Woodgrove Groceries live demo</td>
                        </tr>
                        <tr>
                            <td colspan='2' style='padding: 15px 0px 0px;font-family: "Segoe UI Light", "Segoe UI", "Helvetica Neue Medium", Arial, sans-serif;font-size: 35px;color: white;'>Your Woodgrove verification code</td>
                        </tr>
                        <tr>
                            <td colspan='2' style='padding: 25px 0px 0px;font-family: "Segoe UI", Tahoma, Verdana, Arial, sans-serif;font-size: 14px;color: white;'> To access <span style='font-family: "Segoe UI Bold", "Segoe UI Semibold", "Segoe UI", "Helvetica Neue Medium", Arial, sans-serif; font-size: 14px; font-weight: bold; color: white;'>Woodgrove Groceries</span>'s app, please copy and enter the code below into the sign-up or sign-in page. This code is valid for 30 minutes. </td>
                        </tr>
                        <tr>
                            <td colspan='2' style='padding: 25px 0px 0px;font-family: "Segoe UI", Tahoma, Verdana, Arial, sans-serif;font-size: 14px;color: white;'>Your account verification code:</td>
                        </tr>
                        <tr>
                            <td style='padding: 0px;font-family: "Segoe UI Bold", "Segoe UI Semibold", "Segoe UI", "Helvetica Neue Medium", Arial, sans-serif;font-size: 25px;font-weight: bold;color: white;padding-top: 5px;'>
                            {oneTimeCode}</td>
                            <td rowspan='3' style='text-align: center;'>
                                <img src='https://woodgrovedemo.com/custom-email/shopping.png' style='border-radius: 50%; width: 100px'>
                            </td>
                        </tr>
                        <tr>
                            <td style='padding: 25px 0px 0px;font-family: "Segoe UI", Tahoma, Verdana, Arial, sans-serif;font-size: 14px;color: white;'> If you didn't request a code, you can ignore this email. </td>
                        </tr>
                        <tr>
                            <td style='padding: 25px 0px 0px;font-family: "Segoe UI", Tahoma, Verdana, Arial, sans-serif;font-size: 14px;color: white;'> Best regards, </td>
                        </tr>
                        <tr>
                            <td>
                                <img src='https://woodgrovedemo.com/Company-branding/headerlogo.png' height='20'>
                            </td>
                            <td style='font-family: "Segoe UI", Tahoma, Verdana, Arial, sans-serif;font-size: 14px;color: white; text-align: center;'>
                                <a href='https://woodgrovedemo.com/Privacy' style='color: white; text-decoration: none;'>Privacy Statement</a>
                            </td>
                        </tr>
                    </tbody>
                    </table>
                </div>
                </body></html>";
            }
        }
    }
    
  3. [関数の URL の取得] を選択して [関数キー] URL をコピーします。この URL は、今後 {Function_Url} として利用、参照されます。 関数を完成させます。

手順 2: Azure 関数に接続文字列を追加する

接続文字列を使用すると、Communication Services SDK から Azure への接続と認証を行うことができます。 Azure Communication Services と SendGrid の両方について、これらの接続文字列を環境変数として Azure 関数アプリに追加する必要があります。

2.1: Azure Communication Services リソースから接続文字列とサービス エンドポイントを抽出する

Communication Services の接続文字列とサービス エンドポイントには、Azure portal から、または Azure Resource Manager API を使用してプログラムでアクセスできます。

  1. Azure portal[ホーム] ページでポータル メニューを開き、[すべてのリソース] を検索して選択します。

  2. この記事の前提条件の一部として作成した Azure Communications Service を検索して選択します。

  3. 左側のペインで [設定] ドロップダウンを選択し、[キー] を選択します。

  4. [エンドポイント] をコピーし、[主キー] から [キー][接続文字列] の値をコピーします。

    エンドポイントとキーの場所を示す [Azure Communications Service のキー] ページのスクリーンショット。

2.2: Azure 関数に接続文字列を追加する

  1. Azure 関数アプリの作成」で作成した Azure 関数に戻ります。

  2. 関数アプリの [概要] ページの左側にあるメニューで [設定]>[環境変数] の順に選択し、次のアプリ設定を追加します。 すべての設定が追加されたら、[適用] を選択してから [確認] を押します。

    設定 値 (例) 説明
    mail_connectionString https://ciamotpcommsrvc.unitedstates.communication.azure.com/:accesskey=A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u Azure Communication Services エンドポイント
    mail_sender from.email@myemailprovider.com メール送信元アドレス。
    mail_subject CIAM デモ メールの件名です。

手順 3: カスタム認証拡張機能を登録する

この手順では、Azure 関数を呼び出すために Microsoft Entra ID で使用される、カスタム認証拡張機能を構成します。 カスタム認証拡張機能には、REST API エンドポイントに関する情報、REST API から解析するクレーム、REST API に対する認証方法が含まれています。 Azure portal または Microsoft Graph を使用して、カスタム認証拡張機能を Azure 関数に認証するアプリケーションを登録します。

カスタム認証拡張機能を登録する

  1. アプリケーション管理者および認証管理者以上の権限で Azure portal にサインインします。

  2. [Microsoft Entra ID] を検索して選択し、[エンタープライズ アプリケーション] を選択します。

  3. [カスタム認証拡張機能] を選択して、[カスタム拡張機能の作成] を選択します。

  4. [基本] で、イベントの種類として EmailOtpSend を選択し、[次へ] を選択します。

    メール OTP 送信イベントが強調表示されている Azure portal のスクリーンショット。

  5. [エンドポイント構成] タブで、次のプロパティを入力してから [次へ] を選択して続行します。

    • [名前] - カスタム認証拡張機能の名前。 たとえば、「Email OTP Send」などです。
    • [ターゲット URL] - Azure 関数の URL の {Function_Url}。 Azure 関数 アプリの [概要] ページに移動し、作成した関数を選択します。 関数の [概要] ページで、[関数の URL の取得] を選択し、コピー アイコンを使用して customauthenticationextension_extension (システム キー) URL をコピーします。
    • [説明] - カスタム認証拡張機能の説明。
  6. [API 認証] タブで [アプリの登録を新規作成する] オプションを選んで、"関数アプリ" を表すアプリ登録を作成します。

  7. アプリの名前 (例: Azure Functions 認証イベント API) を指定し、[次へ] を選択します。

  8. [アプリケーション] タブで、カスタム認証拡張機能に関連付けるアプリケーションを選択します。 [次へ] を選択します。 チェック ボックスをオンにして、テナント全体に適用することもできます。 [次へ] を選択して続行します。

  9. [確認] タブで、カスタム認証拡張機能の詳細が正しいことを確認します。 [API 認証] の下の [アプリ ID] をメモします。これは、Azure 関数アプリで Azure 関数の認証を構成するために必要です。 [作成] を選択します

カスタム認証拡張機能が作成されたら、ポータルの [アプリの登録] でアプリケーションを開き、[API アクセス許可] を選択します。

[API アクセス許可] ページ上の ["YourTenant" に管理者の同意を与えます] ボタンを選択し、登録済みアプリに管理者の同意を付与します。これにより、カスタム認証拡張機能は API に対する認証を行えるようになります。 カスタム認証拡張機能は、client_credentials を使い、Receive custom authentication extension HTTP requests アクセス許可を使って Azure Function App に対する認証を行います。

アクセス許可を付与する方法を示すスクリーンショットを次に示します。

管理者の同意を付与する方法を示す Azure portal のスクリーンショット。

手順 4: テストに使用する OpenID Connect アプリを構成する

トークンを取得してカスタム認証拡張機能をテストするには、https://jwt.ms アプリを使用できます。 これは Microsoft 所有の Web アプリケーションであり、トークンのデコードされた内容を表示します (トークンの内容がお使いのブラウザーの外に出ることはありません)。

次の手順のようにして、jwt.ms Web アプリケーションを登録します。

4.1 テスト Web アプリケーションを登録する

  1. アプリケーション管理者以上の権限で Microsoft Entra 管理センターにサインインします。
  2. [ID]>[アプリケーション]>[Application registrations] (アプリケーションの登録) を参照します。
  3. [新規登録] を選択します。
  4. アプリケーションの名前を入力します。 例: マイ テスト アプリケーション
  5. [サポートされているアカウントの種類] で、 [この組織のディレクトリ内のアカウントのみ] を選択します。
  6. [リダイレクト URI][プラットフォームの選択] ドロップダウンで [Web] を選び、[URL] テキスト ボックスに「https://jwt.ms」と入力します。
  7. [登録] を選択して、アプリの登録を完了します。
  8. アプリの登録の [概要] で、[アプリケーション (クライアント) ID] をコピーします。これは、今後 {App_to_sendotp_ID} として利用、参照されます。 Microsoft Graph では、appId プロパティによって参照されます。

次のスクリーンショットは、"マイ テスト アプリケーション" の登録方法を示したものです。

サポートされているアカウントの種類とリダイレクト URI を選ぶ方法を示すスクリーンショット。

4.1 アプリケーション ID を取得する

アプリの登録の [概要] で、[アプリケーション (クライアント) ID] をコピーします。 後の手順では、アプリ ID を {App_to_sendotp_ID} として参照します。 Microsoft Graph では、appId プロパティによって参照されます。

4.2 暗黙的なフローを有効にする

jwt.ms テスト アプリケーションでは、暗黙的なフローを使用します。 "マイ テスト アプリケーション" の登録で暗黙的なフローを有効にします。

重要

Microsoft では、使用可能な最も安全な認証フローを使用することをお勧めします。 この手順のテストに使用される認証フローでは、アプリケーションに非常に高い信頼が必要であり、他のフローには存在しないリスクが伴います。 この方法は、運用アプリに対するユーザーの認証には使用しないでください (詳細については、を参照してください)。

  1. [管理] で、 [認証] を選択します。
  2. [暗黙的な許可およびハイブリッド フロー] で、[ID トークン (暗黙的およびハイブリッド フローに使用)] チェックボックスをオンにします。
  3. [保存] を選択します。

手順 5: Azure 関数を保護する

Microsoft Entra カスタム認証拡張機能は、サーバー間フローを使って、HTTP の Authorization ヘッダーで Azure 関数に送信されるアクセス トークンを取得します。 関数を Azure に発行するときは、特に運用環境では、Authorization ヘッダーで送信されるトークンを検証する必要があります。

Azure 関数を保護するには、受信したトークンを Azure Functions 認証イベント API アプリケーション登録で検証するため、次の手順のようにして Azure AD 認証を統合します。

Note

Azure 関数アプリが、カスタム認証拡張機能が登録されているテナントとは異なる Azure テナントでホストされている場合は、「OpenID Connect ID プロバイダーの使用」ステップに進んでください。

  1. Azure portal にサインインします。
  2. 前に発行した関数アプリに移動して選択します。
  3. 左側のメニューで [認証] を選択します。
  4. [ID プロバイダーの追加] を選択します。
  5. ドロップダウン メニューから ID プロバイダーとして [Microsoft] を選択します。
  6. [アプリの登録] ->[アプリ登録の種類][このディレクトリ内の既存のアプリ登録を選択] を選び、カスタム メール プロバイダーの登録時に前に作成した "Azure Functions 認証イベント API" アプリ登録を選びます。
  7. アプリの [クライアント シークレット] の有効期限を追加します。
  8. [認証されていない要求] で、ID プロバイダーとして [HTTP 401 認可されていない] を選択します。
  9. [トークン ストア] オプションをオフにします。
  10. [追加] を選んで、Azure 関数に認証を追加します。

関数アプリに認証を追加する方法を示すスクリーンショット。

5.1 OpenID Connect ID プロバイダーの使用

Microsoft ID プロバイダーを構成した場合は、このステップをスキップします。 そうではなく、カスタム認証拡張機能が登録されているテナントとは異なるテナントで Azure 関数がホストされている場合は、次の手順のようにして関数を保護します。

  1. Azure portal にサインインしてから、前に発行した関数アプリに移動して選択します。

  2. 左側のペインにある [認証] を選択します。

  3. [ID プロバイダーの追加] を選択します。

  4. ID プロバイダーとして [OpenID Connect] を選びます。

  5. Contoso Microsoft Entra ID などの名前を指定します。

  6. [メタデータ エントリ] で、[ドキュメントの URL] に次の URL を入力します。 {tenantId} を実際の Microsoft Entra テナント ID に置き換え、{tenantname} をテナントの名前 ("onmicrosoft.com" を除く) に置き換えます。

    https://{tenantname}.ciamlogin.com/{tenantId}/v2.0/.well-known/openid-configuration
    
  7. [アプリの登録] で、前に作成した "Azure Functions 認証イベント API" アプリ登録のアプリケーション ID (クライアント ID) を入力します。

  8. Microsoft Entra 管理センターで次の手順を実行します。

    1. 前に作成した "Azure Functions 認証イベント API" アプリの登録を選択します。
    2. [証明書とシークレット]>[Client secrets]\(クライアント シークレット\)>[新しいクライアント シークレット] の順に選択します。
    3. クライアント シークレットの説明を追加します。
    4. シークレットの有効期限を選択するか、カスタムの有効期間を指定します。
    5. [追加] を選択します。
    6. クライアント アプリケーションのコードで使用できるように、シークレットの値を記録しておきます。 このページからの移動後は、このシークレットの値は "二度と表示されません"。
  9. Azure 関数に戻り、[アプリの登録][クライアント シークレット] を入力します。

  10. [トークン ストア] オプションをオフにします。

  11. [追加] を選んで、OpenID Connect ID プロバイダーを追加します。

手順 6: アプリケーションをテストする

カスタム メール プロバイダーをテストするには、次の手順に従います。

  1. 新しいプライベート ブラウザーを開き、次の URL に移動してサインインします。

    https://{tenantname}.ciamlogin.com/{tenant-id}/oauth2/v2.0/authorize?client_id={App_to_sendotp_ID}&response_type=id_token&redirect_uri=https://jwt.ms&scope=openid&state=12345&nonce=12345
    
  2. {tenant-id} は、テナント ID、テナント名、または検証済みドメイン名の 1 つに置き換えます。 たとえば、contoso.onmicrosoft.com のようにします。

  3. {tenantname} を、テナントの名前 ("onmicrosoft.com" を除く) に置き換えます。

  4. {App_to_sendotp_ID} は、マイ テスト アプリケーションの登録 ID に置き換えます。

  5. メールのワンタイム パスコード アカウントを使用してサインインしていることを確認します。 次に、[コードの送信] を選択します。 登録されたメール アドレスに送信されるコードに、上記で登録したカスタム プロバイダーが使用されていることを確認します。

手順 7: Microsoft プロバイダーにフォールバックする

拡張機能 API 内でエラーが発生した場合、既定では Entra ID はユーザーに OTP を送信しません。 代わりに、エラー時の動作を Microsoft プロバイダーにフォールバックするように設定できます。

これを有効にするには、次の要求を実行します。 {customListenerOjectId} を、前に記録したカスタム認証リスナーの ID に置き換えます。

  • EventListener.ReadWrite.All の委任されたアクセス許可が必要です。
PATCH https://graph.microsoft.com/beta/identity/authenticationEventListeners/{customListenerOjectId}

{
    "@odata.type": "#microsoft.graph.onEmailOtpSendListener",
    "handler": {
        "@odata.type": "#microsoft.graph.onOtpSendCustomExtensionHandler",
        "configuration": {
            "behaviorOnError": {
                "@odata.type": "#microsoft.graph.fallbackToMicrosoftProviderOnError"
            }
        }
    }
}

関連項目