適用対象: 従業員テナント緑
外部テナント (詳細)
この記事では、ワンタイム パスコード (OTP) 送信イベントの種類用にカスタム メール プロバイダーを構成および設定する方法について説明します。 このイベントは、OTP メールがアクティブになるとトリガーされます。これにより、REST API を呼び出して独自のメール プロバイダーを使用する REST API を呼び出すことができます。
ヒント
この機能を試すには、Woodgrove Groceries のデモにアクセスし、"Use a custom Email Provider for One Time code" (ワンタイム コード用のカスタム メール プロバイダーを使用する) のユース ケースを開始します。
前提条件
カスタム認証拡張機能で説明されている概念の知識と理解。
Azure サブスクリプション。 既存の Azure アカウントをお持ちでない場合は、 無料試用版 にサインアップするか、アカウントの作成時に Visual Studio サブスクリプション の特典 を使用してください。
Microsoft Entra ID 外部テナント。
メール リレー サービス プロバイダー:
- Azure Communication Services リソース。 お持ちでない場合は、「クイック スタート: 新規または既存のリソース グループを使用して Communication Services リソースを作成および管理 する」で作成します。
- 作成され、プロビジョニングされたドメインと共に準備が整った Azure Email Communication Services リソース。 お持ちでない場合は、「 クイック スタート: Email Communication Service リソースの作成と管理」を参照し、Azure Communication Services と同じリソース グループを使用します。
- メール ドメインに接続されているアクティブな Communication Services リソース。 「クイック スタート: 確認済みメール ドメインを接続する方法」を参照してください
- (省略可能) Azure Communication Services を使用して電子メールを送信 し、Azure Communication Services を使用して目的の受信者に電子メールを送信し、アプリケーションが電子メールを送信するための構成を確認します。
手順 1: Azure 関数アプリを作成する
このセクションでは、Azure portal で Azure 関数アプリを設定する方法について説明します。 関数 API は、メール プロバイダーへのゲートウェイです。 HTTP トリガー関数をホストする Azure 関数アプリを作成し、関数の設定を構成します。
ヒント
この記事の手順は、開始するポータルによって若干異なる場合があります。
Azure portal に、少なくとも アプリケーション管理者 および 認証管理者としてサインインします。
Azure portal のメニューまたは ホーム ページで、[ リソースの作成] を選択します。
関数アプリを検索して選択し、[作成] を選択します。
[ 関数アプリの作成 ] ページで、[ 従量課金プラン ] を選んで、[ 選択 ] をクリックします。
[ 関数アプリの作成 (従量課金)] ページの [ 基本 ] タブで、次の表に示すように設定を使用して関数アプリを作成します。
設定 提案された値 説明 予約 該当するサブスクリプション この新しい関数アプリが作成されるサブスクリプション。 リソース グループ myResourceGroup 前提条件の一部として 、Azure Communications Service リソースと 電子メール通信サービス リソースの設定に使用するリソース グループを選択します 関数アプリ名 グローバルに一意の名前 新しい関数アプリを識別する名前。 有効な文字は、 a-z
(大文字と小文字の区別をしない)、0-9
、および-
です。コードまたはコンテナー イメージをデプロイする コード コード ファイルまたは Docker コンテナーの発行オプション。 このチュートリアルでは、[ コード] を選択します。 ランタイム スタック 。網 好みのプログラミング言語。 このチュートリアルでは、 .NET を選択します。 バージョン 8 (LTS) インプロセス .NET ランタイムのバージョン。 インプロセスは、ポータルで関数の作成と変更ができることを意味します。このガイドでは、これが推奨されます 地域 優先リージョン 関数がアクセスできる近くのリージョンまたは他のサービスの近くにある リージョン を選択します。 オペレーティング システム ウィンドウズ オペレーティング システムは、ランタイム スタックの選択に基づいて、事前に自動的に選択されます。 [ 確認と作成 ] を選択してアプリ構成の選択を確認し、[ 作成] を選択します。 デプロイには、数分かかります。
デプロイが完了したら、[ リソースに移動 ] を選択して新しい関数アプリを表示します。
1.1 HTTP トリガー関数を作成する
Azure 関数アプリが作成されたら、HTTP トリガー関数を作成します。 HTTP トリガーでは、HTTP 要求で関数を呼び出すことができます。 Microsoft Entra カスタム認証拡張機能は、この HTTP トリガー関数にリンクしています。
- 関数アプリ内で、メニューから [関数] を選択します。
- [ 関数の作成] を選択します。
- [ 関数の作成 ] ウィンドウの [ テンプレートの選択] で、 HTTP トリガー テンプレートを検索して選択します。 [ 次へ] を選択します。
- [テンプレートの詳細] で、[関数名] プロパティに「CustomAuthenticationExtensionsAPI」と入力します。
- 承認レベルで、[関数] を選択します。
- 作成を選択します。
1.2 関数を編集する
コードは、受信した JSON オブジェクトの読み取りから始まります。 Microsoft Entra ID は 、JSON オブジェクト を API に送信します。 この例では、メール アドレス (識別子) と OTP を読み取ります。 次に、コードは通信サービスに詳細を送信し、 動的テンプレートを使用して電子メールを送信します。
このハウツー ガイドでは、Azure Communication Services と SendGrid を使用した OTP 送信イベントについて説明します。 タブを使用して実装を選択します。
メニューから、[ コード + テスト] を選択します。
コード全体を次のコード スニペットに置き換えます。
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>"; } } }
[ 関数 URL の取得] を選択し、 関数キー の URL をコピーします。この URL は、今後使用され、
{Function_Url}
と呼ばれます。 関数を完成させます。
手順 2: Azure 関数に接続文字列を追加する
接続文字列を使用すると、関数アプリはメール リレー サービスに接続して認証できます。 Azure Communication Services と SendGrid の両方で、これらの接続文字列を環境変数として Azure 関数アプリに追加します。
2.1: Azure Communication Services リソースから接続文字列とサービス エンドポイントを抽出する
Communication Services の接続文字列とサービス エンドポイントには、Azure portal から、または Azure Resource Manager API を使用してプログラムでアクセスできます。
Azure portal のホーム ページで、ポータル メニューを開き、[すべてのリソース] を検索して選択します。
この記事の前提条件の一部として作成された Azure Communications Service を検索して選択します。
左側のウィンドウで、[ 設定] ドロップダウンを選択し、[ キー] を選択します。
エンドポイントをコピーし、主キーからキーと接続文字列の値をコピーします。
2.2: Azure 関数に接続文字列を追加する
「Azure 関数アプリの作成」で作成した Azure 関数に戻ります。
関数アプリの [概要] ページで、左側のメニューで [設定] を選択>Environment 変数で次のアプリ設定を追加します。 すべての設定が追加されたら、[ 適用]、[ 確認] の順に選択します。
設定 値 (例) 説明 mail_connectionString https://ciamotpcommsrvc.unitedstates.communication.azure.com/:accesskey=
Azure Communication Services エンドポイント mail_sender from.email@myemailprovider.com メール送信元アドレス。 mail_subject アカウント確認コード メールの件名です。
手順 3: カスタム認証拡張機能を登録する
この手順では、Azure 関数を呼び出すために Microsoft Entra ID で使用される、カスタム認証拡張機能を構成します。 カスタム認証拡張機能には、REST API エンドポイントに関する情報、REST API から解析するクレーム、REST API に対する認証方法が含まれています。 Azure portal または Microsoft Graph を使用して、カスタム認証拡張機能を Azure 関数に認証するアプリケーションを登録します。
カスタム認証拡張機能を登録する
Azure portal に、少なくとも アプリケーション管理者 および 認証管理者としてサインインします。
Microsoft Entra ID を検索して選択し、[エンタープライズ アプリケーション] を選択します。
[ カスタム認証拡張機能] を選択し、[ カスタム拡張機能の作成] を選択します。
[基本] で、EmailOtpSend イベントの種類を選択し、[次へ] を選択します。
[ エンドポイント構成 ] タブで、次のプロパティを入力し、[ 次へ ] を選択して続行します。
- 名前 - カスタム認証拡張機能の名前。 たとえば、 電子メール OTP 送信です。
-
ターゲット URL - Azure 関数 URL の
{Function_Url}
。 Azure 関数アプリの [概要 ] ページに移動し、作成した関数を選択します。 [関数の 概要 ] ページで、[ 関数 URL の取得 ] を選択し、コピー アイコンを使用して customauthenticationextension_extension (システム キー) URL をコピーします。 - 説明 - カスタム認証拡張機能の説明。
[ API 認証 ] タブで、[ 新しいアプリ登録の作成 ] オプションを選択して、 関数アプリを表すアプリ登録を作成します。
アプリに Azure Functions 認証イベント API などの名前を付け、[ 次へ] を選択します。
[アプリケーション] タブ で 、カスタム認証拡張機能に関連付けるアプリケーションを選択します。 [ 次へ] を選択します。 チェック ボックスをオンにして、テナント全体に適用することもできます。 [次へ] を選択して続行します。
[ 確認 ] タブで、カスタム認証拡張機能の詳細が正しいことを確認します。 [API 認証] の下のアプリ ID をメモします。これは、Azure 関数アプリで Azure 関数の認証を構成するために必要です。 作成を選択します。
管理者の同意を与える
カスタム認証拡張機能が作成されたら、ポータルの [ アプリの登録 ] でアプリケーションを開き、 API のアクセス許可を選択します。
[API のアクセス許可] ページで、[YourTenant] ボタンに管理者の同意を付与して登録済みアプリに管理者の同意を与えます。これにより、カスタム認証拡張機能が API に対して認証できるようになります。 カスタム認証拡張機能は、client_credentials
を使い、Receive custom authentication extension HTTP requests
アクセス許可を使って Azure Function App に対する認証を行います。
アクセス許可を付与する方法を示すスクリーンショットを次に示します。
手順 4: テストに使用する OpenID Connect アプリを構成する
トークンを取得してカスタム認証拡張機能をテストするには、https://jwt.ms アプリを使用できます。 これは Microsoft 所有の Web アプリケーションであり、トークンのデコードされた内容を表示します (トークンの内容がお使いのブラウザーの外に出ることはありません)。
jwt.ms Web アプリケーションを登録するには、次 の手順に 従います。
4.1 テスト Web アプリケーションを登録する
- Microsoft Entra 管理センターに、少なくともアプリケーション管理者としてサインインします。
- Entra ID>App 登録に移動します。
- [ 新しい登録] を選択します。
- アプリケーションの 名前 を入力します。 たとえば、 My Test アプリケーションです。
- [ サポートされているアカウントの種類] で、[この組織のディレクトリ内のアカウントのみ] を選択します。
- [リダイレクト URI] の [プラットフォームの選択] ドロップダウンで [Web] を選択し、[URL] テキスト ボックスに「
https://jwt.ms
」と入力します。 - [ 登録 ] を選択してアプリの登録を完了します。
- アプリの登録の [ 概要] で、アプリケーション (クライアント) ID をコピーします。これは後で使用され、
{App_to_sendotp_ID}
と呼ばれます。 Microsoft Graph では、 appId プロパティによって参照されます。
次のスクリーンショットは、"マイ テスト アプリケーション" の登録方法を示したものです。
4.1 アプリケーション ID を取得する
アプリの登録の [ 概要] で、 アプリケーション (クライアント) ID をコピーします。 後の手順では、アプリ ID を {App_to_sendotp_ID}
として参照します。 Microsoft Graph では、 appId プロパティによって参照されます。
4.2 暗黙的なフローを有効にする
jwt.ms テスト アプリケーションでは、暗黙的なフローが使用されます。 "マイ テスト アプリケーション" の登録で暗黙的なフローを有効にします。
重要
Microsoft では、使用可能な最も安全な認証フローを使用することをお勧めします。 この手順のテストに使用される認証フローには、アプリケーションに対する高い信頼が必要であり、他のフローに存在しないリスクが伴います。 この方法は、運用アプリに対するユーザーの認証には使用しないでください (詳細を参照)。
- [ 管理] で [ 認証] を選択します。
- [ 暗黙的な許可とハイブリッド フロー] で、 ID トークン (暗黙的およびハイブリッド フローに使用) チェック ボックスをオンにします。
- [保存] を選択します。
手順 5: Azure 関数を保護する
Microsoft Entra カスタム認証拡張機能は、サーバー間フローを使って、HTTP の Authorization
ヘッダーで Azure 関数に送信されるアクセス トークンを取得します。 関数を Azure に発行するときは、特に運用環境では、Authorization ヘッダーで送信されるトークンを検証する必要があります。
Azure 関数を保護するには、次の手順に従って Microsoft Entra 認証を統合し、受信トークンを Azure Functions 認証イベント API アプリケーションの登録と検証します。
注
Azure 関数アプリが、カスタム認証拡張機能が登録されているテナントとは異なる Azure テナントでホストされている場合は、 OpenID Connect ID プロバイダー の手順に進んでください。
- Azure portal にサインインします。
- 前に発行した関数アプリに移動して選択します。
- 左側のメニューで [ 認証 ] を選択します。
- [ ID プロバイダーの追加] を選択します。
- ドロップダウン メニューから、ID プロバイダーとして Microsoft を選択します。
- [アプリの登録>App 登録の種類] で、このディレクトリの既存のアプリ登録を選択し、カスタム電子メール プロバイダーの登録時に以前に作成したAzure Functions 認証イベント API アプリの登録を選択します。
- アプリの クライアント シークレット の有効期限を追加します。
- [ 認証されていない要求] で、ID プロバイダーとして [HTTP 401 Unauthorized ] を選択します。
- [トークン ストア] オプションの選択を解除します。
- [ 追加] を選択して、Azure 関数に認証を追加します。
5.1 OpenID Connect ID プロバイダーの使用
Microsoft ID プロバイダーを構成した場合は、この手順をスキップします。 そうではなく、カスタム認証拡張機能が登録されているテナントとは異なるテナントで Azure 関数がホストされている場合は、次の手順のようにして関数を保護します。
Azure portal にサインインし、前に発行した関数アプリに移動して選択します。
左側のウィンドウで [ 認証 ] を選択します。
[ ID プロバイダーの追加] を選択します。
ID プロバイダーとして OpenID Connect を選択します。
Contoso Microsoft Entra ID などの名前を指定します。
[ メタデータ] エントリで、ドキュメント URL に次の URL を入力します。
{tenantId}
を実際の Microsoft Entra テナント ID に置き換え、{tenantname}
をテナントの名前 ("onmicrosoft.com" を除く) に置き換えます。https://{tenantname}.ciamlogin.com/{tenantId}/v2.0/.well-known/openid-configuration
[アプリの登録] で、前に作成した Azure Functions 認証イベント API アプリ登録のアプリケーション ID (クライアント ID) を入力します。
Microsoft Entra 管理センターで次の手順を実行します。
- 前に作成した Azure Functions 認証イベント API アプリの登録を選択します。
- [証明書とシークレット>クライアント シークレット>新しいクライアント シークレットを選択します。
- クライアント シークレットの説明を追加します。
- シークレットの有効期限を選択するか、カスタムの有効期間を指定します。
- 追加を選択します。
- クライアント アプリケーション コードで使用する シークレットの値 を記録します。 このページからの移動後は、このシークレットの値は "二度と表示されません"。
Azure 関数に戻り、アプリの 登録で クライアント シークレットを入力します。
[トークン ストア] オプションの選択を解除します。
OpenID Connect ID プロバイダーを追加するには、[ 追加] を選択します。
手順 6: アプリケーションをテストする
カスタム メール プロバイダーをテストするには、次の手順に従います。
新しいプライベート ブラウザーを開き、次の 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
{tenant-id}
は、テナント ID、テナント名、または検証済みドメイン名の 1 つに置き換えます。 たとえば、contoso.onmicrosoft.com
のようにします。{tenantname}
を、テナントの名前 ("onmicrosoft.com" を除く) に置き換えます。{App_to_sendotp_ID}
を My Test アプリケーション登録 ID に置き換えます。電子メール ワンタイム パスコード アカウントを使用してサインインしていることを確認します。 次に、[ コードの送信] を選択します。 登録されたメール アドレスに送信されるコードに、上記で登録したカスタム プロバイダーが使用されていることを確認します。
手順 7: Microsoft プロバイダーにフォールバックする
拡張機能 API 内でエラーが発生した場合、既定では、Entra ID は OTP をユーザーに送信しません。 代わりに、エラー時の動作を Microsoft プロバイダーにフォールバックするように設定できます。
これを有効にするには、次の要求を実行します。
{customListenerObjectId}
を、前に記録したカスタム認証リスナーの 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"
}
}
}
}