生成基于 API 的消息扩展

注意

基于 API 的消息扩展仅支持搜索命令。

基于 API 的消息扩展是 Microsoft Teams 应用功能,可将外部 API 直接集成到 Teams 中,从而增强应用的可用性并提供无缝的用户体验。 基于 API 的消息扩展支持搜索命令,可用于从 Teams 中的外部服务提取和显示数据,通过减少在应用程序之间切换的需要来简化工作流。

在开始之前,请确保满足以下要求:


1. OpenAPI 说明 (OAD)

确保遵循以下有关 OpenAPI 说明 (OAD) 文档的准则:

  • 支持 OpenAPI 版本 2.0 和 3.0.x。
  • JSON 和 YAML 是支持的格式。
  • 请求正文(如果存在)必须为 application/Json。
  • servers.url 属性定义 HTTPS 协议服务器 URL。
  • 仅支持 POST 和 GET HTTP 方法。
  • OpenAPI 说明文档必须具有 operationId
  • 仅允许一个不带默认值的必需参数。
  • 具有默认值的必需参数被视为可选参数。
  • 用户不得为标头或 Cookie 输入参数。
  • 操作不得具有不带默认值的必需标头或 Cookie 参数。
  • 确保 OpenAPI 说明文档中没有远程引用。
  • 不支持为请求构造数组;但是,支持 JSON 请求正文中的嵌套对象。
  • Teams 不支持 oneOf、、 anyOfallOfnot (swagger.io) 构造。

以下代码是 OpenAPI 说明文档的示例:

openapi: 3.0.1
info:
title: OpenTools Plugin
description: A plugin that allows the user to find the most appropriate AI tools for their use cases, with their pricing information.
version: 'v1'
servers:
- url: https://gptplugin.opentools.ai
paths:
/tools:
 get:
   operationId: searchTools
   summary: Search for AI Tools
   parameters:
     - in: query
       name: search
       required: true
       schema:
         type: string
       description: Used to search for AI tools by their category based on the keywords. For example, ?search="tool to create music" will give tools that can create music.
   responses:
     "200":
       description: OK
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/searchToolsResponse'
     "400":
       description: Search Error
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/searchToolsError'
components:
schemas:
 searchToolsResponse:
   required:
     - search
   type: object
   properties:
     tools:
       type: array
       items:
         type: object
         properties:
           name:
             type: string
             description: The name of the tool.
           opentools_url:
             type: string
             description: The URL to access the tool.
           main_summary:
             type: string
             description: A summary of what the tool is.
           pricing_summary:
             type: string
             description: A summary of the pricing of the tool.
           categories:
             type: array
             items:
               type: string
             description: The categories assigned to the tool.
           platforms:
             type: array
             items:
               type: string
             description: The platforms that this tool is available on.
       description: The list of AI tools.
 searchToolsError:
   type: object
   properties:
     message:
       type: string
       description: Message of the error.

有关详细信息,请参阅 OpenAPI 结构。


2.应用清单

确保遵循以下应用清单指南:

  • 将应用清单版本设置为 1.17

  • 设置为 composeExtensions.composeExtensionTypeapiBased

  • 定义为 composeExtensions.apiSpecificationFile 文件夹中 OpenAPI 说明文件的相对路径。 这会将应用清单链接到 API 规范。

  • 定义为 apiResponseRenderingTemplateFile 响应呈现模板的相对路径。 这将指定用于呈现 API 响应的模板的位置。

  • 每个命令都必须具有指向响应呈现模板的链接。 这会将每个命令连接到其相应的响应格式。

  • Commands.id应用清单中的 属性必须与 OpenAPI 说明中的 匹配operationId

  • 如果所需的参数没有默认值,则应用清单中的命令 parameters.name 必须与 OpenAPI 说明文档中的 匹配 parameters.name

  • 如果没有必需的参数,则应用清单中的命令 parameters.name 必须与 OpenAPI 说明中的 可选 parameters.name 匹配。

  • 确保每个命令的参数与 OpenAPI 规范中为操作定义的参数名称完全匹配。

  • 必须为每个命令定义 响应呈现模板 ,该命令用于转换来自 API 的响应。

  • 完整说明不得超过 128 个字符。

    {
    "$schema": "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.schema.json",
    +  "manifestVersion": "devPreview",
    "version": "1.0.0",
    "id": "04805b4b-xxxx-xxxx-xxxx-4dbc1cac8f89",
    "packageName": "com.microsoft.teams.extension",
    "developer": {
        "name": "Teams App, Inc.",
        "websiteUrl": "https://www.example.com",
        "privacyUrl": "https://www.example.com/termofuse",
        "termsOfUseUrl": "https://www.example.com/privacy"
    },
    "icons": {
        "color": "color.png",
        "outline": "outline.png"
    },
    "name": {
        "short": "AI tools",
        "full": "AI tools"
    },
    "description": {
        "short": "AI tools",
        "full": "AI tools"
    },
    "accentColor": "#FFFFFF",
    "composeExtensions": [
        {
    +      "composeExtensionType": "apiBased",
    +      "authorization": {
    +        "authType": "apiSecretServiceAuth ",
    +        "apiSecretServiceAuthConfiguration": {
    +            "apiSecretRegistrationId": "96270b0f-7298-40cc-b333-152f84321813"
    +        }
    +      },
    +      "apiSpecificationFile": "aitools-openapi.yml",
           "commands": [
           {
              "id": "searchTools",
              "type": "query",
              "context": [
                 "compose",
                 "commandBox"
              ],
              "title": "search for AI tools",
              "description": "search for AI tools",
              "parameters": [
                 {
                 "name": "search",
                 "title": "search query",
                 "description": "e.g. search='tool to create music'"
                 }
              ],
    +          "apiResponseRenderingTemplateFile": "response-template.json"
           }
           ]
        }
    ],
    "validDomains": []
    }
    

