Configuration de l’authentification de l’observabilité

L’exportateur Agent 365 nécessite un résolveur de jeton pour s’authentifier lors de l’exportation de télémétrie. Ce guide couvre la configuration pour les assistants construits avec le Microsoft 365 Agents SDK, couvrant à la fois les assistants activés par Agent 365 et les assistants à moteur personnalisé sur .NET, Python et Node.js.

Pour l’installation de la distribution, la configuration générale et les scénarios hors Kit de développement logiciel (SDK) assistant, consultez Microsoft OpenTelemetry Distro.

Vue d’ensemble

Il existe quatre scénarios d’authentification, selon le type d’assistant et la manière dont il acquiert les jetons. L’acquisition de jetons peut utiliser le flux On-Behalf-Of (OBO) ou Service-to-Service (S2S). Choisissez le scénario qui correspond à votre configuration :

Scénario Description
Activé par Agent 365 utilisant le flux OBO Le AgenticTokenCache intégré à la distribution gère automatiquement l’acquisition des jetons. Aucun résolveur personnalisé n’est nécessaire. C’est l’approche recommandée pour les assistants activés par Agent 365.
Activé par Agent 365 utilisant le flux S2S L’assistant acquiert un jeton en utilisant la chaîne d’identité agentique (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Nécessite un composant personnalisé TokenResolver. Utilisez cette approche lorsque OBO n’est pas disponible ou si vous avez besoin de jetons d’application uniquement.
Moteur personnalisé utilisant OBO L’assistant obtient un jeton utilisateur via Azure Bot OAuth, ayant pour périmètre l’API d’observabilité. Requiert un TokenResolver personnalisé et une connexion Azure Bot OAuth.
Moteur personnalisé utilisant S2S L’assistant acquiert un jeton d’application à l’aide des informations d’identification du client. Nécessite un composant personnalisé TokenResolver. L’enregistrement de l’application doit être une application standard (non agentique).

Activé par Agent 365 utilisant le flux OBO

Les assistants activés pour Agent 365 reçoivent des requêtes avec une identité agentique (agenticAppId, agenticUserId) de la plateforme Agent 365. Avec OBO, le composant intégré AgenticTokenCache de la distribution gère automatiquement l’acquisition des jetons : aucun composant personnalisé de résolution de jeton n’est nécessaire.

Configuration requise

  • Inscription d’application Entra : un principal de service (inscription d’application) avec l’ID client, la clé secrète client et l’ID de locataire
  • Autorisations API déléguées : ajoutez Agent365.Observability.OtelWrite (Délégué), accordez le consentement de l’administrateur. Pour obtenir les étapes détaillées, voir Accorder la permission.

Configurer

À chaque itération, votre assistant appelle la fonction RegisterObservability avec le contexte de l’itération. Le cache intégré utilise le jeton délégué de l’utilisateur du gestionnaire AgenticUserAuthorization afin d’effectuer un échange OBO et d’obtenir un jeton avec la portée Agent365.Observability.OtelWrite.

Pour obtenir l’ensemble des instructions de configuration complètes, y compris les packages, la configuration et des exemples de code, consultez Cache de jetons agentiques avec les applications Agent Framework.

Activé par Agent 365 utilisant le flux S2S

Les assistants activés par Agent 365 peuvent également utiliser l’authentification S2S (service-à-service) au lieu de l’OBO. L’assistant obtient un jeton à l’aide de sa propre identité de principal de service via une chaîne d’identité agentique en deux étapes :

  1. getAgenticApplicationToken(tenantId, agentId) : informations d’identification du client + chemin d’accès de l’identité managée fédérée (FMI)
  2. MSAL acquireTokenForClient avec le jeton d’application en tant que clientAssertion et étendue api://9b975845-388f-4429-889e-eab1ef63949c/.default

Note

La Federated Managed Identity (FMI) est une architecture dans laquelle une identité gérée participe à la fédération d’identités de charge de travail via des identifiants fédérés, permettant l’échange de jetons et l’authentification sans secrets, fondée sur des relations de confiance entre identités.

Vous devez fournir un TokenResolver personnalisé et définir UseS2SEndpoint = true.

Configuration requise

  • Inscription d’application Entra : un principal de service (inscription d’application) avec l’ID client, la clé secrète client et l’ID de locataire

  • Autorisations de l’API APPLICATION : ajouter Agent365.Observability.OtelWrite (Application), accorder le consentement de l’administrateur

  • Rôle d’application Agent365.Observability.OtelWrite : le principal de service de l’assistant doit avoir le rôle OtelWrite attribué sur la ressource Agent365 Observability. Utiliser CLI Agent 365 :

    a365 setup permissions bot --config-dir "<path-to-config-dir>"
    

    Note

    La propagation du rôle peut prendre quelques minutes. Des erreurs initiales 401 ou 403 provenant du point de terminaison d’exportation sont à prévoir pendant cette période.

Étape 1 : configuration de l’environnement

Les exemples de code suivants montrent comment définir les paramètres d’environnement requis pour la connexion, le locataire, les informations d’identification du client et l’exportateur d’observabilité avant d’activer le flux de jeton S2S personnalisé.

Aucun gestionnaire AgenticUserAuthorization nécessaire. S2S utilise la chaîne manuelle d’identité agentique (get_agentic_application_token + MSAL acquire_token_for_client) pour obtenir un jeton limité à la ressource d’observabilité.

CONNECTIONSMAP__0__SERVICEURL=*
CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION

CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<your-client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<your-client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<your-tenant-id>

ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true

Étape 2 : configurez la distribution avec un résolveur de jeton personnalisé

Les exemples suivants illustrent comment activer l’exportation Agent 365 et enregistrer un résolveur de jeton personnalisé TokenResolver afin que l’exportateur puisse récupérer des jetons S2S pour chaque assistant et locataire.

from microsoft.opentelemetry import use_microsoft_opentelemetry

_token_cache: dict[str, str] = {}

def token_resolver(agent_id: str, tenant_id: str) -> str | None:
    return _token_cache.get(f"{agent_id}:{tenant_id}")

use_microsoft_opentelemetry(
    enable_a365=True,
    a365_token_resolver=token_resolver,
    a365_use_s2s_endpoint=True,
    a365_enable_observability_exporter=True,
)

Étape 3 : acquérir et mettre en cache le jeton S2S

À chaque message entrant, acquérez le jeton S2S via la chaîne d’identité agentique et mettez-le en cache pour le résolveur.

import asyncio
from msal import ConfidentialClientApplication
from microsoft.opentelemetry.a365.core import BaggageBuilder, InvokeAgentScope, InvokeAgentScopeDetails, Request

OBSERVABILITY_S2S_SCOPE = "api://9b975845-388f-4429-889e-eab1ef63949c/.default"

async def get_agentic_s2s_token(connection, tenant_id: str, agent_id: str) -> str:
    # Step 1: Get agentic application token (client_credentials + fmi_path)
    app_token = await connection.get_agentic_application_token(tenant_id, agent_id)
    if not app_token:
        raise ValueError(f"Failed to get agentic app token for agent {agent_id}")

    # Step 2: Exchange for observability-scoped token
    cca = ConfidentialClientApplication(
        client_id=agent_id,
        authority=f"https://login.microsoftonline.com/{tenant_id}",
        client_credential={"client_assertion": app_token},
    )
    result = await asyncio.to_thread(
        lambda: cca.acquire_token_for_client(scopes=[OBSERVABILITY_S2S_SCOPE])
    )
    if not result or "access_token" not in result:
        raise ValueError(f"Token acquisition failed: {result}")
    return result["access_token"]

# In your message handler : use SDK helpers to get agent/tenant from the activity:
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
    # get_agentic_instance_id reads from recipient (SDK convention)
    agent_id = context.activity.get_agentic_instance_id()
    tenant_id = context.activity.get_agentic_tenant_id()

    # Acquire S2S token and cache BEFORE creating spans
    connection = CONNECTION_MANAGER.get_connection("SERVICE_CONNECTION")
    token = await get_agentic_s2s_token(connection, tenant_id, agent_id)
    _token_cache[f"{agent_id}:{tenant_id}"] = token

    # Wrap spans in BaggageBuilder so the exporter can resolve the token
    request = Request(content=user_message, session_id=None)
    with BaggageBuilder().tenant_id(tenant_id).agent_id(agent_id).build():
        invoke_scope = InvokeAgentScope.start(request, InvokeAgentScopeDetails(), agent_details)
        with invoke_scope:
            invoke_scope.record_input_messages([user_message])
            invoke_scope.record_output_messages([response])

Important

Le flux manuel en deux étapes (get_agentic_application_token + MSAL acquire_token_for_client) est nécessaire pour S2S. AgenticUserAuthorization.get_token() retourne un jeton portant sur 5a807f24-.../.default (Bot Framework), et non sur la ressource d’observabilité api://9b975845-.../.default : le point de terminaison S2S le rejette avec une erreur 401 InvalidAudience.

  • Utilisez context.activity.get_agentic_instance_id() et get_agentic_tenant_id() pour lire l’assistant et le locataire depuis l’activité (lecture depuis recipient, conformément à la convention du SDK).
  • Acquérez et mettez en cache le jeton S2S avant de créer des spans. Le BatchSpanProcessor de l’exportateur peut effectuer une opération de flush avant que le gestionnaire ait terminé : si le token n’est pas encore mis en cache, l’exportation échoue.
  • Placez tous les scopes A365 dans BaggageBuilder afin que l’exportateur sache pour quel assistant et quel locataire résoudre les jetons d’authentification. Sans baggage, les spans sont ignorés en silence et indique « Aucun span avec une identité du locataire/de l’assistant n’a été trouvé. »

Moteur personnalisé utilisant OBO

Les assistants de moteur personnalisés utilisent des inscriptions d’application standard avec les connexions OAuth Azure Bot, et non la chaîne d’identité agentique. En utilisant OBO, l’assistant obtient un token utilisateur via Azure Bot OAuth qui possède déjà la portée requise pour l’API d’observabilité A365, attribuée par le Bot Framework Token Service. Un seul appel getToken ou GetTurnTokenAsync retourne le jeton correctement délimité ; exchangeToken n’est pas nécessaire.

Configuration requise

Inscription de l’application Entra avec autorisations API déléguées. Ajouter Agent365.Observability.OtelWrite (Délégué) et accorder le consentement administrateur

Important

La valeur agentId dans le cache de jetons doit correspondre à l’ID client de l’enregistrement de l’application Client ID, et non à celle de l’activité agenticAppId, qui n’existe pas pour les assistants moteur personnalisés. L’URL d’exportation inclut le agentId ; en cas de non-correspondance, cela entraîne une erreur HTTP 403.

Étape 1 : Environnement et configuration de l’application

Les exemples suivants montrent comment configurer votre application et votre environnement d’exécution, y compris les valeurs de connexion de service, les paramètres de locataire et de client, ainsi que les mappages d’autorisations nécessaires.

# .env
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<your-client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<your-client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<your-tenant-id>

CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION
CONNECTIONSMAP__0__SERVICEURL=*

# Auth handler config : TYPE is required, name is uppercased by load_configuration_from_env
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__OBOCONNECTIONPROFILE__TYPE=UserAuthorization
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__OBOCONNECTIONPROFILE__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=oboConnectionProfile
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__OBOCONNECTIONPROFILE__SETTINGS__SCOPES=api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite

ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true

Important

load_configuration_from_env met en majuscules toutes les clés des variables d’environnement. Le nom du gestionnaire devient OBOCONNECTIONPROFILE, et vous devez y faire référence en respectant exactement cette casse dans les appels à auth_handlers et get_token(). L’absence de TYPE provoque Auth handler ... not recognized or not configured à l’exécution.

Étape 2 : configurez la distribution pour l’OBO

Les exemples suivants montrent comment activer l’exportation Agent 365, maintenir l’exportateur sur le point de terminaison OBO et enregistrer un TokenResolver personnalisé qui renvoie des jetons délégués lors de l’export.

from microsoft.opentelemetry import use_microsoft_opentelemetry

_token_cache: dict[str, str] = {}

def token_resolver(agent_id: str, tenant_id: str) -> str | None:
    return _token_cache.get(f"{agent_id}:{tenant_id}")

environ["ENABLE_A365_OBSERVABILITY_EXPORTER"] = "true"

use_microsoft_opentelemetry(
    enable_a365=True,
    a365_token_resolver=token_resolver,
    a365_use_s2s_endpoint=False,  # OBO uses /observability endpoint
    a365_enable_observability_exporter=True,
)

Note

Le mode OBO nécessite jwt_authorization_middleware sur aiohttpApplication (valide le JWT entrant (JSON Web Token) depuis Bot Framework). Le chemin S2S/émulateur ne devrait pas inclure cet intergiciel.

from microsoft_agents.hosting.aiohttp import jwt_authorization_middleware
app = Application(middlewares=[jwt_authorization_middleware])

Étape 3 : acquérir le jeton OBO

Les exemples suivants montrent comment demander un jeton OBO délégué depuis la connexion OAuth Azure Bot configurée, puis le mettre en cache par application cliente et locataire pour l’exportateur.

from microsoft_agents.hosting.core import (
    AgentApplication, Authorization, MemoryStorage, TurnContext, TurnState,
)
from microsoft_agents.activity import load_configuration_from_env
from microsoft_agents.authentication.msal import MsalConnectionManager
from microsoft_agents.hosting.aiohttp import CloudAdapter

# Auth handlers are loaded from .env via load_configuration_from_env (see Environment config above)
agents_sdk_config = load_configuration_from_env(environ)

STORAGE = MemoryStorage()
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)

