Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Un assistant utilise OAuth pour connecter les utilisateurs et obtenir des jetons pour les ressources en aval (comme Microsoft Graph) sans gérer les identifiants lui-même. Azure Bot Service gère l’échange de jetons, et l’assistant récupère le jeton utilisateur résultant lors d’un tour.
Vue d’ensemble
L’utilisation d’OAuth dans un assistant implique trois activités :
- Configurez OAuth sur le bot Azure et l’enregistrement de l’application : créez une ou plusieurs connexions OAuth sur votre ressource Azure Bot, chacune soutenue par un enregistrement d’application Microsoft Entra ID. Ajouter une autorisation utilisateur à l’aide d’un identifiant d’identité fédéré couvre l’approche la plus courante. D’autres types de certifications, tels que les clés secrètes client ou les certificats, sont également pris en charge. Pour l’ensemble complet des options, voir Bases de l’authentification Bot Service.
- Configurer les paramètres correspondants dans l’assistant : chaque connexion OAuth sur Azure Bot correspond à un gestionnaire OAuth dans la configuration de l’assistant. Consultez Paramètres. Pour la configuration générale des assistants, voir Qu’est-ce que Microsoft 365 Agents SDK.
- Utiliser les jetons en code : lors d’un tour, récupérez le jeton utilisateur — ou effectuez un échange au nom de — via l’API d’autorisation utilisateur de l’assistant. Voir Utiliser le jeton en code (hors au nom de) et Utiliser le jeton en code (au nom de).
Gardez à l’esprit les concepts suivants en lisant la suite de cet article :
- Un bot Azure peut contenir plusieurs connexions OAuth. Par exemple, une connexion pour Microsoft Graph et une autre pour GitHub. Chaque connexion est configurée indépendamment sur Azure Bot.
- Il existe une relation 1:1 entre une connexion OAuth sur Azure Bot et un gestionnaire OAuth dans l’assistant. Le paramètre
AzureBotOAuthConnectionNamed’un gestionnaire nomme la connexion Azure Bot qu’il utilise. Pour utiliser deux connexions, définissons deux gestionnaires. - L’API d’autorisation utilisateur de l’assistant est la surface que vous appelez dans le code. Dans .NET, c’est
AgentApplication.UserAuthorization— par exemple,GetTurnTokenAsynclire un jeton etExchangeTurnTokenAsynceffectuer un échange au nom de. Les surfaces équivalentes sontauthorizationen JavaScript etauthen Python.
Pour des exemples en fonctionnement, voir les exemples d’auto-connexion et au nom de pour :
Prise en charge linguistique pour OAuth
Le kit de développement logiciel des assistants prend en charge OAuth pour .NET, JavaScript et Python. Les concepts fondamentaux sont les mêmes dans tous les langages : gestionnaires OAuth, attachement de gestionnaires aux itinéraires, et échange au nom de. Seuls le format de configuration et les noms des API des gestionnaires diffèrent :
| Langage | Où configurer | Surface d’API |
|---|---|---|
| .NET |
appsettings.json (ou code dans Program.cs) |
AgentApplication.UserAuthorization |
| JavaScript |
.env variables d’environnements |
AgentApplication.authorization |
| Python | Variables d’environnement .env |
AgentApplication.auth |
Pour JavaScript et Python, les clés .env utilisent les mêmes noms hiérarchiques que la structure .NET appsettings.json, chaque niveau étant séparé par un double soulignement (__). Les clés JavaScript conservent les noms terminaux utilisant la casse Camel-Case affichés dans les tables (par exemple, azureBotOAuthConnectionName). Les clés Python sont en majuscules (par exemple, AZUREBOTOAUTHCONNECTIONNAME).
Important
La connexion automatique globale (AutoSignIn) et DefaultHandlerName sont prises en charge uniquement en .NET. Dans JavaScript et Python, vous associez des gestionnaires OAuth à des routes spécifiques, comme montré dans Configuration par route.
Configurations
Un objet d’autorisation utilisateur contenu dans AgentApplication détermine comment l’assistant acquiert les jetons utilisateur. Au minimum, chaque gestionnaire nomme la connexion Azure Bot OAuth qu’il utilise. Les exemples suivants montrent la structure minimale de chaque langue. Les tables qui suivent décrivent le reste des propriétés disponibles, et les sections au nom de couvrent les paramètres OBOConnectionName et OBOScopes.
Dans .NET, configurez l’autorisation utilisateur sous AgentApplication dans appsettings.json :
"AgentApplication": {
"UserAuthorization": {
"DefaultHandlerName": "{{handler-name}}",
"AutoSignIn": true | false,
"Handlers": {
"{{handler-name}}": {
"Settings": {
"AzureBotOAuthConnectionName": "{{azure-bot-connection-name}}"
}
}
}
}
}
En JavaScript, configurez l’autorisation utilisateur avec des variables d’environnement dans le fichier .env :
# Connection used to authenticate the agent itself
connections__serviceConnection__settings__clientId=
connections__serviceConnection__settings__clientSecret=
connections__serviceConnection__settings__tenantId=
connectionsMap__0__connection=serviceConnection
connectionsMap__0__serviceUrl=*
# OAuth handler named "{{handler-name}}"
AgentApplication__UserAuthorization__Handlers__{{handler-name}}__Settings__azureBotOAuthConnectionName={{azure-bot-connection-name}}
En Python, configurez l’autorisation utilisateur avec des variables d’environnement dans le fichier .env :
# Connection used to authenticate the agent itself
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=
# OAuth handler named "{{handler-name}}"
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__{{handler-name}}__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME={{azure-bot-connection-name}}
Propriétés UserAuthorization
Le tableau suivant répertorie les propriétés de premier niveau de UserAuthorization qui déterminent comment les gestionnaires sont sélectionnés et comment les jetons sont acquis pour chaque activité entrante.
| Propriété | Obligatoire | Type | Description |
|---|---|---|---|
DefaultHandlerName |
Non (recommandé) | chaine | .NET uniquement. Nom du gestionnaire utilisé quand AutoSignIn prend la valeur true et qu’aucun remplacement par routage n’est spécifié. |
AutoSignIn |
Non | bool ou délégué | .NET uniquement. Lorsqu’il est vrai (par défaut), l’assistant tente d’acquérir un jeton pour chaque activité entrante. Remplacez par Options.AutoSignIn au moment du runtime pour filtrer les types d’activité. |
Handlers |
Oui (au moins un) | objet (dictionnaire) | Correspondance du nom du gestionnaire à sa configuration. Chaque clé doit être unique. |
Dans .NET, pour restreindre les activités auxquelles s’applique la connexion automatique, définissez un prédicat similaire à : Options.AutoSignIn = (context, ct) => Task.FromResult(context.Activity.IsType(ActivityTypes.Message));. JavaScript et Python ne supportent pas la connexion automatique globale. À la place, limitez la connexion en associant des noms spécifiques de gestionnaires à des routes individuelles, comme montré dans les exemples par route .
Propriétés des paramètres
Le tableau suivant décrit l’objet Settings imbriqué appliqué à un gestionnaire OAuth individuel, contrôlant la présentation de la carte d’accession, le comportement de nouvelle tentative, les délais d’attente et la configuration optionnelle de l’échange OBO.
| Propriété | Obligatoire | Type | Description |
|---|---|---|---|
AzureBotOAuthConnectionName |
Oui | chaine | Nom de connexion OAuth défini sur la ressource Azure Bot. |
OBOConnectionName |
Non (OBO uniquement) | chaine | Nom d’une connexion du Kit de développement logiciel (SDK) Assistants utilisée pour effectuer un échange de jetons on-Behalf-Of. |
OBOScopes |
Non (OBO uniquement) | string[] | Étendues demandées lors de l’échange OBO. S’il est omis avec OBOConnectionName, vous pouvez appeler ExchangeTurnTokenAsync manuellement. |
Title |
Non | chaine | Titre de la carte de connexion personnalisée. Valeur par défaut définie sur Se connecter. |
Text |
Non | chaine | Texte du bouton de la carte de connexion. Valeur par défaut définie sur Veuillez vous connecter. |
InvalidSignInRetryMax |
Non | int | Nombre maximal de tentatives autorisées lorsque l’utilisateur saisit un code invalide. La valeur par défaut est 2. |
InvalidSignInRetryMessage |
Non | chaine | Message affiché après une entrée de code non valide. Par défaut , le code de connexion est non valide. Veuillez entrer le code à 6 chiffres. |
Timeout |
Non | int (ms) | Nombre de millisecondes avant l’expiration d’une tentative de connexion en cours. La durée par défaut est de 900 000 (15 minutes). |
Note
AzureBotOAuthConnectionName, OBOConnectionName, OBOScopes, Title, et Text appliquez les trois langages (à l’aide de la casse principale par langue décrite dans Prise en charge linguistique pour OAuth).
InvalidSignInRetryMax, InvalidSignInRetryMessage et Timeout sont les paramètres .NET.
Quel type devez-vous utiliser ?
Utilisez le tableau suivant pour décider quelle approche convient à votre situation.
| Option | Cas d’utilisation |
|---|---|
| Connexion automatique (.NET uniquement) | Vous souhaitez que chaque activité entrante acquière automatiquement un jeton, ou vous souhaitez un sous-ensemble filtré (par exemple, uniquement les messages ou tout sauf les événements) en fournissant un prédicat à UserAuthorizationOptions.AutoSignIn. Pris en charge uniquement en .NET. |
| Par route | Seuls des gestionnaires de routes spécifiques ont besoin de jetons, ou des routes différentes doivent utiliser des connexions OAuth différentes (et donc des jetons différents). Cette option est la seule en JavaScript et Python. En .NET, ceci est cumulatif avec la connexion automatique globale. Si les deux sont activés en .NET, l’activité a accès aux jetons de chacune. |
Utiliser le jeton dans le code (non-OBO)
Cette section explique comment récupérer et utiliser le jeton utilisateur retourné directement par votre connexion OAuth du bot Azure sans effectuer un échange d’authentification déléguée. En .NET, vous pouvez utiliser la connexion automatique globale ou des gestionnaires par route. JavaScript et Python utilisent uniquement des gestionnaires par route. Dans votre gestionnaire d’activité, récupérez le jeton (GetTurnTokenAsync en .NET, authorization.getToken en JavaScript, auth.get_token en Python) le plus tard possible afin que le kit de développement logiciel puisse actualiser le jeton s’il est proche de l’expiration. Les exemples suivants illustrent les deux approches.
Connexion automatique (.NET uniquement)
Note
La connexion automatique globale et DefaultHandlerName sont disponibles uniquement en .NET. Pour JavaScript et Python, utilisez la configuration par route.
Utilisez cette configuration lorsque la connexion automatique globale doit acquérir un jeton pour chaque activité entrante sans avoir à spécifier de gestionnaires par routage.
"AgentApplication": {
"UserAuthorization": {
"DefaultHandlerName": "auto",
"Handlers": {
"auto": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
}
}
}
}
},
Votre code d’assistant ressemblerait à ceci :
public class MyAgent : AgentApplication
{
[MessageRoute]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
var token = await UserAuthorization.GetTurnTokenAsync(turnContext);
// use the token
}
}
Configuration par route
Utilisez une configuration par route lorsque vous souhaitez un contrôle précis : seules les routes que vous marquez explicitement acquièrent des jetons. La configuration par route présente les avantages suivants :
- Cela réduit la récupération inutile de jetons.
- Il permet à différentes routes de cibler des connexions OAuth distinctes (et donc des ressources ou des périmètres différents).
- Cela permet de mélanger des routes authentifiées et non authentifiées au sein d’un même assistant.
Dans l’exemple suivant, un seul graph gestionnaire est attaché uniquement à la route du message.
Dans .NET, la connexion automatique globale est désactivée et le gestionnaire graph est attaché à la route en utilisant autoSignInHandlers.
"AgentApplication": {
"UserAuthorization": {
"AutoSignIn": false,
"Handlers": {
"graph": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
}
}
}
}
},
Votre code d’assistant ressemblerait à ceci :
public class MyAgent : AgentApplication
{
[MessageRoute(autoSignInHandlers: "graph")]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "graph");
// use the token
}
}
En JavaScript, passez une table de noms de gestionnaires comme dernier argument à l’enregistrement de la route. Seul cette route déclenche la connexion pour le gestionnaire graph.
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__title=Graph Sign In
AgentApplication__UserAuthorization__Handlers__graph__Settings__text=Sign in with Microsoft Graph
Votre code d’assistant ressemblerait à ceci :
class MyAgent extends AgentApplication {
constructor () {
super({ storage: new MemoryStorage() })
// the `graph` handler runs only for this route
this.onMessage('-me', this._profileRequest, ['graph'])
}
private _profileRequest = async (context, state) => {
const tokenResponse = await this.authorization.getToken(context, 'graph')
// use tokenResponse.token
}
}
En Python, passez auth_handlers au décorateur de route. Seul cette route déclenche la connexion pour le gestionnaire GRAPH.
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
Votre code d’assistant ressemblerait à ceci :
@AGENT_APP.message(re.compile(r"^/(me|profile)$", re.IGNORECASE), auth_handlers=["GRAPH"])
async def profile_request(context: TurnContext, state: TurnState) -> None:
token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
# use token_response.token
Récupérer le jeton pendant un tour
Récupérez le jeton utilisateur chaque fois que vous en avez besoin pendant un tour. Vous pouvez l’appeler plusieurs fois. Appelez-la immédiatement avant utilisation afin que la logique de rafraîchissement (si nécessaire) soit gérée de manière transparente.
| Langage | Appel |
|---|---|
| .NET |
GetTurnTokenAsync(turnContext, handlerName) |
| JavaScript | authorization.getToken(context, handlerName) |
| Python | auth.get_token(context, handler_name) |
Utiliser le jeton dans le code (OBO)
On-Behalf-Of (OBO) repose sur le retour initial de la connexion utilisateur d’un jeton échangeable. Cela nécessite que les scopes de la connexion OAuth incluent un scope correspondant à un scope exposé par l’API en aval (par exemple, si le scope exposé est defaultScopes, le scope configuré pourrait être api://botid-{{clientId}}/defaultScopes). Le SDK Agents effectue alors un échange via la Microsoft Authentication Library (MSAL) en utilisant une connexion configurée identifiée par OBOConnectionName et la liste de OBOScopes. Lorsque OBOConnectionName et OBOScopes sont présents en configuration, l’échange se produit automatiquement et vous obtenez le jeton final via l’appel de jeton standard (GetTurnTokenAsync / getToken / get_token). Si l’un des deux est manquant, vous pouvez effectuer l’échange explicitement pendant l’exécution (ExchangeTurnTokenAsync en .NET, authorization.exchangeToken en JavaScript, auth.exchange_token en Python), ce qui vous permet de déterminer dynamiquement la connexion ou la liste de portées.
OBO dans la configuration
Utilisez ce modèle lorsque vous connaissez la ressource en aval et les portées nécessaires au moment de la configuration. Lorsque vous fournissez à la fois OBOConnectionName et OBOScopes, le Kit de développement logiciel (SDK) effectue automatiquement l’échange On-Behalf-Of pendant la connexion. Cela signifie que les appels suivants au jeton standard retournent directement le jeton OBO, sans qu’aucun code supplémentaire à l’exécution ne soit nécessaire.
"AgentApplication": {
"UserAuthorization": {
"AutoSignIn": false,
"Handlers": {
"graph": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
"OBOConnectionName": "ServiceConnection",
"OBOScopes": [
"https://graph.microsoft.com/.default"
]
}
}
}
}
},
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "FederatedCredentials",
"AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
"ClientId": "{{ClientId}}",
"FederatedClientId": "{{ManagedIdentityClientId}}",
"Scopes": [
"https://api.botframework.com/.default"
]
}
}
},
Votre code d’assistant ressemblerait à ceci :
public class MyAgent : AgentApplication
{
[MessageRoute(autoSignInHandlers: "graph")]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
// returns the OBO token because OBOConnectionName and OBOScopes are configured
var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "graph");
// use the token
}
}
En JavaScript, définissez une connexion au nom de dans la carte des connexions et référencez-la depuis le gestionnaire avec oboConnectionName et oboScopes.
# Agent's own connection
connections__serviceConnection__settings__clientId=
connections__serviceConnection__settings__clientSecret=
connections__serviceConnection__settings__tenantId=
# OBO connection
connections__oboConnection__settings__clientId=
connections__oboConnection__settings__clientSecret=
connections__oboConnection__settings__tenantId=
connectionsMap__0__connection=serviceConnection
connectionsMap__0__serviceUrl=*
connectionsMap__1__connection=oboConnection
connectionsMap__1__serviceUrl=obo
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=oboConnection
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboScopes=https://graph.microsoft.com/.default
Votre code d’assistant ressemblerait à ceci :
class MyAgent extends AgentApplication {
constructor () {
super({ storage: new MemoryStorage() })
this.onActivity('message', this._onMessage, ['graph'])
}
private _onMessage = async (context, state) => {
// returns the OBO token because oboConnectionName and oboScopes are configured
const tokenResponse = await this.authorization.getToken(context, 'graph')
// use tokenResponse.token
}
}
En Python, définissez une connexion au nom de sous CONNECTIONS et référencez-la depuis le gestionnaire avec OBOCONNECTIONNAME et OBOSCOPES.
# Agent's own connection
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=
# OBO connection
CONNECTIONS__OBO__SETTINGS__CLIENTID=
CONNECTIONS__OBO__SETTINGS__CLIENTSECRET=
CONNECTIONS__OBO__SETTINGS__TENANTID=
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__OBOCONNECTIONNAME=OBO
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__GRAPH__SETTINGS__OBOSCOPES=https://graph.microsoft.com/.default
Votre code d’assistant ressemblerait à ceci :
@AGENT_APP.message(re.compile(r".*"), auth_handlers=["GRAPH"])
async def on_message(context: TurnContext, state: TurnState) -> None:
# returns the OBO token because OBOCONNECTIONNAME and OBOSCOPES are configured
token_response = await AGENT_APP.auth.get_token(context, "GRAPH")
# use token_response.token
OBO Exchange au moment de l’exécution
Utilisez un échange à l’exécution lorsque vous ne pouvez pas corriger la ressource en aval, les étendues ou la connexion dans la configuration. Cette situation se produit, par exemple, lorsque les périmètres dépendent du client, du rôle utilisateur ou d’un indicateur de fonctionnalité. Dans ce modèle, vous configurez optionnellement la connexion au nom de, puis appelez la méthode d’échange avec les étendues que vous décidez au moment du tour. Vous recevez un jeton échangé que vous pouvez immédiatement appliquer.
Appelez ExchangeTurnTokenAsync avec les étendues que vous sélectionnez au tour.
"AgentApplication": {
"UserAuthorization": {
"AutoSignIn": false,
"Handlers": {
"graph": {
"Settings": {
"AzureBotOAuthConnectionName": "teams_sso",
"OBOConnectionName": "ServiceConnection"
}
}
}
}
},
"Connections": {
"ServiceConnection": {
"Settings": {
"AuthType": "FederatedCredentials",
"AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
"ClientId": "{{ClientId}}",
"FederatedClientId": "{{ManagedIdentityClientId}}",
"Scopes": [
"https://api.botframework.com/.default"
]
}
}
},
Votre code d’assistant ressemblerait à ceci :
public class MyAgent : AgentApplication
{
[MessageRoute(autoSignInHandlers: "graph")]
public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
var scopes = GetScopes();
var exchangedToken = await UserAuthorization.ExchangeTurnTokenAsync(turnContext, "graph", exchangeScopes: scopes);
// use the token
}
}
Appelez authorization.exchangeToken avec le nom du gestionnaire et les étendues que vous sélectionnez au tour.
connections__oboConnection__settings__clientId=
connections__oboConnection__settings__clientSecret=
connections__oboConnection__settings__tenantId=
connectionsMap__1__connection=oboConnection
connectionsMap__1__serviceUrl=obo
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=teams_sso
AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=oboConnection
Votre code d’assistant ressemblerait à ceci :
class MyAgent extends AgentApplication {
constructor () {
super({ storage: new MemoryStorage() })
this.onActivity('message', this._onMessage, ['graph'])
}
private _onMessage = async (context, state) => {
const scopes = getScopes()
const exchangedToken = await this.authorization.exchangeToken(context, 'graph', { scopes })
// use exchangedToken.token
}
}
Appelez auth.exchange_token avec les étendues que vous sélectionnez au tour et le nom du gestionnaire.
CONNECTIONS__MCS__SETTINGS__CLIENTID=
CONNECTIONS__MCS__SETTINGS__CLIENTSECRET=
CONNECTIONS__MCS__SETTINGS__TENANTID=
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=teams_sso
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__MCS__SETTINGS__OBOCONNECTIONNAME=MCS
Votre code d’assistant ressemblerait à ceci :
@AGENT_APP.message(re.compile(r".*"), auth_handlers=["MCS"])
async def on_message(context: TurnContext, state: TurnState) -> None:
scopes = get_scopes()
token_response = await AGENT_APP.auth.exchange_token(context, scopes, "MCS")
# use token_response.token
Paramètres régionaux OAuth
Pour les régions hors États-Unis, mettez à jour le point de terminaison du service de jeton utilisé par votre assistant.
L’exemple suivant montre la configuration .NET. Ajoutez-le à appsettings.json :
"RestChannelServiceClientFactory": {
"TokenServiceEndpoint": "{{service-endpoint-uri}}"
}
Pour service-endpoint-url, utilisez la valeur appropriée du tableau suivant pour les bots de cloud public dont les données résident dans la région spécifiée.
| URI | Région |
|---|---|
https://europe.api.botframework.com |
Europe |
https://unitedstates.api.botframework.com |
États-Unis |
https://india.api.botframework.com |
Inde |