Teamsは匿名の参加者をロビーで待たせ、さらにcaptchaによる確認を課すケースを増やしています。botを実在のMicrosoft 365アカウントにログインさせれば、そのどちらも解消できます。APIとテナントの設定、そしてTeamsがMeetと異なる仕組みになっている理由を解説します。

AAmr El Shimy
··8 min read
認証済みMicrosoft Teams bot:ロビーをスキップする

匿名のbotがTeamsミーティングに参加すると、ロビーに入れられます。誰かが「参加を待っている人がいます」という通知に気づいて、入室を許可しなければなりません。誰も気づかなければbotはタイムアウトし、録画は残りません。さらにMicrosoftは、ゲスト参加の経路にcaptchaによる確認を着実に追加してきており、headlessブラウザではこれを解けません。

実在のMicrosoft 365アカウントにログインしたbotは、そのすべてを回避します。ロビーもなく、ホストが見逃す通知もなく、captchaもありません。参加者リストに表示される名前は、そのアカウントのものになります。

ゲストbotはcaptchaの後ろでロビーに待機しています。Microsoft 365にサインインしたbotはミーティングに直接入室します

bot単位のオプトインです。teams_configを指定しなければ、botはこれまでどおりゲストとして参加します。

なぜSAMLではないのか

認証済みGoogle Meet botはSAMLを使って提供しており、私たちのAPIがあなたの所有するWorkspaceのIdPとして振る舞います。当然、なぜTeamsでは同じ方法を取らないのかという疑問が出てきます。

Microsoftには同等の経路がありません。Entra IDにサードパーティのIdPを受け入れさせるには、テナント全体をフェデレーションする必要があります。これはLegacy SSO profileに証明書をアップロードするのとは比べものにならないほど重い決断であり、お客様にお願いしたいことではありません。そのためTeamsでは、人間と同じ方法で認証します。つまりlogin.microsoftonline.comにメールアドレスとパスワードを入力するのです。

この1点の違いが、他のすべてを決めています。

Google MeetMicrosoft Teams
仕組みSAML assertion(私たちがIdPとして動作)ユーザー名とパスワード
workspaceリソースが保持するものドメイン、証明書、秘密鍵テナントのドメインのみ
credentialは私たちのサーバーの外に出るか?出ません。assertionはサーバー側で署名します。出ます。botがTLS経由で一度だけ取得します。
保存されるシークレットprivate_key_pempassword(暗号化)
失敗のパターン証明書の不一致、ユーザーの停止パスワード誤り、MFAのプロンプト

Teams botは参加時に実際のパスワードを保持する必要があります。そうしなければ入力する手段がないからです。botは、短命なセッションIDをキーとする認証済みのresolve endpointから一度だけパスワードを取得します。これはZoom botがOBF tokenを取得するのと同じ形です。パスワードはAES-256-GCMで保存時に暗号化され、どの読み取りendpointからも返されることはありません。

API

Meetと同じ2階層の構造です。1つのMicrosoft 365テナントを表すworkspaceと、その配下にぶら下がる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は薄い作りです。1つのテナントのアカウントをまとめ、単位としてまとめて無効化できるようにするために存在します。

