返回所有博客/microsoft-teams

Teams 会把匿名加入者拦在等候室里,而且越来越多地加上验证码。让 bot 登录一个真实的 Microsoft 365 账户就能同时绕开这两点。本文介绍 API、租户配置,以及 Teams 为什么和 Meet 的做法不同。

AAmr El Shimy
··6 min read
已认证的 Microsoft Teams Bot:跳过等候室

匿名 bot 加入 Teams 会议时会先进入等候室。必须有人注意到「有人在等待」的提示并放它进来。如果没人操作,bot 就会超时,你什么录像都拿不到。同时,Microsoft 也在不断给访客加入路径添加验证码挑战,而无头浏览器解不了验证码。

登录了真实 Microsoft 365 账户的 bot 可以跳过这一切。没有等候室,没有可能被主持人忽略的提示,也没有验证码。它在参会者列表里的名字来自这个账户。

访客 bot 在候客室等待并遭遇验证码;已登录 Microsoft 365 的 bot 直接进入会议

按 bot 选择启用。不传 teams_config,你的 bot 就继续以访客身份加入,行为不变。

为什么这次不用 SAML

我们用 SAML 发布了已认证的 Google Meet bot,由我们的 API 充当你所拥有的 Workspace 的身份提供方。很自然的问题是:Teams 为什么不这么做?

Microsoft 没有提供对等的路径。要让 Entra ID 接受第三方 IdP,就意味着整个租户的完全联合认证,这比往 Legacy SSO 配置文件里上传一份证书重得多,我们也不会要求客户这么做。所以 Teams 的认证方式和真人一样:在 login.microsoftonline.com 里输入邮箱,再输入密码。

这一个差别决定了其他所有事情:

Google MeetMicrosoft Teams
机制SAML 断言,我们充当 IdP用户名和密码
workspace 资源保存什么域名、证书、私钥仅租户域名
凭据会离开我们的服务器吗?不会。我们在服务端签署断言。会。bot 通过 TLS 获取一次。
存储的密文private_key_pempassword,加密存储
失败形态证书不匹配、用户被停用密码错误、MFA 提示

Teams bot 在加入时必须持有真实密码,因为除此之外没有别的办法把密码输进去。它通过一个需要认证的解析 endpoint 获取一次,该 endpoint 以一个短期有效的 session id 为键,形式和 Zoom bot 获取 OBF token 的方式一致。密码在静态存储时用 AES-256-GCM 加密,任何读取 endpoint 都不会返回它。

API

和 Meet 一样的两层结构:一个 workspace 代表一个 Microsoft 365 租户,login 挂在它下面。

POST   /v2/teams-workspaces
GET    /v2/teams-workspaces
GET    /v2/teams-workspaces/:workspace_id
PATCH  /v2/teams-workspaces/:workspace_id
DELETE /v2/teams-workspaces/:workspace_id

POST   /v2/teams-logins
GET    /v2/teams-logins
GET    /v2/teams-logins/utilization
GET    /v2/teams-logins/:credential_id
PATCH  /v2/teams-logins/:credential_id
DELETE /v2/teams-logins/:credential_id

这里的 workspace 很轻,因为没有证书要保存。它存在的意义是把同一个租户的账户归到一起,并且可以整体停用:

{
  "name": "Contoso Production Tenant",
  "domain": "contoso.onmicrosoft.com"
}

租户任意一个已验证域名下的账户都可以加进来。

然后是每个账户:

{
  "workspace_id": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
  "name": "Teams Bot Pool — Account 1",
  "email": "bot1@contoso.onmicrosoft.com",
  "password": "...",
  "email_group": "bots@contoso.onmicrosoft.com"
}

password 只写不读。回读这个 login 时,除密码之外的内容都会返回:

{
  "credential_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "workspace_id": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
  "name": "Teams Bot Pool — Account 1",
  "email": "bot1@contoso.onmicrosoft.com",
  "email_group": "bots@contoso.onmicrosoft.com",
  "state": "active",
  "last_error_message": null,
  "last_error_at": null,
  "last_used_at": "2026-08-10T14:22:03.000Z",
  "active_session_count": 3,
  "extra": null,
  "created_at": "2026-08-01T09:12:44.000Z",
  "updated_at": "2026-08-10T14:22:03.000Z"
}

轮换密码就是用 PATCH 传入新密码。其他都不变,运行中的 bot 也保住自己的名额。

在 bot 上使用

{
  "meeting_url": "https://teams.microsoft.com/l/meetup-join/...",
  "bot_name": "Recording Bot",
  "teams_config": {
    "email_group": "bots@contoso.onmicrosoft.com",
    "fallback": "anonymous"
  }
}

teams_config 的解析规则和 meet_config 完全相同:

你传入的内容实际行为
null(默认值)以匿名访客身份加入。
{ "email_group": "X" }在分组 X 内的活跃 login 之间轮询。
{ "credential_id": "Y" }固定使用 login Y。
两者都传,且分组非空email_group 优先。
只传 { "email_group": "" }在团队全部活跃 login 之间轮询。
{ "email_group": "", "credential_id": "Y" }固定使用 login Y。空分组不算分组,所以 credential_id 生效。

