次の方法で共有

Web PubSub Socket.IOとAzure Functionの接続について、API managementを経由した設定方法を知りたい

takezawa_skk 0 評価のポイント
2025-08-25T05:50:59.9+00:00

Web PubSub Socket.IOとAzure Functionの接続について、API managementを経由した設定方法を知りたい

<聞きたいこと>

(前提)

前提として、Web PubSub Socket.IOにおいてクライアントイベントの送信先としてイベントハンドラーのURLテンプレートに「イベントがトリガーされたときに呼び出すサービスのパブリックにアクセス可能なエンドポイント」が設定されます。今回の場合、これはAzure Functionのエンドポイント(パブリックアクセス可能)に当たります。

(やりたいこと)

Azure Functionはパブリックアクセス不可にして、API management経由でアクセスさせます。その場合の設定方法を知りたいです。

1. イベントハンドラーのURLテンプレートは何になるか?
(推測)URLテンプレート「<API_management_Endpoint>/runtime/webhooks/socketio」にする
2. API managementの設定はどうなるか?
(推測)
 ・API/Operationを追加
  ・Frontend
    ・プレフィックス: runtime/webhooks/socketio
    ・method: POST
    ・Query parameters: Name:code, value:「アプリ キー/システム キー/socketio_extension」
    ・Inbound processing: <set-header name="Authorization" exists-action="delete" />を追加

<実証済みなこと>

クライアントプログラム
↓
↓ Functionから/Negotiate経由でアクセストークン取得
↓ socket.connetで接続
↓
Web PubSub Socket.IO

ー

Azure Functions
↑
↑ socket.on("connet")で接続確認済み
↓ socket.set()やsocket.emit()で送受信確認済み
↓
クライアントプログラム

(環境設定)

言語: python

Web PubSub Socket.IO

  • type: Serverless
  • パブリック ネットワーク アクセス: 有効
  • アクセスの制御ルール: クライアントプログラムのIPアドレスだけ許可

API management

  • API/ Operation: Negotiateを登録済み(/Negotiate)

Azure Functions

  • 公衆ネットワーク アクセス: すべてのネットワークから有効
  • プライベート エンドポイント: 設定済み
  • 仮想ネットワーク統合: 設定済み
  • API manaement: 接続済みで、アクセスも成功している
  • 「アプリ キー/システム キー/socketio_extension」: Azure Portalから確認済み
  • 「WebPubSubForSocketIOConnectionString」: 環境変数に登録済み
# Azure Function/ rfid.py

import json

import azure.functions as func
from azure.functions.decorators.core import DataType

from common.utils import logging_config

logger = logging_config.get_logger()

bp = func.Blueprint()

# OnConnect Function
@bp.function_name(name="OnConnect")
@bp.generic_trigger(arg_name="sio", type="socketiotrigger", data_type=DataType.STRING, hub="rfid", eventName="connect")
def connect(sio: str) -> str:
    logger.info("接続しました")
    data = json.loads(sio)
    headers = data.get("headers")
    if headers is not None:
        user_id_headers = headers.get("x-wps-user-id")
    else:
        user_id_headers = None
    logger.info(f"user_id_headers: {user_id_headers}")
    return json.dumps({"statusCode": 200})

クライアントプログラム

# main.py

import getpass
import platform
import time

import requests
import socketio

import rfid

log_content = []


def log(message):
    log_content.append(message)


sio = socketio.Client()
NEGOTIATE_URL = "<API_management_Endpoint>/Negotiate"

pc_id = platform.node()
local_account = getpass.getuser()

user_id = f"{pc_id}_{local_account}"
headers = {
    "Content-Type": "application/json",
    "x-wps-user-id": user_id,
}

def connect_to_pubsub():
    response = requests.post(NEGOTIATE_URL, json={}, headers=headers)
    nego = response.json()
    sio.connect(
        nego["endpoint"],
        socketio_path=nego["path"],
        headers={
            "Authorization": f"Bearer {nego['token']}",
            "x-wps-user-id": user_id,
        },
    )

@sio.event
def connect():
    print("Web PubSubに接続しました")

@sio.event
def disconnect():
    print("切断されました。再接続を試みます...")
    connect_to_pubsub()  # 再接続

@sio.event
def connect_error(data):
    print("接続エラー:", data)
    connect_to_pubsub()  # 再接続


if __name__ == "__main__":
    connect_to_pubsub()
    sio.wait()  # 常駐

すでにトライしたこと

「やりたいこと」セクションの推測部分については、実際にトライしています。結果として、Azure Functionsの方で「AuthenticationScheme: WebJobsAuthLevel was forbidden.」エラーになります。クライアントプログラムからのエラーは以下です。