参数

名称 说明
composeExtensions.composeExtensionType 撰写扩展类型。 将值更新为 apiBased
composeExtensions.authorization 基于 API 的消息扩展的授权相关信息
composeExtensions.authorization.authType 可能授权类型的枚举。 支持的值为 noneapiSecretServiceAuthmicrosoftEntra
composeExtensions.authorization.apiSecretServiceAuthConfiguration 对象捕获执行服务身份验证所需的详细信息。仅当身份验证类型为 apiSecretServiceAuth时适用。
composeExtensions.authorization.apiSecretServiceAuthConfiguration.apiSecretRegistrationId 开发人员通过开发人员门户提交 API 密钥时返回的注册 ID。
composeExtensions.apiSpecificationFile 引用应用包中的 OpenAPI 说明文件。 类型为 apiBased时包括 。
composeExtensions.commands.id 分配给搜索命令的唯一 ID。 用户请求包含此 ID。 ID 必须与 OpenAPI 说明中提供的 匹配 OperationId
composeExtensions.commands.context 定义消息扩展入口点的数组。 默认值为 composecommandBox
composeExtensions.commands.parameters 定义命令的参数的静态列表。 名称必须映射到 parameters.name OpenAPI 说明中的 。 如果要引用请求正文架构中的属性,则该名称必须映射到 properties.name 或 查询参数。
composeExtensions.commands.apiResponseRenderingTemplateFile 用于格式化从开发人员 API 到自适应卡片响应的 JSON 响应的模板。 [必需]

有关详细信息,请参阅 composeExtensions


3. 响应呈现模板

注意

Teams 支持最高版本 1.5 的自适应卡片,自适应卡片Designer支持最高版本 1.6。

  • 在 属性中$schema定义架构引用 URL,以建立模板的结构。
  • 支持的值responseLayoutgridlist ,确定响应的视觉呈现方式。
  • jsonPath自适应卡片的数据不是根对象时,建议对数组使用 。 例如,如果数据嵌套在 下 productDetails,则 JSON 路径为 productDetails
  • 定义为 jsonPath API 响应中相关数据或数组的路径。 如果路径指向数组,则数组中的每个条目都与自适应卡片模板绑定,并作为单独的结果返回。 [可选]
  • 获取 用于验证响应呈现模板的示例响应。 这可用作一个测试,以确保模板按预期工作。
  • 使用 Fiddler 或 Postman 等工具 调用 API 并确保请求和响应有效。 此步骤对于排查和确认 API 正常运行至关重要。
  • 可以使用自适应卡片Designer将 API 响应绑定到响应呈现模板并预览自适应卡片。 在 卡片有效负载编辑器 中插入模板,并在示例 数据编辑器中插入示例响应条目。

以下代码是响应呈现模板的示例:

