将 .NET 智能体配置为使用 OAuth

首先在 Azure 机器人资源和相应的应用注册上为 .NET 智能体预配 OAuth。 然后,代理运行时通过 AgentApplication 自动登录功能为你获取和刷新用户令牌。 自动登录可以全局运行(所有活动类型)或有选择地运行,以便仅某些路由或活动类型请求令牌。

可以在配置中注册多个 OAuth 处理程序并将其分配给特定路由,或声明随处使用的单个默认处理程序。 在 Azure 端为它配置时,每个处理程序可以选择执行代表 (OBO) 交换。 有关快速入门,请参阅 AutoSignIn 示例,或将此处所示配置集成到现有代理中。

以下各节介绍如何配置 UserAuthorization,如何确定采用全局性还是按路由方法,以及如何在轮次中检索令牌(标准和 OBO)。 还为非美国部署提供了区域指南。

.NET 代理可以通过“appsettings”中进行配置,或通过代码进行配置 Program.cs。 本文档详细介绍了如何使用 appsettings。

设置

位于UserAuthorizationAgentApplication内部的对象控制如何获取用户令牌。 以下 JSON 显示分层结构,后面是描述UserAuthorization的每个属性以及嵌套Settings对象的表。

  "AgentApplication": {
    "UserAuthorization": {
      "DefaultHandlerName": "{{handler-name}}",
      "AutoSignIn": true | false,
      "Handlers": {
        "{{handler-name}}": {
          "Settings": {
            "AzureBotOAuthConnectionName": "{{azure-bot-connection-name}}",
            "OBOConnectionName": "{{connection-name}}",
            "OBOScopes": [
              "{{obo-scope}}"
            ],
            "Title": "{{signin-card-title}}",
            "Text": "{{signin-card-button-text}}",
            "InvalidSignInRetryMax": {{int}},
            "InvalidSignInRetryMessage": {{invalid-attempt-message}},
            "Timeout": {{timeout-ms}}
          }
        }
      }
    }
  }

用户授权属性

下表列出了顶级 UserAuthorization 属性,这些属性确定如何选择处理程序以及如何为每个传入活动获取令牌。

资产 必选 类型 Description
DefaultHandlerName 否(建议) 字符串 AutoSignIn 计算结果为 true 且未指定按路由替代时使用的处理程序名称。
AutoSignIn bool 或 delegate 如果设置为 true(默认值),代理会尝试为每个传入的活动获取令牌;可以在运行时使用 Options.AutoSignIn 来过滤活动类型。
Handlers 是(至少一个) 对象(字典) 将处理程序名称映射到其配置。 每个键必须是唯一的。

若要限制自动登录适用的活动,请设置类似于: Options.AutoSignIn = (context, ct) => Task.FromResult(context.Activity.IsType(ActivityTypes.Message));的谓词。

设置属性

下表描述了应用于单个 OAuth 处理程序的嵌套 Settings 对象,控制登录卡演示文稿、重试行为、超时和可选的 OBO 交换配置。

资产 必选 类型 Description
AzureBotOAuthConnectionName 是的 字符串 在 Azure 机器人工具资源上定义的 OAuth 连接名称。
OBOConnectionName 否(仅限 OBO) 字符串 用于执行代表令牌交换的智能体 SDK 连接的名称。
OBOScopes 否(仅限 OBO) 字符串[] 在 OBO 交换期间请求的范围;如果 OBOConnectionName 省略,可以手动调用 ExchangeTurnTokenAsync
Title 字符串 自定义登录卡标题;默认为 “登录”。
Text 字符串 登录卡按钮文本;默认值为 “请登录”。
InvalidSignInRetryMax int 当用户输入无效代码时允许的最大重试次数;默认值为 2。
InvalidSignInRetryMessage 字符串 无效代码条目后显示的消息;默认为 “登录代码无效”。请输入 6 位代码。
Timeout int(毫秒) 正在进行的登录尝试过期之前的毫秒数。 默认值为 900000(15 分钟)。

要使用哪种类型?

使用下表确定哪种方法适合你的方案。

选择 使用场景
自动登录 您希望每个传入活动自动获取令牌,或者希望筛选的子集(例如,仅消息或除活动外的所有项目)通过向 UserAuthorizationOptions.AutoSignIn 提供谓词来获取。
按路由 只有特定的路由处理程序需要令牌或不同的路由才能使用不同的 OAuth 连接(因此使用不同的令牌)。 这是全局自动登录的附加功能。 如果两者都已启用,轮次有权访问每种方式的令牌。

使用代码中的令牌(非 OBO)

本节介绍如何检索和使用 Azure 机器人 OAuth 连接直接返回的用户令牌,而无需执行代表交换。 首先确定是首选全局自动登录还是按路由处理程序;然后,在活动处理程序中,尽可能晚调用 UserAuthorization.GetTurnTokenAsync ,以便 SDK 可以在令牌即将过期时刷新令牌。 以下示例说明了这两种模式。

仅自动登录配置

当需要全局自动登录为每个传入活动获取令牌,并且无需为每个路由指定处理程序时,请使用此配置。

  "AgentApplication": {
    "UserAuthorization": {
      "DefaultHandlerName": "auto",
      "Handlers": {
        "auto": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
          }
        }
      }
    }
  },

