チュートリアル:Azure Functions を使用した Azure SignalR Service 認証

このステップ バイ ステップ チュートリアルでは、次のテクノロジを使用して、認証とプライベート メッセージングでチャット ルームを構築します。

  • Azure Functions: ユーザーを認証してチャット メッセージを送信するためのバックエンド API。
  • Azure SignalR Service: 接続されたチャット クライアントに新しいメッセージをブロードキャストするためのサービス。
  • Azure Storage: Azure Functions に必要なストレージ サービス。
  • Azure App Service: ユーザー認証を提供するサービス。

Note

この記事に記載しているコードは、GitHub から取得できます。

前提条件

問題がある場合は、 ぜひご意見をお寄せください。

Azure で重要なリソースを作成する

Azure SignalR Service リソースを作成する

アプリケーションは、Azure SignalR Service インスタンスにアクセスします。 Azure portal を用して Azure SignalR Service インスタンスを作成するには、次の手順に従います。

  1. Azure portal で、[リソースの作成] (+) ボタンを選択します。

  2. SignalR Service を探して選択します。

  3. [作成] を選択します

  4. 次の情報を入力します。

    名前
    リソース グループ 一意の名前を持つ新しいリソース グループを作成します。
    リソース名 Azure SignalR Service インスタンスの一意の名前を入力します。
    リージョン 近くのリージョンを選択します。
    価格レベル [無料] を選択します。
    サービス モード [サーバーレス] を選択します。
  5. [確認および作成] を選択します。

  6. [作成] を選択します

問題がある場合は、 ぜひご意見をお寄せください。

Azure Function App と Azure Storage アカウントを作成する

  1. Azure portal のホーム ページで、[リソースの作成] (+) を選択します。

  2. 関数アプリ」を検索し、これを選択します。

  3. [作成] を選択します

  4. 次の情報を入力します。

    名前
    リソース グループ Azure SignalR Service のインスタンスで同じリソース グループを使用します。
    関数アプリ名 関数アプリ インスタンスの一意の名前を入力します。
    ランタイム スタック [Node.js] を選択します。
    リージョン 近くのリージョンを選択します。
  5. 既定では、関数アプリと共に、同じリソース グループに新しい Azure ストレージ アカウントが作成されます。 関数アプリで別のストレージ アカウントを使用する場合は、[ホスティング] タブに切り替えてアカウントを選択します。

  6. [確認と作成][作成] の順に選択します。

Azure Functions プロジェクトをローカルに作成する

関数アプリを初期化する

  1. コマンド ラインから、プロジェクトのルート フォルダーを作成し、フォルダーに変更します。

  2. ターミナルで次のコマンドを実行して、新しい JavaScript Functions プロジェクトを作成します。

func init --worker-runtime node --language javascript --name my-app --model V4

既定では、生成されたプロジェクトには、SignalR 拡張機能を含む拡張機能バンドルを含んだ host.json ファイルが含まれています。 拡張機能バンドルの詳細については、「Azure Functions バインド拡張機能を登録する」を参照してください。

アプリケーション設定の構成

Azure Functions ランタイムをローカルで実行してデバッグすると、関数アプリは local.settings.json からアプリケーション設定読み取ります。 以前に作成した Azure SignalR Service のインスタンスとストレージ アカウントの接続文字列でこのファイルを更新します。

local.settings.json の内容を次のコードに置き換えます。

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "AzureWebJobsStorage": "<your-storage-account-connection-string>",
    "AzureSignalRConnectionString": "<your-Azure-SignalR-connection-string>"
  }
}

上のコードでは以下の操作が行われます。

  • AzureSignalRConnectionString 設定に Azure SignalR Service の接続文字列を入力します。

    文字列を取得するには、Azure portal で Azure SignalR Service インスタンスに移動します。 [設定] セクションで、[キー] 設定を見つけます。 接続文字列の右側にある [コピー] ボタンを選択して、クリップボードにコピーします。 プライマリまたはセカンダリの接続文字列を使用できます。

  • AzureWebJobsStorage 設定にストレージ アカウントの接続文字列を入力します。

    文字列を取得するには、Azure portal でストレージ アカウントに移動します。 [セキュリティ + ネットワーク] セクションで、[アクセス キー] 設定を見つけます。 接続文字列の右側にある [コピー] ボタンを選択して、クリップボードにコピーします。 プライマリまたはセカンダリの接続文字列を使用できます。

問題がある場合は、 お知らせください。

Azure SignalR Service に対してユーザーを認証する関数を作成する