响应呈现模板示例
{
"version": "1.0",
"jsonPath": "repairs",
"responseLayout": "grid",
"responseCardTemplate": {
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.4",
  "body": [
    {
      "type": "Container",
      "items": [
        {
          "type": "ColumnSet",
          "columns": [
            {
              "type": "Column",
              "width": "stretch",
              "items": [
                {
                  "type": "TextBlock",
                  "text": "Title: ${if(title, title, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "TextBlock",
                  "text": "Description: ${if(description, description, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "TextBlock",
                  "text": "Assigned To: ${if(assignedTo, assignedTo, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "Image",
                  "url": "${image}",
                  "size": "Medium",
                  "$when": "${image != null}"
                }
              ]
            },
            {
              "type": "Column",
              "width": "auto",
              "items": [
                {
                  "type": "Image",
                  "url": "${if(image, image, '')}",
                  "size": "Medium"
                }
              ]
            }
          ]
        },
        {
          "type": "FactSet",
          "facts": [
            {
              "title": "Repair ID:",
              "value": "${if(id, id, 'N/A')}"
            },
            {
              "title": "Date:",
              "value": "${if(date, date, 'N/A')}"
            }
          ]
        }
      ]
    }
  ]
  },
  "previewCardTemplate": {
  "title": "Title: ${if(title, title, 'N/A')}",
  "subtitle": "Description: ${if(description, description, 'N/A')}",
  "text": "Assigned To: ${if(assignedTo, assignedTo, 'N/A')}",
  "image": {
    "url": "${image}",
    "$when": "${image != null}"
    }
  }
 }

预览卡

屏幕截图显示了在搜索特定单词时显示预览卡片数组的 compose 扩展的示例。在这种情况下,在“SME 测试应用”中搜索“a”将返回五张卡片,其中显示“Title”、“Description” (截断) 和“AssignedTo”属性和值。

扩展的自适应卡片

用户选择预览版卡后自适应卡片的外观示例。自适应卡片显示 Title、完整的 Description、AssignedTo、RepairId 和 Date 值。

参数

属性 类型 说明 必需
version string 当前响应呈现模板的架构版本。
jsonPath string responseCardTemplate 和 previewCardTemplate 应应用到的结果中相关节的路径。 如果未设置,则根对象被视为相关部分。 如果相关部分是数组,则每个条目将映射到 responseCardTemplate 和 previewCardTemplate。
responseLayout responseLayoutType 指定消息扩展浮出控件中结果的布局。 支持的类型为 listgrid
responseCardTemplate adaptiveCardTemplate 用于从结果条目创建自适应卡片的模板。
previewCardTemplate previewCardTemplate 用于从结果条目创建预览卡的模板。 生成的预览卡显示在消息扩展浮出控件菜单中。

Json 路径

JSON 路径是可选的,但应用于数组,或者用作自适应卡数据的对象不是根对象。 JSON 路径应遵循 Newtonsoft 定义的格式。 如果 JSON 路径指向数组,则该数组中的每个条目都与自适应卡模板绑定,并返回为单独的结果。

例子假设你具有以下产品列表的 JSON,并且你希望为每个条目创建卡结果。

{
   "version": "1.0",
   "title": "All Products",
   "warehouse": {
      "products": [
        ...
      ]
   }
}

可以看到,结果数组位于“products”下,嵌套在“warehouse”下,因此 JSON 路径为“warehouse.products”。

使用 https://adaptivecards.io/designer/ 通过将模板插入卡片有效负载编辑器来预览自适应卡,并从数组或对象获取示例响应条目,并将其插入右侧的“相同数据”编辑器中。 确保卡正确呈现,并且符合你的喜好。 请注意,Teams 支持最高版本 1.5 的卡片,而设计器支持 1.6。

架构映射

OpenAPI 说明文档中的属性映射到自适应卡片模板,如下所示:

  • stringnumberinteger类型 boolean 将转换为 TextBlock。

    例子
    • 源架构stringnumberintegerboolean

       name:
         type: string
         example: doggie
      
    • 目标架构Textblock

      {
      "type": "TextBlock",
      "text": "name: ${if(name, name, 'N/A')}",
      "wrap": true
      }
      
  • array:数组转换为自适应卡内的容器。

    例子
    • 源架构array

          type: array
                    items:
                    required:
                      - name
                    type: object
                      properties:
                      id:
                        type: integer
                      category:
                        type: object
                        properties:
                        name:
                          type: string
      
    • 目标架构Container

          {
                    "type": "Container",
                    "$data": "${$root}",
                    "items": [
                      {
                        "type": "TextBlock",
                        "text": "id: ${if(id, id, 'N/A')}",
                        "wrap": true
                      },
                      {
                        "type": "TextBlock",
                        "text": "category.name: ${if(category.name, category.name, 'N/A')}",
                        "wrap": true
                      }
                    ]
                  }
      
      
  • object:对象转换为自适应卡片中的嵌套属性。

    例子
    • 源架构object

      components:
        schemas:
          Pet:
              category:
                type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
      
      
    • 目标架构:自适应卡片中的嵌套属性

      {
        "type": "TextBlock",
        "text": "category.id: ${if(category.id, category.id, 'N/A')}",
        "wrap": true
      },
      {
        "type": "TextBlock",
        "text": "category.name: ${if(category.name, category.name, 'N/A')}",
        "wrap": true
      }
      
      
  • image:如果属性是图像 URL,则它将转换为自适应卡片中的 Image 元素。

    例子
    • 源架构image

          image:
            type: string
            format: uri
            description: The URL of the image of the item to be repaired
      
      
    • 目标架构"Image"

      {
            "type": "Image",
            "url": "${image}",
            "$when": "${image != null}"
          }
      
      

