チュートリアル:Azure Functions を使用した Azure SignalR Service 認証
このステップ バイ ステップ チュートリアルでは、次のテクノロジを使用して、認証とプライベート メッセージングでチャット ルームを構築します。
- Azure Functions: ユーザーを認証してチャット メッセージを送信するためのバックエンド API。
- Azure SignalR Service: 接続されたチャット クライアントに新しいメッセージをブロードキャストするためのサービス。
- Azure Storage: Azure Functions に必要なストレージ サービス。
- Azure App Service: ユーザー認証を提供するサービス。
重要
この記事に示す生の接続文字列は、デモンストレーションのみを目的としています。
接続文字列には、アプリケーションが Azure Web PubSub サービスにアクセスするために必要な認可情報が含まれています。 接続文字列内のアクセス キーは、サービスのルート パスワードに似ています。 運用環境では、常にアクセス キーを保護してください。 Azure Key Vault を使用してキーの管理とローテーションを安全に行い、Microsoft Entra ID を使用して接続文字列をセキュリティで保護し、Microsoft Entra ID を使用してアクセスを認可します。
アクセス キーを他のユーザーに配布したり、ハードコーディングしたり、他のユーザーがアクセスできるプレーンテキストで保存したりしないでください。 キーが侵害された可能性があると思われる場合は、キーをローテーションしてください。
前提条件
- アクティブなサブスクリプションが含まれる Azure アカウント。 持っていない場合は、無料で作成できます。
- Node.js (バージョン 20.x)。
- Azure Functions Core Tools (バージョン 4)。
Azure で重要なリソースを作成する
Azure SignalR Service リソースを作成する
アプリケーションは、Azure SignalR Service インスタンスにアクセスします。 Azure portal を用して Azure SignalR Service インスタンスを作成するには、次の手順に従います。
Azure portal で、[リソースの作成] (+) ボタンを選択します。
SignalR Service を探して選択します。
[作成] を選択します
次の情報を入力します。
名前 値 リソース グループ 一意の名前を持つ新しいリソース グループを作成します。 リソース名 Azure SignalR Service インスタンスの一意の名前を入力します。 リージョン 近くのリージョンを選択します。 価格レベル [無料] を選択します。 サービス モード [サーバーレス] を選択します。 [確認および作成] を選択します。
[作成] を選択します
Azure Function App と Azure Storage アカウントを作成する
Azure portal のホーム ページで、[リソースの作成] (+) を選択します。
「関数アプリ」を検索し、これを選択します。
[作成] を選択します
次の情報を入力します。
名前 値 リソース グループ Azure SignalR Service のインスタンスで同じリソース グループを使用します。 関数アプリ名 関数アプリ インスタンスの一意の名前を入力します。 ランタイム スタック [Node.js] を選択します。 リージョン 近くのリージョンを選択します。 既定では、関数アプリと共に、同じリソース グループに新しい Azure ストレージ アカウントが作成されます。 関数アプリで別のストレージ アカウントを使用する場合は、[ホスティング] タブに切り替えてアカウントを選択します。
[確認と作成]、[作成] の順に選択します。
Azure Functions プロジェクトをローカルに作成する
関数アプリを初期化する
コマンド ラインから、プロジェクトのルート フォルダーを作成し、フォルダーに変更します。
ターミナルで次のコマンドを実行して、新しい JavaScript Functions プロジェクトを作成します。
既定では、生成されたプロジェクトには、SignalR 拡張機能を含む拡張機能バンドルを含んだ host.json ファイルが含まれています。 拡張機能バンドルの詳細については、「Azure Functions バインド拡張機能を登録する」を参照してください。
アプリケーション設定の構成
Azure Functions ランタイムをローカルで実行してデバッグすると、関数アプリは local.settings.json からアプリケーション設定読み取ります。 以前に作成した Azure SignalR Service のインスタンスとストレージ アカウントの接続文字列でこのファイルを更新します。
この記事では、デモ目的でのみ生の接続文字列が表示されます。 運用環境では、常にアクセス キーを保護してください。 Azure Key Vault を使用してキーの管理とローテーションを安全に行い、Microsoft Entra ID を使用して接続文字列をセキュリティで保護し、Microsoft Entra ID を使用してアクセスを認可します。
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
という名前を付ける必要があります。
ルート プロジェクト フォルダーから、次のコマンドを使用して組み込みのテンプレートから
negotiate
関数を作成します。func new --template "HTTP trigger" --name negotiate
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 トリガー関数を作成します。
ルート プロジェクト フォルダーから、次のコマンドを使用して、テンプレートから
sendMessage
という名前の HTTP トリガー関数を作成します。func new --name sendMessage --template "Http trigger"
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 人のユーザーに限定してメッセージを送信することができます。 これらの機能はこのチュートリアルの後半で使用します。ファイルを保存します。
チャット クライアントの Web ユーザー インターフェイスをホストする
チャット アプリケーションの UI は、ASP.NET Core SignalR JavaScript クライアントを使用して Vue JavaScript フレームワークで作成された単純なシングル ページ アプリケーション (SPA) です。 わかりやすくするために、関数アプリで Web ページをホストしています。 運用環境では、Static Web Apps を使用して Web ページをホストできます。
関数プロジェクトのルート ディレクトリに index.html という名前のファイルを作成します。
index.html の内容をコピーしてファイルに貼り付けます。 ファイルを保存します。
ルート プロジェクト フォルダーから、次のコマンドを使用して、テンプレートから
index
という名前の HTTP トリガー関数を作成します。func new --name index --template "Http trigger"
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 ページを読み取って、ユーザーに返します。
アプリをローカルでテストします。 次のコマンドを使用して関数アプリを起動します。
func start
Web ブラウザーで
http://localhost:7071/api/index
を開きます。 チャット Web ページが表示されます。チャット ボックスにメッセージを入力します。
Enter キーを選択すると、Web ページにメッセージが表示されます。 SignalR クライアントのユーザー名が設定されていないため、すべてのメッセージを匿名で送信しています。
Azure をデプロイして認証を有効にする
これまではローカルで関数アプリとチャット アプリを実行してきました。 次に、それらを Azure にデプロイし、認証とプライベート メッセージングを有効にします。
認証のために関数アプリを構成する
これまで、チャット アプリは匿名で動作しています。 Azure では、App Service 認証を使用してユーザーを認証します。 認証済みユーザーのユーザー ID またはユーザー名を SignalRConnectionInfo
バインドに渡して、ユーザーとして認証される接続情報を生成します。
src/functions/negotiate.js ファイルを開きます。
値
{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}' });
ファイルを保存します。
関数アプリを 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、X、Microsoft アカウント、Google を使用した認証がサポートされています。 このチュートリアルでは、ID プロバイダーとして Microsoft を使用します。
Azure portal で、関数アプリのリソース ページに移動します。
[設定]>[認証] の順に選択します。
[ID プロバイダーの追加] を選択します。
ID プロバイダー の一覧で、Microsoft を選択します。 その後、追加を選択します。
完了した設定により、ID プロバイダーを関数アプリに関連付けるアプリ登録が作成されます。
サポートされている ID プロバイダーの詳細については、次の記事を参照してください。
アプリケーションを試す
https://<YOUR-FUNCTION-APP-NAME>.azurewebsites.net/api/index
を開きます。- [ログイン] を選択して、選択した認証プロバイダーの認証を受けます。
- パブリック メッセージを送信するには、メインのチャット ボックスに入力します。
- プライベート メッセージを送信するには、チャット履歴のユーザー名を選択します。 選択した受信者のみがこれらのメッセージを受け取ります。
お疲れさまでした。 リアルタイムのサーバーレス チャット アプリをデプロイしました。
リソースをクリーンアップする
このチュートリアルで作成したリソースをクリーンアップするには、Azure portal を使用してリソース グループを削除します。
注意事項
リソース グループを削除すると、そのグループに含まれるすべてのリソースが削除されます。 リソース グループにこのチュートリアルの範囲外のリソースが含まれている場合は、それらも削除されます。
次のステップ
このチュートリアルでは、Azure SignalR Service で Azure Functions を使用する方法について説明しました。 詳細については、Azure Functions に Azure SignalR Service バインドを使用してリアルタイムのサーバーレス アプリケーションをビルドする方法を参照してください。