You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Discord: allow configuring a bounded `agentComponents.ttlMs` callback registry lifetime for long-running component workflows, with per-account overrides and a 24-hour cap. (#84189) Thanks @100menotu001.
10
10
- Plugin SDK: add row-level session workflow helpers and deprecate `loadSessionStore` so plugins can read and patch sessions without depending on the legacy whole-store shape. (#84693) Thanks @efpiva.
11
+
- Microsoft Teams/plugins: let explicitly allowed plugin tools request per-user Teams delegated access tokens through Teams SSO, including first-time consent and six-digit-code fallback handling, so downstream APIs can perform OBO without exposing user tokens to prompts, transcripts, or generic tool metadata. Thanks @MSNexploder.
11
12
- Gateway/plugins: reuse a compatible Gateway startup plugin registry during dispatch so safe plugin dispatches avoid redundant registry loading. (#84324) Thanks @ai-hpc.
12
13
- Dependencies: refresh provider, plugin, UI, and tooling packages, update `protobufjs` to 8.4.0 to clear the current npm advisory, and carry the Claude ACP completion patch forward to `@agentclientprotocol/claude-agent-acp` 0.36.1.
13
14
- Agents/tools: remove the old sender-owner tool gating path so configured tools stay visible for trusted sessions while command and channel-action auth still carry real sender identity.
Copy file name to clipboardExpand all lines: docs/channels/msteams.md
+181-2Lines changed: 181 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -465,6 +465,174 @@ Checks bot registration, AAD app, manifest, and SSO configuration in one pass.
465
465
2. Find the bot in Teams and send a DM
466
466
3. Check gateway logs for incoming activity
467
467
468
+
## Teams SSO for delegated plugin tools
469
+
470
+
OpenClaw can use Bot Framework Teams SSO to let trusted plugin tools request the current Teams user's delegated access token at tool execution time. This is for downstream APIs that need user delegation, including APIs that then perform Microsoft Entra on-behalf-of exchange to Microsoft Graph.
471
+
472
+
For delegated SSO testing, create an **Azure Bot** resource first, then create or upload the Teams app manifest that references that bot's Microsoft App ID. The `teams app create` quick setup is fine for a plain bot smoke test, but it can create a Teams-managed bot registration that replies in Teams without exposing the Azure Bot **Configuration** page where Bot Framework OAuth connection settings are managed.
473
+
474
+
Recommended first-time SSO order:
475
+
476
+
1. Create an Azure Bot with a single-tenant Microsoft App ID, client secret, Teams channel, and messaging endpoint such as `https://<tunnel-host>/api/messages`.
477
+
2. Create the downstream Entra app registration, expose a scope such as `api://<downstream-app-id>/downstream.access`, and add the Teams client apps as authorized clients.
478
+
3. Add the Azure Bot OAuth connection that targets the downstream app scope.
479
+
4. Upload a Teams app manifest whose `bots[].botId` is the Azure Bot Microsoft App ID and whose `webApplicationInfo` points at the downstream app ID and Application ID URI.
480
+
5. Configure OpenClaw with the Azure Bot credentials and the OAuth connection name.
481
+
482
+
If the Azure Bot can receive messages but OpenClaw queues replies with
483
+
`AADSTS7000229`/ "missing service principal", create the Enterprise
484
+
Application/service principal for the bot app in the tenant:
485
+
486
+
```bash
487
+
az ad sp create --id <bot-microsoft-app-id>
488
+
az ad sp show --id <bot-microsoft-app-id> --query '{appId:appId,displayName:displayName,id:id}' -o table
489
+
```
490
+
491
+
Configure the Bot Framework OAuth connection name:
492
+
493
+
```json5
494
+
{
495
+
channels: {
496
+
msteams: {
497
+
sso: {
498
+
enabled: true,
499
+
connectionName: "DownstreamApiConnection",
500
+
},
501
+
},
502
+
},
503
+
}
504
+
```
505
+
506
+
Authorized tool plugins receive a runtime-only resolver on `ctx.auth`. The token is not added to the model prompt, inbound context payload, transcript JSONL, or generic tool metadata.
507
+
508
+
Delegated auth is plugin-gated. Add an explicit token-release allowlist for each trusted plugin that may receive delegated user tokens:
509
+
510
+
```json5
511
+
{
512
+
plugins: {
513
+
entries: {
514
+
"downstream-tools": {
515
+
auth: {
516
+
delegatedAccess: {
517
+
enabled: true,
518
+
providers: ["msteams"],
519
+
audiences: ["api://downstream-tools"],
520
+
scopes: ["downstream.access"],
521
+
chatTypes: ["direct"],
522
+
},
523
+
},
524
+
},
525
+
},
526
+
},
527
+
}
528
+
```
529
+
530
+
Plugins without this opt-in can still run their tools when otherwise enabled, but they do not receive `ctx.auth`.
531
+
532
+
Delegated auth provider ids are channel-owned. The Teams channel exposes `msteams`; core enforces the plugin policy and calls the active channel resolver, but does not maintain a fixed provider-id list.
533
+
534
+
When `audiences` or `scopes` are configured, OpenClaw checks both the plugin request and the returned JWT claims before releasing the token. The plugin request may only ask for configured audiences and scopes, and the returned token must contain the requested scopes; extra granted scopes in the token are allowed because Microsoft Entra can include previously consented scopes for the same resource. Returned audiences stay explicit: when the plugin requests an audience, the returned `aud` claim must match that requested audience; when the plugin omits an audience and policy audiences are configured, the returned `aud` claim must include at least one configured audience. A multi-valued `aud` claim is accepted when at least one returned audience satisfies that same requested-or-configured check. When `chatTypes` is configured, OpenClaw releases delegated tokens only for trusted inbound route types from `direct`, `group`, or `channel`; this decision uses runtime route context, not plugin-supplied tool arguments.
535
+
536
+
For Teams, delegated auth resolves only `channels.msteams.sso.connectionName`. A tool may omit `connectionName` or pass the same value, but it cannot select a different Bot Framework OAuth connection at runtime.
537
+
538
+
When Bot Framework has no stored user token for that OAuth connection, OpenClaw sends a Teams OAuth card into the active conversation. If the Teams client suppresses the card, the same message includes the Bot Framework sign-in link as plain text. After the user completes the card or link, Teams sends `signin/tokenExchange`/`signin/verifyState`; in browser fallback flows Microsoft may instead show a six-digit code, which the user should paste back into the same Teams chat. The Teams channel verifies the code, stores the exchanged token, and later tool calls can resolve it. Plugins should not construct or send this consent card themselves.
539
+
540
+
```typescript
541
+
api.registerTool((ctx) => ({
542
+
name: "call_downstream_api",
543
+
description: "Call the signed-in user's downstream API",
For Microsoft Entra OBO, configure the Bot Framework OAuth connection for the downstream API, not for Graph directly. The downstream API should validate a token whose `aud` is that API, require the expected delegated scope, then exchange that token to Graph itself. Do not forward a Graph access token as the downstream API's bearer token. Microsoft Entra may emit the delegated token `aud` as the downstream app client id even when the Application ID URI is `api://<downstream-app-id>`; OpenClaw treats `api://<uuid>` policy audiences as equivalent to the bare UUID claim.
565
+
566
+
### Production-shaped PoC
567
+
568
+
Use two app registrations:
569
+
570
+
-**Azure Bot app**: `channels.msteams.appId`, `channels.msteams.appPassword`, and `bots[].botId`.
571
+
-**Downstream API app**: Teams SSO `webApplicationInfo.id`, OAuth connection client id/secret, and delegated token audience.
572
+
573
+
Concrete setup:
574
+
575
+
1. Create the single-tenant Azure Bot, enable the Teams channel, and set the messaging endpoint to `https://<host>/api/messages`.
576
+
2. Create the downstream Entra app registration and set `requestedAccessTokenVersion` to `2`.
577
+
3. Expose the downstream API as `api://<downstream-app-id>` with delegated scope `downstream.access`.
578
+
4. Pre-authorize the Teams clients for that scope:
579
+
- Teams desktop/mobile: `1fec8e78-bce4-4aaf-ab1b-5451cc387264`
580
+
- Teams web: `5e3ce6c0-2b1f-4285-8d4b-75ee78787346`
581
+
5. Add redirect URI `https://token.botframework.com/.auth/web/redirect` to the downstream app.
582
+
6. Add Microsoft Graph delegated permissions such as `User.Read` to the downstream app and grant admin consent. OBO token acquisition has no interactive UI at the moment the downstream API requests Graph, so missing Graph consent appears as `AADSTS65001` or `graph_consent_required` in the local PoC server.
583
+
7. On the Azure Bot, create OAuth connection `DownstreamApiConnection`:
8. In the Teams app manifest, keep `bots[].botId` set to the Azure Bot app id, set `webApplicationInfo.id` to the downstream app id, set `webApplicationInfo.resource` to `api://<downstream-app-id>`, and include `token.botframework.com` in `validDomains`.
590
+
9. Configure OpenClaw:
591
+
592
+
```json5
593
+
{
594
+
channels: {
595
+
msteams: {
596
+
sso: {
597
+
enabled:true,
598
+
connectionName:"DownstreamApiConnection",
599
+
},
600
+
},
601
+
},
602
+
plugins: {
603
+
entries: {
604
+
"downstream-tools": {
605
+
auth: {
606
+
delegatedAccess: {
607
+
enabled:true,
608
+
providers: ["msteams"],
609
+
audiences: ["api://<downstream-app-id>"],
610
+
scopes: ["downstream.access"],
611
+
chatTypes: ["direct"],
612
+
},
613
+
},
614
+
},
615
+
},
616
+
},
617
+
}
618
+
```
619
+
620
+
The Bot Framework OAuth connection uses the full scope URI (`api://<downstream-app-id>/downstream.access`). OpenClaw plugin policy and tool requests use the JWT `scp` claim value (`downstream.access`). For the audience guard, use either the downstream app client id or `api://<downstream-app-id>`; OpenClaw accepts those two forms as equivalent for UUID app-id URIs.
621
+
622
+
The in-repo PoC at `examples/plugins/msteams-graph-profile` follows this shape:
623
+
the tool requests a downstream API token and calls a configured `/api/me`
624
+
endpoint. The sibling `examples/msteams-obo-downstream-api` package includes a
625
+
local-only downstream API server that validates the token and performs Graph OBO
626
+
server-side.
627
+
628
+
Manual validation:
629
+
630
+
1. Send a Teams DM that invokes a trusted plugin tool using `ctx.auth.getDelegatedAccessToken`.
631
+
2. Complete the Teams consent/sign-in card if OpenClaw sends one. If Teams only shows a Bot Framework sign-in link, open it and paste the six-digit code from Microsoft back into the same Teams chat.
632
+
3. Verify the downstream API receives a JWT with `aud=<downstream-app-id>` or `aud=api://<downstream-app-id>`, `scp` containing `downstream.access`, the expected `tid`, and the user's object id claim.
633
+
4. Verify the downstream API exchanges that token to Graph with OBO and calls `/me`.
634
+
5. Confirm the bearer value does not appear in gateway logs, transcripts, prompt/debug context, or tool metadata.
635
+
468
636
## Environment variables
469
637
470
638
All config keys can be set via environment variables instead:
@@ -554,7 +722,9 @@ Minimal, valid example with the required fields. Replace IDs and URLs.
@@ -575,7 +745,9 @@ Minimal, valid example with the required fields. Replace IDs and URLs.
575
745
### Manifest caveats (must-have fields)
576
746
577
747
-`bots[].botId`**must** match the Azure Bot App ID.
578
-
- `webApplicationInfo.id`**must** match the Azure Bot App ID.
748
+
-`webApplicationInfo.id` must match the Entra app used for Teams SSO. In a single-app bot setup this is the Azure Bot App ID; in a downstream API/OBO setup this is the downstream API app ID.
749
+
-`webApplicationInfo.resource` must match that Entra app's Application ID URI, for example `api://<app-id>`.
750
+
-`validDomains` must include `token.botframework.com` when Bot Framework OAuth/SSO is enabled.
579
751
-`bots[].scopes` must include the surfaces you plan to use (`personal`, `team`, `groupChat`).
580
752
-`bots[].supportsFiles: true` is required for file handling in personal scope.
581
753
-`authorization.permissions.resourceSpecific` must include channel read/send if you want channel traffic.
@@ -702,7 +874,9 @@ Key settings (see `/gateway/configuration` for shared channel patterns):
702
874
-`toolsBySender` keys should use explicit prefixes:
703
875
`channel:`, `id:`, `e164:`, `username:`, `name:` (legacy unprefixed keys still map to `id:` only).
704
876
-`channels.msteams.actions.memberInfo`: enable or disable the Graph-backed member info action (default: enabled when Graph credentials are available).
705
-
- `channels.msteams.authType`: authentication type - `"secret"` (default) or `"federated"`.
877
+
-`channels.msteams.sso.enabled`: enable Bot Framework Teams SSO token exchange for runtime delegated plugin auth.
878
+
-`channels.msteams.sso.connectionName`: Bot Framework OAuth connection name used by `ctx.auth.getDelegatedAccessToken`.
879
+
-`channels.msteams.authType`: authentication type — `"secret"` (default) or `"federated"`.
706
880
-`channels.msteams.certificatePath`: path to PEM certificate file (federated + certificate auth).
707
881
-`channels.msteams.certificateThumbprint`: certificate thumbprint (optional, not required for auth).
@@ -1005,6 +1179,11 @@ Bots have limited support in private channels:
1005
1179
1006
1180
-**Images not showing in channels:** Graph permissions or admin consent missing. Reinstall the Teams app and fully quit/reopen Teams.
1007
1181
-**No responses in channel:** mentions are required by default; set `channels.msteams.requireMention=false` or configure per team/channel.
1182
+
-**Inbound works, dispatch completes, but no Teams reply appears:** check the OpenClaw state directory's `delivery-queue/*.json` files for `lastError`. `AADSTS7000229` means the bot app is missing its service principal in the tenant; run `az ad sp create --id <bot-microsoft-app-id>`, then restart the Gateway.
1183
+
-**Delegated tool returns `unavailable` or no OAuth card appears:** confirm `channels.msteams.sso.connectionName` exactly matches the Azure Bot OAuth connection, the trusted plugin allowlist uses the downstream API audience and JWT `scp` value, and the Teams app manifest includes `token.botframework.com` in `validDomains`. With debug logging enabled, look for `msteams delegated auth token rejected by requested claims` (wrong OAuth connection audience/scope) or `msteams delegated auth consent challenge failed` (Bot Framework sign-in resource or card delivery failed). For Entra app-id URI audiences, `api://<downstream-app-id>` and the bare `<downstream-app-id>` JWT `aud` claim are treated as equivalent.
1184
+
-**Delegated example returns `auth_context_missing` or `not_configured`:**`auth_context_missing` means the tool ran without a runtime delegated-auth binding; reinstall changed local plugin code and restart the Gateway. `not_configured` means the plugin request did not pass the configured delegated-auth policy; check `providers`, `audiences`, `scopes`, and `channels.msteams.sso.connectionName`.
1185
+
-**Delegated auth returns `missing_consent`:** complete the OAuth card or sign-in link. If Microsoft shows a six-digit code, paste that code back into the same Teams chat, wait for the Gateway log that says the code was verified, then retry the tool.
1186
+
-**Downstream API returns `graph_consent_required` or logs `AADSTS65001`:** Teams delegated auth succeeded and the API reached the Graph OBO exchange, but the downstream Entra app does not have consent for its Graph delegated permission. In the downstream app registration, add Microsoft Graph delegated `User.Read` and click **Grant admin consent** for the tenant.
1008
1187
-**Version mismatch (Teams still shows old manifest):** remove + re-add the app and fully quit Teams to refresh.
1009
1188
-**401 Unauthorized from webhook:** Expected when testing manually without Azure JWT - means endpoint is reachable but auth failed. Use Azure Web Chat to test properly.
0 commit comments