身份验证

可以在基于 API 的消息扩展中实现身份验证,以提供对应用程序的安全和无缝访问。 如果消息扩展需要身份验证,请在应用清单的 下composeExtensions添加 authorization 属性,并通过在 下authorization设置 authType 属性来定义应用程序的身份验证类型。 若要为消息扩展启用身份验证,请使用以下任一身份验证方法更新应用清单:

没有

如果消息扩展不需要用户进行任何身份验证即可访问 API,则可以在基于 API 的消息扩展中更新 none 为 的值 authorization

    "authorization": {
      "authType": "none"
      }
    },

机密服务身份验证

API 机密服务身份验证是应用使用 API 进行身份验证的安全方法。 可以通过 Teams 开发人员门户 注册 API 密钥 ,并生成 API 密钥注册 ID。 使用 具有 属性的 apiSecretServiceAuthConfiguration 对象apiSecretRegistrationId更新应用清单。 此属性应包含通过门户提交 API 密钥时返回的引用 ID。

启动 API 请求时,系统会从安全存储位置检索 API 密钥,并使用持有者令牌方案将其包含在授权标头中。 API 终结点在收到请求后验证 API 密钥的有效性。 如果验证成功,终结点将处理请求并返回所需的响应,确保只有经过身份验证的请求才能访问 API 的资源。

注册 API 密钥

通过 API 密钥注册,可以保护身份验证背后的 API,并在消息扩展中使用。 可以注册 API 密钥,并指定可以访问 API 的域、租户和应用,并提供对 API 调用进行身份验证所需的机密。 然后,可以将 API 密钥 ID 粘贴到简化的消息扩展中,API 密钥 ID 为身份验证背后的 API 调用启用身份验证。

若要注册 API 密钥,请执行以下步骤:

  1. 转到 “工具 API>密钥注册”。

    屏幕截图显示了 Teams 开发人员门户中的 API 密钥注册选项。

  2. 选择“ + 新建 API 密钥”。

  3. “API 密钥注册 ”页的 “注册 API 密钥”下,更新以下内容:

    1. 说明:API 密钥的说明。

    2. 添加域:更新 API 终结点的基路径。 路径必须是安全的 HTTPS URL,包括完全限定的域名,并且可以选择性地包含特定路径。 例如,https://api.yelp.com

      屏幕截图显示了 Teams 开发人员门户的 API 密钥注册页中的“说明”和“添加域”选项。

  4. “设置目标租户”下,选择以下任一项:

    • 家庭租赁
    • 任何租户
    选项 何时使用 说明
    家庭租户 在租户中开发应用并将应用测试为组织生成的自定义应用或自定义应用时。 API 密钥仅在注册 API 的租户中可用。
    任何租户 完成应用测试并想要跨不同租户启用应用后。 在将应用包提交到合作伙伴中心之前,请确保将目标租户更新为 “任何租户 ”。 应用在 Teams 应用商店中可用后,可以在其他租户中使用 API 密钥。

    屏幕截图显示了 Teams 开发人员门户中“设置目标租户”标题下的“主页租户”和“任何租户”选项。

  5. “设置 Teams 应用”下,选择以下任一项:

    • 任何 Teams 应用
    • 现有 Teams 应用 ID
    选项 何时使用 说明
    任何 Teams 应用 在租户中开发应用并将应用测试为组织生成的自定义应用或自定义应用时。 API 密钥可用于任何 Teams 应用。 当为组织生成的自定义应用或自定义应用在应用上传后生成了 ID 时,此功能非常有用。
    现有 Teams 应用 ID 在租户中作为为组织构建的自定义应用或自定义应用完成测试后。更新 API 密钥注册并选择“ 现有 Teams 应用 ”,并输入应用的清单 ID。 现有 Teams 应用 ”选项将 API 机密注册绑定到特定的 Teams 应用。

    屏幕截图显示“在 Teams 开发人员门户”中设置 Teams 应用标题下的“任何 Teams 应用和现有 Teams 应用选项”。

  6. 选择“ + 添加机密”。 此时会显示 “添加 API 密钥 ”对话框。

  7. 输入机密的值,然后选择“ 保存”。

    注意

    • 每个 API 密钥注册最多可以保留两个机密。 如果一个密钥泄露,可以立即将其删除,并允许 Teams 切换到第二个密钥。
    • 机密值必须至少包含 10 个字符,最多 128 个字符。
    • 如果第一个键导致 401 错误,Teams 会自动尝试使用第二个密钥。 它有助于为用户提供不间断的服务,并消除创建新机密期间的任何潜在停机时间。

    屏幕截图显示了“输入此机密的值”选项,用于向 API 密钥添加机密。