AGENT_APP = AgentApplication[TurnState](
    storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config,
)

CLIENT_ID = environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID", "")
TENANT_ID = environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID", "")

# Message handler : get_token returns a token already scoped to the observability API.
# The Azure Bot Token Service performs the OBO exchange internally based on the
# OAuth connection's configured scope. No manual MSAL exchange_token call is needed.
@AGENT_APP.activity("message", auth_handlers=["OBOCONNECTIONPROFILE"])
async def on_message(context: TurnContext, _state: TurnState):
    token_response = await AGENT_APP.auth.get_token(context, "OBOCONNECTIONPROFILE")
    # token_response.token has aud=<a365-observability-app-id>,
    # scp=Agent365.Observability.OtelWrite
    _token_cache[f"{CLIENT_ID}:{TENANT_ID}"] = token_response.token

Important

Prérequis du portail Azure : la connexion OAuth Azure Bot nommée oboConnectionProfile doit avoir ses Étendues définies sur api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Sans ce paramètre, le jeton est attribué à l’audience du bot (api://botid-...) et l’export échoue avec une erreur HTTP 401 InvalidAudience.

Note

AGENT_APP.auth.get_token() retourne directement le jeton correctement délimité : aucun appel à exchange_token() n’est nécessaire. Le Bot Framework Token Service gère l’échange OBO lorsque le périmètre de connexion OAuth cible la ressource d’observabilité A365.

Moteur personnalisé utilisant S2S

Les assistants de moteur personnalisés peuvent utiliser S2S (informations d’identification client) pour obtenir un jeton réservé à l’application en utilisant les informations d’identification de la connexion de service. Cette méthode utilise les identifiants client MSAL standard – aucune chaîne manuelle d’identité agentique requise.

Configuration requise

  • Enregistrement de l’application Azure AD : doit être une application moteur personnalisé (standard). Les enregistrements d’applications activés par Agent 365 ne peuvent pas utiliser client_credentials en entier pour la ressource d’observabilité (AADSTS82001).
  • Autorisations de l’application : ajouter Agent365.Observability.OtelWrite (Application, non déléguée), et accorder le consentement de l’administrateur.

Important

L’utilisation de agentId pour la mise en cache doit être le ClientId de ServiceConnection L’URL d’exportation est /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces : une discordance entraîne une erreur HTTP 403.

Étape 1 : Environnement et configuration de l’application

Les exemples suivants montrent comment configurer votre application et votre environnement d’exécution, y compris les valeurs de connexion de service, les paramètres de locataire et de client, ainsi que les mappages d’autorisations nécessaires.

# .env
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<your-client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<your-client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<your-tenant-id>

CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION
CONNECTIONSMAP__0__SERVICEURL=*

ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true

Étape 2 : configurez la distribution pour l’S2S

Les exemples suivants montrent comment activer l’exportation Agent 365, configurer l’exportateur vers le point de terminaison S2S et enregistrer une TokenResolver personnalisée pour la recherche de jeton lors de l’export.

from microsoft.opentelemetry import use_microsoft_opentelemetry

_token_cache: dict[str, str] = {}

def token_resolver(agent_id: str, tenant_id: str) -> str | None:
    return _token_cache.get(f"{agent_id}:{tenant_id}")

use_microsoft_opentelemetry(
    enable_a365=True,
    a365_token_resolver=token_resolver,
    a365_use_s2s_endpoint=True,  # S2S uses /observabilityService endpoint
    a365_enable_observability_exporter=True,
)

Étape 3 : acquérir le jeton S2S

Les exemples suivants montrent comment demander un jeton d’accès d’application uniquement pour la ressource d’observabilité à l’aide des informations d’identification de connexion de service, puis le mettre en cache par assistant et locataire pour l’exportateur.

# Force agentId to ServiceConnection ClientId (custom engine agents have no agenticAppId)
agent_id = os.environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID")
tenant_id = os.environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID")

connection = CONNECTION_MANAGER.get_connection("SERVICE_CONNECTION")
token = await connection.get_access_token(
    resource_url="https://login.microsoftonline.com",
    scopes=["api://9b975845-388f-4429-889e-eab1ef63949c/.default"],
)
_token_cache[f"{agent_id}:{tenant_id}"] = token

Étape 4 : Configurer le baggage pour l’exportation de span

L’exportateur Agent 365 nécessite que le baggage (ID du locataire et ID de l’assistant) soit défini dans le contexte de span. Sans cela, l’exportateur supprime silencieusement des spans avec le message No spans with tenant/agent identity found..

from microsoft.opentelemetry.a365.core import BaggageBuilder, InvokeAgentScope

# Baggage must wrap the span as a context manager
with BaggageBuilder().tenant_id(tenant_id).agent_id(agent_id).build():
    invoke_scope = InvokeAgentScope.start(request, InvokeAgentScopeDetails(), agent_details)
    with invoke_scope:
        invoke_scope.record_input_messages([user_message])
        invoke_scope.record_output_messages([response])