{
  "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をround-robinで使用します。
{ "credential_id": "Y" }login Yに固定します。
両方を指定し、グループが空でない場合email_groupが優先されます。
{ "email_group": "" }のみチーム上のすべてのアクティブなloginをround-robinで使用します。
{ "email_group": "", "credential_id": "Y" }login Yに固定されます。空のグループはグループではないため、credential_idが優先されます。

飽和時、fallback: "fail"は503とともにFST_ERR_TEAMS_LOGIN_UNAVAILABLEを返し、fallback: "anonymous"はゲスト参加に切り替えます。Teamsに関しては、Meetの場合よりも"anonymous"が妥当なデフォルトです。ゲスト参加でも多くの場合は成功し、単に遅く、信頼性が低いというだけだからです。

1つのアカウントで同時に何台のbotを動かせるか

これは非常によく聞かれます。Teamsで「同時にアクティブにできるミーティングは1つだけで、他は保留にする必要がある」という表示を誰もが見たことがあるからです。

この制限はクライアントセッション単位であり、アカウント単位ではありません。 これは、1つのTeamsアプリのインスタンスが1つのオーディオスタックを占有することに由来します。Microsoftが公開している制限のページには、同時ミーティング数に関するユーザーごとの上限は記載されておらず、2つのミーティングに同時に参加する方法として案内されているのは、2台目のデバイスか2つ目のブラウザプロファイルを使うことです。

MeetingBaaSのbotはそれぞれが独立したpodであり、それぞれ独自のブラウザと、login.microsoftonline.comへの新規のログインを持ちます。各botは別々のクライアントセッションです。1つのアカウントで、異なるミーティングにいる多数の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です。1つのアカウントがN個の異なるpodのIPからN回同時にログインするというのは、リスクベースの条件付きアクセスがまさに検知するために作られたパターンであり、これが発動するとheadlessブラウザでは答えられないMFAの確認が求められます。飽和に近づいているなら、アカウントを追加する前にアカウントあたりの上限を引き上げてみる価値はありますが、いきなり大きな数値にせず、1つのアカウントで5、次に10、次に20と計測しながらログインの失敗を監視してください。

テナントの設定

ここは慎重さが必要な部分なので、何かを変更する前にこのセクションを最後まで読んでください。

専用テナントを使う

botアカウントは専用のテナント内に作成してください。たとえばbot1@contoso-bots.onmicrosoft.comのような形です。Business Basicで十分です。

認証済みbotには、実在の人間がログインするテナントには適用すべきでないアカウントレベルの設定が必要になります。テナントを分けておけば、その影響範囲はゼロに保てます。これはあれば良い程度のものではなく、以下のすべての前提条件です。

botアカウントにMFAのプロンプトが出ないようにする

新規テナントはセキュリティ既定値群(Security Defaults)が有効な状態で提供され、初回ログイン時に「アカウントのセキュリティ保護(Let's keep your account secure)」の登録画面が強制されます。headlessブラウザはこれを通過できず、Teamsのログインが失敗する最も多い原因がこれです。

通す方法は2つあり、どちらを選ぶかはテナント次第です。

人間のアカウントがない、bot専用テナントの場合。 セキュリティ既定値群をテナント全体でオフにします。entra.microsoft.com → ID(Identity)→ 概要(Overview)→ プロパティ(Properties)→ セキュリティ既定値群の管理(Manage security defaults)→ 無効(Disabled)。

人間のアカウントが含まれるテナントの場合。 MFAは有効なままにします。botアカウントをグループにまとめ、「MFAを必須にする」条件付きアクセスポリシーからそのグループだけを除外します。これにはEntra ID P1が必要です。設定に手間はかかりますが、衛生面ははるかに優れています。

いずれの場合も、botアカウントについてレガシーのユーザー単位MFAが無効になっていることを確認し、後からプロンプトが復活しないよう認証方法の登録キャンペーンもオフにしてください。

目指す最終状態は、botアカウントでログインしたときに、メールアドレス、パスワード、そして「ログイン状態を維持しますか?(Stay signed in?)」だけが表示され、その間に何も挟まらない状態です。

一度だけ手動でログインする

各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を1回ずつ実行します。

失敗はどう見えるか

今後も失敗し続ける形でログインが失敗すると、そのloginはinvalidになり、割り当ての対象から外れます。原因は3つです。

原因意味
credentialの誤りパスワードが変更されたか、作成時に誤って入力されました。
MFAが必要セキュリティ既定値群、条件付きアクセスポリシー、またはリスク検知がアカウントに確認を求めています。
タイムアウトMicrosoft側が遅かったか、フローが停止しました。

タイムアウトは一時的なものとして扱われ、何も無効化しません。他の2つは無効化を行い、last_error_messagelast_error_at、および該当のbot情報を含むfailure_dataを記録します。

原因を修正したら、再度有効化します。

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

数週間問題なく動いていたアカウントが突然「MFAが必要」で失敗し始めた場合、その原因はほぼ確実に、パスワードの変更ではなく、あなたの同時実行数に反応したリスクベースの条件付きアクセスです。何かをリセットする前に、Entraのログインlogを確認してください。

実行中のセッションが残っているloginを削除しようとすると409が返ります。スロットはbotのプロセス終了時に解放され、クラッシュした場合も同様なので、停止したpodがキャパシティを恒久的に食いつぶすことはありません。

関連記事

必要なアカウント数を自社の処理量から見積もりたい場合や、条件付きアクセスの除外が自社のセキュリティチームに受け入れられるか判断したい場合は、ぜひご相談ください

関連ブログmicrosoft-teams