将生成 API 密钥注册 ID

屏幕截图显示 Teams 开发人员门户中生成的 API 密钥注册 ID。

复制并保存 API 密钥注册 ID,并将其更新为应用清单中 属性的值 apiSecretRegistrationId

更新应用清单

可以通过配置静态 API 密钥来授权传入服务的请求。 API 密钥将安全地存储并添加到 API 调用。 apiSecretServiceAuthConfiguration通过 Teams 开发人员门户提交 API 密钥时,添加具有 apiSecretRegistrationId 属性的对象,该属性包含引用 ID。 有关详细信息,请参阅 composeExtensions.commands。

"composeExtensions": [
    {
      "composeExtensionType": "apiBased",
      "authorization": {
        "authType": "apiSecretServiceAuth",
        "apiSecretServiceAuthConfiguration": {
            "apiSecretRegistrationId": "9xxxxb0f-xxxx-40cc-xxxx-15xxxxxxxxx3"
        }
      },

Microsoft Entra

microsoftEntra 身份验证方法使用应用用户的 Teams 标识向他们提供对应用的访问权限。 登录到 Teams 的用户无需在 Teams 环境中再次登录到你的应用。 Teams 应用只需获得应用用户的同意,即可从Microsoft Entra ID检索其访问详细信息。 应用用户同意后,他们甚至可以从其他设备访问应用,而无需再次验证。

先决条件

在开始之前,请确保具有以下各项:

  • 具有活动订阅的 Azure 帐户。
  • 基本熟悉Microsoft Entra ID和 Teams 应用开发。

下图显示了当 Teams 应用用户尝试访问 API-bsed 消息扩展应用时 SSO 的工作原理:

屏幕截图显示Microsoft Entra SSO 授权如何用于身份验证 API。

  • 用户从 Teams 中的消息扩展调用基于 API 的消息扩展应用,并请求一个需要身份验证的命令。
  • 应用使用应用 ID 和所需范围 (access_as_user) 向 Teams 后端服务发送请求。
  • Teams 后端服务会检查用户是否同意应用和范围。 否则,它会向用户显示一个同意屏幕,并请求权限。
  • 如果用户同意,Teams 后端服务会为用户和应用生成访问令牌,并将其发送到请求的授权标头中的应用。
  • 应用验证令牌。 用户可以从令牌中提取用户信息,例如名称、电子邮件和对象 ID。
  • 应用可以使用令牌来调用自己的 API。
  • 应用在 Teams 中将响应返回给用户。

若要为基于 API 的消息扩展启用 microsoftEntra 身份验证方法,请执行以下步骤:

在 Microsoft Entra ID 中注册新应用

  1. 在 Web 浏览器上打开 Azure 门户

  2. 选择 应用注册 图标。

    Microsoft Entra 管理中心页。

    将显示 应用注册 页。

  3. 选择 + 新建注册 图标。

    Microsoft Entra 管理中心上的“新建注册”页。

    将显示注册应用程序页。

  4. 输入要显示给应用用户的应用名称。 如果需要,可以在稍后阶段更改名称。

    Microsoft Entra 管理中心上的应用注册页。

  5. 选择可以访问应用的用户帐户的类型。 可以从组织目录中的单个或多租户选项中进行选择,或仅限制对个人 Microsoft 帐户的访问。

    支持的帐户类型的选项
    选项 选择此选项可...
    此组织目录中的帐户仅 (Microsoft - 单租户) 生成仅供租户中(或来宾)用户使用的应用程序。
    此应用通常称为为组织 (LOB 应用) 构建的自定义应用,是Microsoft 标识平台中的单租户应用程序。
    任何组织目录中的帐户 (任何Microsoft Entra ID租户 - 多租户) 允许任何Microsoft Entra租户中的用户使用应用程序。 例如,如果要生成 SaaS 应用程序,并且打算将其提供给多个组织,则此选项适用。
    这种类型的应用在Microsoft 标识平台称为多租户应用程序。
    任何组织目录中的帐户 (任何Microsoft Entra ID租户 - 多租户) 和个人 Microsoft 帐户 (,例如 Skype、Xbox) 面向最广泛的客户集。
    通过选择此选项,你将注册一个多租户应用程序,该应用程序可以支持具有个人 Microsoft 帐户的应用用户。
    仅限个人 Microsoft 帐户 仅为拥有个人 Microsoft 帐户的用户生成应用程序。

    注意

    无需输入 重定向 URI 即可为基于 API 的消息扩展应用启用 SSO。

  6. 选择“注册”。 浏览器上弹出一条消息,指出应用已创建。

    屏幕截图显示了应用注册成功后Azure 门户通知的示例。

    将显示具有应用 ID 和其他配置的页面。

    屏幕截图显示了 Azure 门户 中的应用详细信息页。

  7. 记下并保存 应用程序 (客户端中的应用 ID) ID ,以便稍后更新应用清单。

    你的应用已在 Microsoft Entra ID 中注册。 现在,你已获得基于 API 的消息扩展应用的应用 ID。

配置访问令牌的范围

创建新的应用注册后,配置范围 (权限) 选项,以便将访问令牌发送到 Teams 客户端,以及授权受信任的客户端应用程序启用 SSO。

若要配置范围并授权受信任的客户端应用程序,需要:

  • 添加应用程序 ID URI:为应用配置范围 (权限) 选项。 公开 Web API 并配置应用程序 ID URI。
  • 配置 API 范围:定义 API 的范围,以及可以同意范围的用户。 你只能允许管理员提供对更高特权权限的同意。
  • 配置授权客户端应用程序:为要预授权的应用程序创建授权客户端 ID。 它允许应用用户访问你配置的应用范围(权限),而无需任何进一步的同意。 仅预授权你信任的客户端应用程序,因为应用用户没有机会拒绝同意。

应用程序 ID URI。

  1. 从左窗格中选择“管理>公开 API”。

    随即显示“公开 API”页。

  2. 选择“ 添加 ”,以以 的形式 api://{AppID}生成应用程序 ID URI。

    设置应用 ID URI

    将显示用于设置应用程序 ID URI 的部分。

  3. 按此处介绍的格式输入 应用程序 ID URI

    应用程序 ID URI。

    • 应用程序 ID URI 预填充了应用 ID (GUID) 格式api://{AppID}
    • 应用程序 ID URI 格式必须为: api://fully-qualified-domain-name.com/{AppID}
    • api://{AppID} 之间出入 fully-qualified-domain-name.com(即 GUID)。 例如,api://example.com/{AppID}。

    重要

    • 敏感信息:应用程序 ID URI 记录为身份验证过程的一部分,不得包含敏感信息。

    • 具有多种功能的应用的应用程序 ID URI:如果要生成基于 API 的消息扩展,请输入应用程序 ID URI 作为 api://fully-qualified-domain-name.com/{YourClientId},其中 {YourClientId} 是Microsoft Entra应用 ID。

    • 域名的格式:对域名使用小写字母。 请勿使用大写。

      例如,若要创建具有资源名称的应用服务或 Web 应用,请执行以下操作 demoapplication

      如果使用了基本资源名称 URL 将为... 支持格式...
      demoapplication https://demoapplication.example.net 所有平台
      DemoApplication https://DemoApplication.example.net 仅限桌面、Web 和 iOS。 Android 不支持它。

      使用小写选项 demoapplication 作为基资源名称。

  4. 选择“保存”

    浏览器上弹出一条消息,指出应用程序 ID URI 已更新。

    应用程序 ID URI 消息

    应用程序 ID URI 显示在页面上。

    应用程序 ID URI 已更新

  5. 记下并保存应用程序 ID URI,以便稍后更新应用清单。

配置 API 范围

注意

基于 API 的消息扩展仅支持 access_as_user 范围。

  1. 在此 API 部分定义的“作用域”中选择“+ 添加作用域”。

    选择作用域

    将显示 “添加范围 ”页。

  2. 输入配置范围的详细信息。

    屏幕截图显示如何在 Azure 中添加范围详细信息。

    1. 输入范围名称。 此字段是必需的。
    2. 选择可以对此范围表示同意的用户。 默认选项为 仅限管理员
    3. 输入管理员同意显示名称。 此字段是必需的。
    4. 输入管理员同意的说明。 此字段是必需的。
    5. 输入 用户同意显示名称
    6. 输入用户同意说明。
    7. 为状态选择“启用”选项。
    8. 选择“添加作用域”。

    浏览器上弹出一条消息,指出已添加范围。

    范围添加的消息

    定义的新范围显示在页面上。

    屏幕截图显示了在 Azure 门户 中添加到应用的范围示例。

配置授权的客户端应用程序

  1. 将“公开 API ”页移动到“授权客户端应用程序”部分,然后选择“+ 添加客户端应用程序”。

    授权的客户端应用程序

    将显示“添加客户端应用程序”页。

  2. 为要为应用的 Web 应用程序授权的应用程序输入相应的 Microsoft 365 客户端 ID。

    屏幕截图显示了“客户端 ID”和“授权的范围”选项,用于在 Azure 门户 中将客户端应用程序添加到应用。添加客户端应用程序

    注意

    • 适用于 Teams 的移动、桌面和 Web 应用程序的 Microsoft 365 客户端 ID 是必须添加的实际 ID。
    • 对于基于 Teams API 的消息扩展应用,需要 Web 或 SPA,因为 Teams 中不能有移动或桌面客户端应用程序。
    1. 选择以下客户端 ID 之一:

      使用客户端 ID 用于授权...
      1fec8e78-bce4-4aaf-ab1b-5451cc387264 Teams 移动或桌面应用程序
      5e3ce6c0-2b1f-4285-8d4b-75ee78787346 Teams Web 应用程序
    2. 选择在 授权范围内 为应用创建的应用程序 ID URI,将范围添加到公开的 Web API。

    3. 选择“添加应用程序”

      浏览器上弹出一条消息,指出已添加授权的客户端应用。

      客户端应用程序添加了消息

      授权应用的客户端 ID 显示在页面上。

      添加并显示的客户端应用

注意

可以授权多个客户端应用程序。 重复此过程的步骤以配置另一个已授权的客户端应用程序。

已成功配置应用范围、权限和客户端应用程序。 确保记下并保存应用程序 ID URI。 接下来,配置访问令牌版本。

更新应用清单

注意

webApplicationInfo 应用清单 1.5 或更高版本支持。

更新应用清单文件中的以下属性:

  • webApplicationInfo:为应用启用 SSO,以帮助应用用户无缝访问基于 API 的消息扩展应用。 部分,其中包含有关应用的关键详细信息。 在 Microsoft Entra ID 中注册的应用程序 ID URI 配置了公开的 API 的范围。 在 中 resource 配置应用的子域 URI,以确保 使用的 getAuthToken() 身份验证请求来自应用清单中给定的域。 有关详细信息,请参阅 webApplicationInfo

      显示应用清单配置的屏幕截图。

  • microsoftEntraConfiguration:为应用启用单一登录身份验证。 将 supportsSingleSignOn 属性配置为 true 以支持 SSO,并减少对多个身份验证的需求。

配置应用清单:

  1. 打开基于 API 的消息扩展应用项目。

  2. 打开应用清单文件夹。

    注意

  3. 打开 manifest.json 文件

  4. 将以下代码片段添加到应用清单文件:

    webApplicationInfo

    "webApplicationInfo":
    {
    "id": "{Microsoft Entra AppId}",
    "resource": "api://subdomain.example.com/{Microsoft Entra AppId}"
    }
    

    其中,

    • {Microsoft Entra AppId}是在 Microsoft Entra ID 中注册应用时创建的应用 ID。 这是 GUID。
    • subdomain.example.com是在 Microsoft Entra ID 中创建范围时注册的应用程序 ID URI。

    MicrosoftEntraConfiguration

    "authorization": {
      "authType": "microsoftEntra",
      “microsoftEntraConfiguration”: {
        “supportsSingleSignOn”: true
      }
    },
    
  5. 更新以下属性中的子域 URL:

    1. contentUrl
    2. configurationUrl
  6. 保存应用清单文件。

有关详细信息,请参阅 composeExtensions.commands

身份验证令牌

当消息扩展在身份验证期间调用 API 时,它会收到一个请求,其中包含用户的身份验证令牌 (AED 令牌) 。 然后,消息扩展会将令牌添加到传出 HTTP 请求的授权标头中。 标头格式为 Authorization: Bearer <token_value>。 例如,当消息扩展对需要身份验证的服务进行 API 调用时。 扩展按如下所示构造 HTTP 请求:

GET /api/resource HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

在基于 API 的消息扩展获取具有令牌的请求标头后,请执行以下步骤:

  • 身份验证:验证受众、范围、颁发者和签名声明的令牌,以检查令牌是否适用于你的应用。

    下面是包含标头和响应的 JSON Web 令牌 (JWT) 的示例:

    {
    "typ": "JWT",
    "rh": "0.AhoAv4j5cvGGr0GRqy180BHbR6Rnn7s7iddIqxdA7UZsDxYaABY.",
    "alg": "RS256",
    "kid": "q-23falevZhhD3hm9CQbkP5MQyU"
    }.{
      "aud": "00000002-0000-0000-c000-000000000000",
      "iss": "https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0",
      "iat": 1712509315,
      "nbf": 1712509315,
      "exp": 1712513961,
      "aio": "Y2NgYEjJqF0stqv73u41a6ZmxPEvBgA=",
      "azp": "1fec8e78-bce4-4aaf-ab1b-5451cc387264",
      "azpacr": "0",
      "name": "John Doe",
      "oid": "00000000-0000-0000-0000-000000000000",
      "preferred_username": "john.doe@contoso.com",
      "rh": "I",
      "scp": "access_as_user",
      "sub": "e4uM7JgAEm08GBuasSltQjvPuMX1fR5TqxopJpqZJB8",
      "tid": "12345678-aaaa-bbbb-cccc-9876543210ab",
      "uti": "h7DMQwSPAEeiEe62JJUGAA",
      "ver": "2.0"
      }
    
  • 使用令牌:从令牌中提取用户信息,例如名称、电子邮件和对象 ID,并使用令牌调用消息扩展应用自己的 API。

    注意

    API 接收一个Microsoft Entra令牌,其范围设置为 access_as_user Azure 门户 中注册。 但是,令牌无权调用任何其他下游 API,例如 Microsoft Graph。


疑难解答

  • 如果在将应用上传到团队时收到 清单分析失败 错误消息,请使用 Teams 应用验证程序 验证应用包,包括应用清单和 OpenAPI 规范文件。 查看 应用清单OpenAPI 说明文档 要求,以解决错误或警告,并尝试上传应用。

    屏幕截图显示了将应用上传到 Teams 时的错误消息,以及将错误详细信息复制到剪贴板的选项。

  • 如果在 Teams 中运行应用时遇到任何问题,请使用以下故障排除步骤来识别并解决问题:

    • 网络:选择开发人员工具中的“网络”选项卡以检查网络活动

      1. 打开 Teams Web 客户端

      2. 使用 Microsoft 365 凭据登录。

      3. 转到聊天,并运行消息扩展应用。

      4. 在右上角,选择“设置和更多 (...) ”。转到“更多工具>开发人员工具

      5. 选择“网络”。 选择 筛选器 选项,并在搜索字段中输入 invoke

      6. 从列表中选择错误。

      7. 在右窗格中,选择“ 响应 ”选项卡。

      8. 将显示表示来自服务或 API 的错误响应的 JSON 对象。 它包含具有 standardizedErrorerrorCodeerrorSubCodeerrorDescription的对象,这些对象具有有关错误的更多详细信息。

        屏幕截图显示了在 Teams 中运行消息扩展并收到错误时,开发人员工具响应选项卡中的“网络”选项卡、“调用错误”列表和错误详细信息。

      常见 HTTP 错误响应

      • 如果请求参数缺失或格式不正确,则可能会出现 400 错误请求错误。
      • “401 未授权”或“403 禁止访问”错误表示 API 密钥存在问题,例如缺少或未经授权。
      • 500 内部服务器错误指示由于服务器端问题,服务不知道如何响应。
  • 使用工具进行故障排除:如果网络跟踪中的信息不足,可以按照 OpenAPI 说明文档构造请求,并在必要时使用 Swagger 编辑器 或 Postman 等工具来测试请求,包括 API 密钥的授权标头。

如果无法解决这些错误,建议联系 Microsoft Teams 产品支持 部门以获取进一步的帮助。