チャット アプリをブラウザーで初めて開くと、Azure SignalR Service に接続できる有効な接続資格情報が要求されます。 関数アプリで negotiate という名前の HTTP トリガー関数を作成して、この接続情報を返します。

Note

SignalR クライアントには /negotiate で終わるエンドポイントが必要であるため、この関数には negotiate という名前を付ける必要があります。

  1. ルート プロジェクト フォルダーから、次のコマンドを使用して組み込みのテンプレートから negotiate 関数を作成します。

    func new --template "HTTP trigger" --name negotiate
    
  2. src/functions/negotiate.js を開き、コンテンツを次のように更新します。

    const { app, input } = require('@azure/functions');
    
    const inputSignalR = input.generic({
        type: 'signalRConnectionInfo',
        name: 'connectionInfo',
        hubName: 'default',
        connectionStringSetting: 'AzureSignalRConnectionString',
    });
    
    app.post('negotiate', {
        authLevel: 'anonymous',
        handler: (request, context) => {
            return { body: JSON.stringify(context.extraInputs.get(inputSignalR)) }
        },
        route: 'negotiate',
        extraInputs: [inputSignalR],
    });
    

    この関数には、SignalR クライアントからの要求を受信するための HTTP トリガー バインドが含まれています。 この関数には、クライアントが default という名前の Azure SignalR Service ハブに接続するための有効な資格情報を生成する SignalR 入力バインドも含まれています。

    この関数は、入力バインドから SignalR の接続情報を受け取り、それを HTTP 応答の本文でクライアントに返します。

    ローカル開発用の signalRConnectionInfo バインディングには、userId プロパティがありません。 後で追加して、関数アプリを Azure にデプロイするときに SignalR 接続のユーザー名を設定します。

問題がある場合は、 ぜひご意見をお寄せください。

チャット メッセージを送信する関数を作成する

また、チャット メッセージを送信するには、Web アプリに HTTP API が必要です。 Azure SignalR Service を使用するすべての接続済みクライアントにメッセージを送信する HTTP トリガー関数を作成します。

  1. ルート プロジェクト フォルダーから、次のコマンドを使用して、テンプレートから sendMessage という名前の HTTP トリガー関数を作成します。

    func new --name sendMessage --template "Http trigger"
    
  2. src/functions/sendMessage.js ファイルを開き、コンテンツを次のように更新します。

    const { app, output } = require('@azure/functions');
    
    const signalR = output.generic({
        type: 'signalR',
        name: 'signalR',
        hubName: 'default',
        connectionStringSetting: 'AzureSignalRConnectionString',
    });
    
    app.http('messages', {
        methods: ['POST'],
        authLevel: 'anonymous',
        extraOutputs: [signalR],
        handler: async (request, context) => {
            const message = await request.json();
            message.sender = request.headers && request.headers.get('x-ms-client-principal-name') || '';
    
            let recipientUserId = '';
            if (message.recipient) {
                recipientUserId = message.recipient;
                message.isPrivate = true;
            }
            context.extraOutputs.set(signalR,
                {
                    'userId': recipientUserId,
                    'target': 'newMessage',
                    'arguments': [message]
                });
        }
    });
    

    この関数には、HTTP トリガーと SignalR 出力バインドが含まれています。 HTTP 要求から本文を取り出し、Azure SignalR Service に接続されているクライアントに送信します。 各クライアントで newMessage という名前の関数を呼び出します。

    この関数は送信元の ID を読み取ることができます。また、メッセージ本文の recipient 値を受け取り、1 人のユーザーに限定してメッセージを送信することができます。 これらの機能はこのチュートリアルの後半で使用します。

  3. ファイルを保存します。

問題がある場合は、 お知らせください。

チャット クライアントの Web ユーザー インターフェイスをホストする

チャット アプリケーションの UI は、ASP.NET Core SignalR JavaScript クライアントを使用して Vue JavaScript フレームワークで作成された単純なシングル ページ アプリケーション (SPA) です。 わかりやすくするために、関数アプリで Web ページをホストしています。 運用環境では、Static Web Apps を使用して Web ページをホストできます。

  1. 関数プロジェクトのルート ディレクトリに index.html という名前のファイルを作成します。

  2. index.html の内容をコピーしてファイルに貼り付けます。 ファイルを保存します。

  3. ルート プロジェクト フォルダーから、次のコマンドを使用して、テンプレートから index という名前の HTTP トリガー関数を作成します。

    func new --name index --template "Http trigger"
    
  4. src/functions/index.js のコンテンツを次のコードに変更します。

    const { app } = require('@azure/functions');
    const { readFile } = require('fs/promises');
    
    app.http('index', {
        methods: ['GET'],
        authLevel: 'anonymous',
        handler: async (context) => {
            const content = await readFile('index.html', 'utf8', (err, data) => {
                if (err) {
                    context.err(err)
                    return
                }
            });
    
            return {
                status: 200,
                headers: {
                    'Content-Type': 'text/html'
                },
                body: content,
            };
        }
    });
    

    この関数は静的 Web ページを読み取って、ユーザーに返します。

  5. アプリをローカルでテストします。 次のコマンドを使用して関数アプリを起動します。

    func start
    
  6. Web ブラウザーで http://localhost:7071/api/index を開きます。 チャット Web ページが表示されます。

    ローカル チャット クライアントの Web ユーザー インターフェイスのスクリーンショット。

  7. チャット ボックスにメッセージを入力します。

    Enter キーを選択すると、Web ページにメッセージが表示されます。 SignalR クライアントのユーザー名が設定されていないため、すべてのメッセージを匿名で送信しています。