您的智能体代码如下所示:

public class MyAgent : AgentApplication
{
    public MyAgent(AgentApplicationOptions options) : base(options)
    {
        OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
    }

    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var token = await UserAuthorization.GetTurnTokenAsync(turnContext);

        // use the token 
    }
}

仅按路由配置

如果需要精细控制,使用按路由配置:只有您显式标记的路由会获取令牌。 每个路由配置可减少不必要的令牌检索,允许不同的路由以不同的 OAuth 连接(因此不同的资源或范围)为目标,并允许在同一代理中混合经过身份验证和未经身份验证的路由。 在以下示例中,全局自动登录处于禁用状态,并且单个 messageOauth 处理程序仅附加到消息路由。

  "AgentApplication": {
    "UserAuthorization": {
      "AutoSignIn": false,
      "Handlers": {
        "messageOauth": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
          }
        }
      }
    }
  },

您的智能体代码如下所示:

public class MyAgent : AgentApplication
{
    public MyAgent(AgentApplicationOptions options) : base(options)
    {
        OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last, autoSignInHandlers: ["messageOauth"]);
    }

    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var token = await UserAuthorization.GetTurnTokenAsync(turnContext, "messageOauth");

        // use the token
    }
}

使用 GetTurnTokenAsync

每当在回合中需要用户令牌时,调用 GetTurnTokenAsync。 可以多次调用它。 使用前立即调用它,以便以透明方式处理刷新逻辑(如有必要)。

使用代码中的令牌 (OBO)

On-Behalf-Of(OBO)依赖于初始用户登录返回的可交换令牌。 这要求 OAuth 连接的范围包含一个与下游 API 公开的范围相对应的范围(例如,如果公开的范围是 defaultScopes,则配置的作用域可能是 api://botid-{{clientId}}/defaultScopes)。 然后,代理 SDK 使用由 OBOConnectionName 标识的已配置连接和 OBOScopes 列表执行 Microsoft 身份验证库(MSAL)交换。 当OBOConnectionNameOBOScopes都存在于配置中时,交换会自动发生,通过GetTurnTokenAsync获取最终令牌。 如果缺少其中任何一项,您可以在运行时显式使用 ExchangeTurnTokenAsync 执行交换,从而动态地解析连接或范围列表。

配置中的 OBO

了解配置时所需的下游资源和范围时,请使用此模式。 当您同时提供 OBOConnectionNameOBOScopes 时,SDK 会在登录过程中自动执行代表交换。 这意味着后续调用 GetTurnTokenAsync 会直接返回 OBO 令牌,无需额外的运行时代码。

  "AgentApplication": {
    "UserAuthorization": {
      "DefaultHandlerName": "auto",
      "Handlers": {
        "auto": {
          "Settings": {
            "AzureBotOAuthConnectionName": "teams_sso",
            "OBOConnectionName": "ServiceConnection",
            "OBOScopes": [
              "https://myservicescope.com/.default"
            ]
          }
        }
      }
    }
  },
  "Connections": {
    "ServiceConnection": {
      "Settings": {
        "AuthType": "FederatedCredentials",
        "AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
        "ClientId": "{{ClientId}}",
        "FederatedClientId": "{{ManagedIdentityClientId}}",
        "Scopes": [
          "https://api.botframework.com/.default"
        ]
      }
    }
  },

您的智能体代码如下所示:

public class MyAgent : AgentApplication
{
    public MyAgent(AgentApplicationOptions options) : base(options)
    {
        OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
    }

    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var token = await UserAuthorization.GetTurnTokenAsync(turnContext);

        // use the token
    }
}

运行时的 OBO 交换

当下游资源、作用域或必须使用的连接无法在配置中预先设定时(例如,当这些作用域依赖于租户、用户角色或功能标志时),应使用运行时动态交换机制。 在此模型中,将配置(可选)OBOConnectionName,然后使用您在轮次时确定的范围调用 ExchangeTurnTokenAsync,接收可以立即应用的交换令牌。

  "AgentApplication": {
    "UserAuthorization": {
      "DefaultHandlerName": "auto",
      "Handlers": {
        "auto": {
          "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"
        ]
      }
    }
  },

您的智能体代码如下所示:

public class MyAgent : AgentApplication
{
    public MyAgent(AgentApplicationOptions options) : base(options)
    {
        OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
    }

    public async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
    {
        var scopes = GetScopes();

        var exchangedToken = await UserAuthorization.ExchangeTurnTokenAsync(turnContext, exchangeScopes: scopes);

        // use the token
    }
}

区域 OAuth 设置

对于非美国地区,您需要更新您的代理所使用的令牌服务端点。

请将以下内容添加到 appsettings.json

"RestChannelServiceClientFactory": {
   "TokenServiceEndpoint": "{{service-endpoint-uri}}"
}

对于 service-endpoint-url,对于位于指定区域内具有数据驻留的公有云机器人,使用下表中的相应值。

URI 区域
https://europe.api.botframework.com 欧洲
https://unitedstates.api.botframework.com 美国
https://india.api.botframework.com 印度