Configuration de l’authentification d’observabilité

L’exportateur Agent 365 nécessite un programme de résolution de jetons pour s’authentifier lors de l’exportation des données de télémétrie. Ce guide traite de la configuration des agents créés avec le Microsoft 365 Agents SDK, couvrant les agents compatibles avec Agent 365 et les agents de moteur personnalisés sur .NET, Python et Node.js.

Pour l’installation de la distribution, la configuration générale et les scénarios sans SDK Agent, consultez Microsoft OpenTelemetry Distro.

Overview

Il existe quatre scénarios d’authentification, en fonction du type de votre agent et de la façon dont il acquiert des jetons. L’acquisition de jetons peut utiliser le flux On-Behalf-Of (OBO) ou le service à service (S2S). Choisissez le scénario qui correspond à votre configuration :

Scénario Description
Agent 365 activé à l’aide de OBO Le AgenticTokenCache intégré de la distribution gère automatiquement l’acquisition des jetons sur la route de délégation. Aucun programme de résolution personnalisé n’est nécessaire. Cette route nécessite l’autorisation déléguée Agent365.Observability.OtelWrite et le consentement administrateur, que a365 setup all ne configure pas pour les agents blueprint.
Agent 365 activé à l’aide de S2S L’agent acquiert un jeton d’application uniquement en utilisant la chaîne d’identités agentiques (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Cela nécessite une option personnalisée TokenResolver et l’option de point de terminaison S2S. Une instance d’agent enregistré n’a pas besoin d’une permission d’Observabilité ni d’un consentement administrateur, c’est donc l’approche recommandée pour les agents blueprint configurés avec a365 setup all.
Moteur personnalisé utilisant OBO L’agent obtient un jeton utilisateur via Azure Bot OAuth, limité à l’API d’observabilité. Cela nécessite une connexion personnaliséeTokenResolver, une connexion Azure Bot OAuth, et la permission déléguée Agent365.Observability.OtelWrite avec le consentement de l’administrateur.
Moteur personnalisé utilisant S2S L’agent acquiert un jeton d’application uniquement à l’aide des informations d’identification du client. Nécessite un TokenResolver personnalisé. L’application enregistrée doit être une application standard (non agent). Les enregistrements d’application standard ne sont pas des instances d’agent inscrit, donc ils ont besoin de l’autorisation d’application Agent365.Observability.OtelWrite avec le consentement de l’administrateur.

Agent 365 activé à l’aide de OBO

Les agents activés pour Agent 365 reçoivent des requêtes avec une identité agentique (agenticAppId, agenticUserId) de la plateforme Agent 365. En utilisant OBO, le composant intégré de la distro AgenticTokenCache gère automatiquement l’acquisition de jetons sur la route déléguée, donc vous n’avez pas besoin d’un résolveur de jetons personnalisé.

Prerequisites

  • Enregistrement de l’application Microsoft Entra : un principal de service (enregistrement d’application) avec ID client, secret client et identifiant locataire.

  • Autorisations API déléguées : Ajouter Agent365.Observability.OtelWrite (Délégué) et accorder le consentement de l’administrateur. La a365 setup all commande n’ajoute pas cette permission pour les agents Blueprint. Un administrateur global peut l’ajouter et accorder le consentement de l’administrateur en exécutant la commande suivante :

    a365 setup permissions custom --resource-app-id 9b975845-388f-4429-889e-eab1ef63949c --scopes Agent365.Observability.OtelWrite
    

    Pour plus d’options, y compris centre d’administration Microsoft Entra, consultez Accorder l’autorisation.

Tip

Pour éviter la permission d’observabilité déléguée et le consentement administrateur, utilisez Agent 365-enabled via S2S pour les agents blueprint configurés avec a365 setup all.

Paramétrage

À chaque tour, votre agent appelle la fonction RegisterObservability avec le contexte du tour. 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 des instructions complètes de configuration, notamment sur les packages logiciels, la configuration et des exemples de code, consultez Cache de jetons Agentic pour les applications Agent Framework.

Agent 365 activé à l’aide de S2S

Les agents compatibles avec Agent 365 peuvent également utiliser l’authentification S2S (de service à service) au lieu d’OBO. Ce chemin est recommandé pour les agents blueprint configurés avec a365 setup all. L’agent obtient un jeton à l’aide de sa propre identité de principal de service via une chaîne d’identité agentique en deux étapes :

  1. Appelez getAgenticApplicationToken(tenantId, agentId) pour obtenir un jeton d’informations d’identification du client avec le chemin Federated Managed Identity (FMI).
  2. Appelez MSAL acquireTokenForClient avec le token application comme clientAssertion et la portée api://9b975845-388f-4429-889e-eab1ef63949c/.default.

Note

L’identité managée fédérée (FMI) est une architecture dans laquelle une identité managée participe à la fédération des identités de charge de travail par le biais d’informations d’identification d’identité fédérée, ce qui permet l’échange de jetons et l’authentification sans secret en fonction des relations d’approbation entre les identités.

La distribution OpenTelemetry de Microsoft utilise par défaut la route déléguée, donc vous devez fournir un TokenResolver personnalisé et définir l’option de point de terminaison S2S pour votre langage, comme UseS2SEndpoint = true, useS2SEndpoint: true ou a365_use_s2s_endpoint=True.

Prerequisites

  • Enregistrement Agent 365 : L’instance d’agent doit être enregistrée auprès de l’Agent 365. La commande a365 setup all enregistre les instances d’agent de blueprint. Créer une identité Microsoft Entra seule ne permet pas d'enregistrer une instance d'agent.
  • Informations d’identification Blueprint pour l’échange FMI : Utilisez un secret client Blueprint ou une identité managée pour obtenir le token Federated Managed Identity (FMI) pour api://AzureADTokenExchange/.default, puis échangez-le contre le token d’observabilité agent-instance.
  • Correspondance des valeurs d’identité : L’ID de l’agent et l’ID de locataire dans le baggage ainsi que l’URL d’exportation doivent correspondre à l’ID client de l’instance de l’agent et au locataire dans le jeton d’application uniquement.

Note

Une instance d’agent enregistrée Agent 365 peut exporter via l’itinéraire S2S avec un jeton uniquement applicatif qui n’a aucun Agent365.Observability.OtelWrite rôle. Il n’a pas besoin d’une autorisation d’observabilité ni d’un consentement administrateur. Les identités non enregistrées, y compris les inscriptions standard aux applications, nécessitent toujours le Agent365.Observability.OtelWrite rôle de l’application et le consentement de l’administrateur.

É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 AgenticUserAuthorization gestionnaire n’est 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 : Configurer la distribution avec un programme de résolution de jeton personnalisé

Les exemples suivants montrent comment activer l’exportation d’Agent 365 et enregistrer un TokenResolver personnalisé afin que l’exportateur puisse récupérer des jetons S2S pour chaque agent 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

Sur chaque message entrant, achetez le jeton S2S via la chaîne d’identité agentique et mettez-le en cache pour le programme de résolution.

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 à deux étapes manuel (get_agentic_application_token + MSAL acquire_token_for_client) est requis pour S2S. AgenticUserAuthorization.get_token() renvoie un jeton dont la portée est limitée à 5a807f24-.../.default (Bot Framework), et non à la ressource d’observabilité api://9b975845-.../.default : le point de terminaison S2S le rejette avec un code 401 InvalidAudience.

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

Important

Avant que votre résolveur ne retourne et mette en cache le jeton Observability final :

  • Validez que le jeton est réservé à l’application. Accepter idtyp=app. Si idtyp est absent, on n’accepte qu’un jeton ayant un claim roles non vide ou un claim oid non vide égal à sub. Rejetez les jetons qui ont une scp claim ou une autre idtyp valeur.
  • Vérifiez que aud c’est 9b975845-388f-4429-889e-eab1ef63949c ou api://9b975845-388f-4429-889e-eab1ef63949c, et que le jeton n’est pas expiré.
  • N’envoyez jamais l’assertion intermédiaire de blueprint, un jeton blueprint, ou un token utilisateur ou OBO à l’Observabilité.
  • Mettez en cache le dernier jeton et rafraîchissez-le avant qu’il n’expire. L’exportateur appelle le résolveur une fois pour chaque locataire et chaque identité d’agent dans chaque lot d’exportation.

Pour des implémentations complètes utilisant la distro, voir les exemples Agent 365 pour Node.js, Python et .NET.

Moteur personnalisé utilisant OBO

Les agents de moteur personnalisés utilisent des enregistrements d’application standard avec les connexions OAuth Azure Bot, et non la chaîne d’identité agentique. En utilisant OBO, l'agent obtient un jeton utilisateur via Azure Bot OAuth qui est déjà étendu à l'API d'observabilité A365 par le service de jeton Bot Framework. Un seul appel à getToken ou à GetTurnTokenAsync renvoie le jeton avec la portée appropriée, vous n’avez donc pas besoin de exchangeToken.

Prerequisites

Enregistrement de l’application Microsoft Entra avec permissions API déléguées. Ajouter Agent365.Observability.OtelWrite (Délégué) et accorder un consentement administrateur.

Important

Le agentId dans le cache de jetons doit correspondre au ID client de l’inscription de l’application, et non au agenticAppId de l’activité, qui n’existe pas pour les agents de moteur personnalisés. L’URL d’exportation inclut le agentId, et une incompatibilité provoque http 403.

Étape 1 : Configuration de l’environnement et de l’application

Les exemples suivants montrent comment configurer votre application et votre environnement d’exécution, notamment les valeurs de connexion au service, les paramètres du locataire et du client, ainsi que les mappages d’autorisation requis.

# .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 toutes les clés des variables d’environnement en majuscules. 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 : Configurer la distribution pour OBO

Les exemples suivants montrent comment activer l’exportation d’Agent 365, configurer l’exportateur pour qu’il utilise le point de terminaison OBO, et enregistrer un TokenResolver personnalisé qui renvoie des jetons délégués pendant l’exportation.

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 (qui valide le jeton JWT entrant (JSON Web Token) provenant de Bot Framework). Le chemin d’accès S2S/émulateur ne doit pas inclure ce middleware.

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é à partir de la connexion OAuth configurée Azure Bot, puis la mettre en cache par client d’application 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 : Définir les scopes pour la connexion Azure Bot OAuth nommée oboConnectionProfile à api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Sans ce paramètre, le jeton est limité au propre public du bot (api://botid-...) et l’exportation échoue avec HTTP 401 InvalidAudience.

Note

AGENT_APP.auth.get_token() retourne directement le jeton avec la portée correcte - aucun appel à exchange_token() n’est nécessaire. Bot Framework Token Service gère l’échange OBO lorsque l’étendue de connexion OAuth cible la ressource d’observabilité A365.

Moteur personnalisé utilisant S2S

Les agents du moteur personnalisé peuvent utiliser S2S (informations d’identification du client) pour obtenir un jeton uniquement applicatif en utilisant les informations d’identification de la connexion de service. Cette méthode utilise les identifiants MSAL standards du client - elle n’a pas besoin d’une chaîne d’identité agentique.

Prerequisites

  • Enregistrement de l’application Microsoft Entra : L’enregistrement de l’application doit être une application de moteur personnalisé (standard). Les enregistrements d’applications avec Agent 365 activé ne peuvent pas utiliser simplement client_credentials pour la ressource d’observabilité (AADSTS82001).
  • Autorisations d’application : Ajouter Agent365.Observability.OtelWrite (Application, non Délégué), et accorder le consentement administrateur. Les inscriptions d’application standard ne sont pas des instances d’agent enregistrées Agent 365, donc elles ont besoin de ce rôle d’application sur la route S2S.

Important

La agentId que vous utilisez pour la mise en cache doit être la file d’attente ClientId de ServiceConnection. L’URL d’exportation est /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces : une incompatibilité provoque l’erreur HTTP 403.

Étape 1 : Configuration de l’environnement et de l’application

Les exemples suivants montrent comment configurer votre application et votre environnement d’exécution, notamment les valeurs de connexion au service, les paramètres du locataire et du client, ainsi que les mappages d’autorisation requis.

# .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 : Configurer la distribution pour S2S

Les exemples suivants montrent comment activer l’exportation d’Agent 365, configurer l’exportateur pour utiliser le point de terminaison S2S et enregistrer un TokenResolver personnalisé pour la recherche de jetons lors de l’exportation.

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 agent 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’export de span

L’exportateur Agent365 nécessite le baggage (identifiant du locataire et identifiant de l’agent) dans le contexte du span. Sans ce bagage, l’exportateur ignore les spans et enregistre 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])