問題がある場合は、 ぜひご意見をお寄せください。

Azure をデプロイして認証を有効にする

これまではローカルで関数アプリとチャット アプリを実行してきました。 次に、それらを Azure にデプロイし、認証とプライベート メッセージングを有効にします。

認証のために関数アプリを構成する

これまで、チャット アプリは匿名で動作しています。 Azure では、App Service 認証を使用してユーザーを認証します。 認証済みユーザーのユーザー ID またはユーザー名を SignalRConnectionInfo バインドに渡して、ユーザーとして認証される接続情報を生成します。

  1. src/functions/negotiate.js ファイルを開きます。

  2. {headers.x-ms-client-principal-name} を使用して、userId プロパティを inputSignalR バインドに挿入します。 この値は、SignalR クライアントのユーザー名を、認証されたユーザーの名前に設定するバインド式です。 バインドは次の例のようになります。

    const inputSignalR = input.generic({
        type: 'signalRConnectionInfo',
        name: 'connectionInfo',
        hubName: 'default',
        connectionStringSetting: 'AzureSignalRConnectionString',
        userId: '{headers.x-ms-client-principal-name}'
    });
    
  3. ファイルを保存します。

関数アプリを Azure にデプロイする

以下のコマンドを使用して関数アプリを Azure にデプロイします。

func azure functionapp publish <your-function-app-name> --publish-local-settings

--publish-local-settings オプションは、local.settings.json ファイルから Azure にローカル設定が発行されるため、Azure で再度構成する必要はありません。

App Service 認証を有効にする

Azure Functions では、Microsoft Entra ID、Facebook、Twitter、Microsoft アカウント、Google を使用した認証がサポートされています。 このチュートリアルでは、ID プロバイダーとして Microsoft を使用します。

  1. Azure portal で、関数アプリのリソース ページに移動します。

  2. [設定]>[認証] の順に選択します。

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

    関数アプリの [認証] ページと ID プロバイダーを追加するボタンのスクリーンショット。

  4. ID プロバイダー の一覧で、Microsoft を選択します。 その後、追加を選択します。

    ID プロバイダーを追加するページのスクリーンショット。

完了した設定により、ID プロバイダーを関数アプリに関連付けるアプリ登録が作成されます。

サポートされている ID プロバイダーの詳細については、次の記事を参照してください。

アプリケーションを試す

  1. https://<YOUR-FUNCTION-APP-NAME>.azurewebsites.net/api/index を開きます。
  2. [ログイン] を選択して、選択した認証プロバイダーの認証を受けます。
  3. パブリック メッセージを送信するには、メインのチャット ボックスに入力します。
  4. プライベート メッセージを送信するには、チャット履歴のユーザー名を選択します。 選択した受信者のみがこれらのメッセージを受け取ります。

認証されたオンライン クライアント チャット アプリのスクリーンショット。

お疲れさまでした。 リアルタイムのサーバーレス チャット アプリをデプロイしました。

問題がある場合は、 ぜひご意見をお寄せください。

リソースをクリーンアップする

このチュートリアルで作成したリソースをクリーンアップするには、Azure portal を使用してリソース グループを削除します。

注意事項

リソース グループを削除すると、そのグループに含まれるすべてのリソースが削除されます。 リソース グループにこのチュートリアルの範囲外のリソースが含まれている場合は、それらも削除されます。

問題がある場合は、 お知らせください。

次のステップ

このチュートリアルでは、Azure SignalR Service で Azure Functions を使用する方法について説明しました。 詳細については、Azure Functions に Azure SignalR Service バインドを使用してリアルタイムのサーバーレス アプリケーションをビルドする方法を参照してください。

問題がある場合は、 ぜひご意見をお寄せください。