池子占满时,fallback: "fail" 返回 FST_ERR_TEAMS_LOGIN_UNAVAILABLE 和一个 503,fallback: "anonymous" 则退回访客加入。对 Teams 来说,"anonymous" 比在 Meet 上更适合做默认值,因为访客加入通常还是能成功的,只是更慢、更不可靠。

一个账户能同时跑多少个 bot?

这个问题被反复问到,因为大家都见过 Teams 提示:你只能在一个会议中处于活动状态,其余的必须保持等待。

那个限制是按客户端会话算的,不是按账户算的。 它来自一个 Teams 应用实例只拥有一套音频栈。Microsoft 公布的限制页面并没有对每个用户的并发会议数设上限,而且他们自己给出的「同时参加两个会议」的建议就是用第二台设备或第二个浏览器配置文件。

每个 MeetingBaaS bot 都是独立的 pod,有自己的浏览器,会自己重新登录一次 login.microsoftonline.com。每个 bot 都是一个单独的客户端会话。一个账户可以同时支撑多个位于不同会议中的 bot。

每个 login 的默认上限是 20 个并发会话,GET /v2/teams-logins/utilization 会按这个上限报告:

{
  "logins_total": 3,
  "logins_active": 3,
  "logins_invalid": 0,
  "concurrent_sessions": 41,
  "concurrent_capacity": 60,
  "utilization_pct": 68,
  "by_email_group": [
    { "email_group": "bots@contoso.onmicrosoft.com", "logins": 3, "concurrent": 41, "capacity": 60 }
  ]
}

不过真正的天花板不是 Teams,而是 Entra ID。同一个账户从 N 个不同的 pod IP 同时登录 N 次,正是基于风险的条件访问专门要标记的模式;一旦触发,你会收到无头浏览器无法应答的 MFA 挑战。如果你已经接近饱和,先试着调高单账户的上限,再考虑开通更多账户;但要在单个账户上分别测到 5、10、20,观察有没有登录失败,不要一步跳到很高的数字。

租户配置

这部分需要格外小心,动手改之前请先把整节读完。

使用专用租户

在一个独立的租户里创建 bot 账户,例如 bot1@contoso-bots.onmicrosoft.com。Business Basic 就够了。

已认证的 bot 需要一些账户级设置,而这些设置不该应用到真人登录的租户上。单独的租户能把影响范围压到零。这不是锦上添花,而是下面所有步骤的前提。

确保 bot 账户不会收到 MFA 提示

新建租户默认开启安全默认值(Security Defaults),会在首次登录时强制弹出「保护你的账户安全」注册页。无头浏览器过不去,而这正是 Teams 登录失败最常见的原因。

有两种解决路径,选哪一种取决于你的租户:

专用 bot 租户,没有真人账户。 在整个租户范围内关闭安全默认值:entra.microsoft.com → 标识(Identity)→ 概述(Overview)→ 属性(Properties)→ 管理安全默认值(Manage security defaults)→ 已禁用(Disabled)。

租户里有真人账户。 保持 MFA 开启。把 bot 账户放进一个组,然后只把这个组从「要求 MFA」的条件访问策略中排除。这需要 Entra ID P1。配置更慢,但安全卫生要好得多。

无论走哪条路,都要再检查一下 bot 账户的旧版每用户 MFA 已禁用,并关掉身份验证方法注册推广活动,免得它以后又把提示带回来。

你要达到的最终状态是:用 bot 账户登录时依次出现邮箱、密码、「保持登录状态?」,中间不夹带任何其他页面。

先手工登录一次

在普通浏览器里手动登录每个 bot 账户,把 Microsoft 弹出的各种首次运行插页处理干净。道理和 Meet 那边的 Workspace 引导步骤一样:第一次登录无论如何都是交互式的,那就赶在 bot 需要这个账户之前把它做掉。

注册 workspace 和 login

curl -X POST https://api.meetingbaas.com/v2/teams-workspaces \
  -H "x-meeting-baas-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "contoso-bots.onmicrosoft.com"}'

然后每个账户执行一次 POST /v2/teams-logins

失败时是什么样子

如果一次登录失败的方式注定会继续失败,这个 login 就会被置为 invalid 并停止参与分配。有三种原因:

原因含义
凭据错误密码变更了,或者创建时就输错了。
需要 MFA安全默认值、某条条件访问策略,或者一次风险检测正在挑战这个账户。
超时Microsoft 响应慢,或者流程卡住了。

超时被当作临时问题处理,不会让任何东西失效。另外两种会,并且会记录 last_error_messagelast_error_at,以及包含触发它的 bot 的 failure_data

修复原因之后再重新启用:

PATCH /v2/teams-logins/:credential_id
{ "state": "active" }

如果某个账户用了好几周一直正常,然后突然开始报「需要 MFA」,这几乎总是基于风险的条件访问在对你的并发量作出反应,而不是密码被改了。在动手重置任何东西之前,先去 Entra 里看登录 logs。

删除仍有会话在进行的 login 会返回 409。名额在 bot 进程退出时释放,包括崩溃退出的情况,所以一个死掉的 pod 不会永久占用容量。

相关阅读

如果你正在估算自己的业务量需要多少个账户,或者拿不准条件访问排除方案能不能通过安全团队那一关,我们很乐意和你聊聊

相关博客microsoft-teams