接続エラー: {'message': The request is denied because the preflight abuse protection request failed. Request ID: request_id'}

Traceback (most recent call last):
  File "main.py", line 86, in <module>
  File "main.py", line 33, in connect_to_pubsub
  File "socketio\client.py", line 168, in connect
socketio.exceptions.ConnectionError: One or more namespaces failed to connect

参照したページ

Socket.IO Azure 関数トリガーとバインド/トリガー バインド

Azure Web PubSub サービスの内部構造/イベント ハンドラー

HTTP Protocol Binding for CloudEvents - Version 1.0.1

Azure
Azure

Microsoft が管理する世界のデータ センター ネットワークを介してアプリケーションとサービスを構築、配置、および管理するインフラストラクチャおよびクラウド コンピューティング プラットフォーム。


1 件の回答

並べ替え方法: 最も役に立つ
  1. Rakesh Mishra 9,700 評価のポイント Microsoft 外部スタッフ モデレーター
    2025-08-25T14:50:06.6266667+00:00

    takezawa_skk 様、こんにちは。

    Microsoft Q&A プラットフォームへようこそ!ご質問いただきありがとうございます。以下の解決策をご確認ください。さらに詳細な手順が必要な場合はお知らせください。

    想定されるワークフロー

    Web PubSub → APIM (パブリック/アクセス可能) → APIM が AAD トークン (マネージド ID) を取得 → APIM が Authorization: Bearer <token> でリクエストを転送 → 関数 (AAD/Easy Auth で保護) が呼び出しを受け入れます。

    手順
    1. Web PubSub イベント Webhook を受信する APIM 操作を作成します。例のパス: POST (および OPTIONS) をサポートする /webpubsub/{event}
    2. APIM インスタンスでマネージド ID (システム割り当てまたはユーザー割り当て) を有効にします。
    3. Microsoft Entra (App Service 認証 / Azure AD) を使用して Function App を保護します。トークンの対象ユーザーとして、App ID URI を公開するか、App のクライアント ID を使用します。
    4. APIM 受信ポリシーを追加してトークンを取得し、バックエンド呼び出し用の Authorization ヘッダーをアタッチします。サンプルポリシースニペット:
    <policies>
    <inbound>
    <base />
    <choose>
    <when condition="@(context.Request.Method == \"OPTIONS\")">
    <return-response>
    <set-status code="204" reason="No Content" />
    </return-response>
    </when>
    </choose>
    
    <authentication-managed-identity resource="https://{your-function-app-appid-uri}" output-token-variable-name="msi-token" />
    
    <set-header name="Authorization" exists-action="override">
    <value>@("Bearer " + (string)context.Variables["msi-token"])</value>
    </set-header>
    </inbound>
    <backend>
    <base />
    </backend>
    <outbound>
    <base />
    </outbound>
    </policies>
    
    1. Web PubSub を構成します。イベント ハンドラーの URL テンプレートを APIM のエンドポイントに設定します (例: https://api.contoso.com/webpubsub/{event})。
    2. エンドツーエンドのテストを行います。OPTIONS プローブの応答、APIM トークンの取得、および Function がトークンを受け入れてイベントを処理することを検証します。
    考慮すべき重要なポイント
    • APIM の到達可能性: Web PubSub は APIM エンドポイントを呼び出すことができる必要があります。APIM がプライベートの場合は、Web PubSub が APIM へのネットワーク アクセス (プライベート リンク) を持っていることを確認するか、直接プライベート呼び出しを行うために Web PubSub 共有プライベート エンドポイントを使用します。
    • トークンの対象ユーザー/リソース: トークンを取得する際、Function App の App ID URI またはクライアント ID をリソース/対象ユーザーとして使用します。
    • OPTIONS/プローブ処理: Web PubSub はプローブを送信する場合があります。 APIM は、障害を回避するために、OPTIONS に対して 204 またはその他の許容可能な応答を返す必要があります。
    • ファンクションキーは不要: マネージド ID と AAD を組み合わせることで、APIM にファンクションキーを保存する必要がなくなります (セキュリティ強化のため推奨)。
    • ネットワークとレイテンシ: APIM が Function のプライベートエンドポイント (VNet/統合) に到達できることを確認し、負荷時の追加レイテンシを測定します。
    • 最小限の権限: APIM のマネージド ID に必要な権限のみを付与します。可能な場合は、環境ごとにユーザー割り当て ID を優先します。
    制限事項
    • 複雑さ: このパターンでは、マネージド ID、Microsoft Entra アプリ登録または App Service 認証設定、および APIM ポリシーを正しく構成する必要があります。
    • プライベートネットワークの制約: APIM をプライベートにする場合は、Web Pub/Sub のネットワーク到達可能性を解決する必要があります。パスに APIM が必要ない場合は、Web Pub/Sub 共有プライベートエンドポイント機能を代替手段として使用できます。
    • 関数認証の不一致: 関数が依然として ?code= 関数キーを必要とする場合は、AAD 認証に移行するか、APIM を調整して関数キーを挿入してください(セキュリティが低下します)。
    • トークンの有効期間と更新: APIM は有効期限までトークンをキャッシュします。トークンの有効期間がニーズに適合していることを確認してください。
    参考資料:

    この回答は役に立ちましたか?

    0 件のコメント コメントはありません

お客様の回答

質問作成者は回答に "承認済み"、モデレーターは "おすすめ" とマークできます。これにより、ユーザーは作成者の問題が回答によって解決したことを把握できます。