Teams: support separate graphTenantId for cross-tenant Graph API access#67174
Teams: support separate graphTenantId for cross-tenant Graph API access#67174hddevteam wants to merge 6 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryAdds All three inline findings are P2 and do not block merge. Confidence Score: 5/5Safe to merge; all findings are P2 style/improvement suggestions with no current production breakage. The cross-tenant token-provider plumbing is logically correct and well-scoped. Single-tenant behavior is unchanged. The three flagged items (error-result caching after removing the silent catch, undocumented federated-auth limitation, missing tests for the new cross-tenant path) are all quality/documentation concerns rather than defects in the changed code path. extensions/msteams/src/thread-parent-context.ts (error-caching behavior after fetchChannelMessage change), extensions/msteams/src/graph.ts and token.ts (missing test coverage for graphTenantId paths)
|
There was a problem hiding this comment.
Pull request overview
Adds cross-tenant Microsoft Graph token acquisition support for the bundled Microsoft Teams integration, so Bot Framework reply tokens and Graph API tokens can be issued from different Azure tenants when needed.
Changes:
- Introduces
graphTenantId/MSTEAMS_GRAPH_TENANT_IDand wires a dedicated Graph token provider into Teams runtime. - Improves channel thread context behavior (thread root resolution, mention-only thread replies, Graph error propagation, and an in-memory fallback cache).
- Hardens Teams webhook JSON parsing by repairing invalid escape sequences; extends runtime logging to forward structured
meta.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/plugins/runtime/runtime-logging.ts | Passes meta through runtime logger methods. |
| src/plugins/runtime/runtime-logging.test.ts | Adds tests ensuring logger meta is forwarded and shouldLogVerbose is re-exported. |
| src/plugin-sdk/msteams.ts | Re-exports resolveThreadSessionKeys via the Teams plugin SDK surface. |
| src/config/zod-schema.providers-core.ts | Adds graphTenantId to the Teams config schema. |
| src/config/types.msteams.ts | Documents and types the new graphTenantId config field. |
| extensions/msteams/src/token.ts | Resolves graphTenantId from config/env and returns it in secret credentials. |
| extensions/msteams/src/thread-message-cache.ts | Adds a simple in-memory per-thread message cache with TTL. |
| extensions/msteams/src/sdk-types.ts | Extends activity typing to include team.aadGroupId. |
| extensions/msteams/src/monitor.ts | Creates a dedicated Graph token provider when graphTenantId differs; switches webhook parsing to raw + repair middleware. |
| extensions/msteams/src/monitor.lifecycle.test.ts | Updates Express mocking for raw() and middleware count/order assertions. |
| extensions/msteams/src/monitor-handler/message-handler.ts | Uses graph token provider, adds mention-only thread handling, thread root resolution, and cache-based context fallback. |
| extensions/msteams/src/monitor-handler/message-handler.thread-parent.test.ts | Adds coverage for mention-only thread invocation and cache fallback behaviors. |
| extensions/msteams/src/monitor-handler/message-handler.test-support.ts | Extends test logger stub with warn. |
| extensions/msteams/src/monitor-handler.types.ts | Adds optional graphTokenProvider dependency. |
| extensions/msteams/src/graph.ts | Uses graphTenantId when resolving app-only Graph tokens. |
| extensions/msteams/src/graph-thread.ts | Removes silent error swallowing in fetchChannelMessage. |
| extensions/msteams/src/graph-thread.test.ts | Updates test to assert errors propagate. |
| extensions/msteams/src/tests/monitor-json-repair.test.ts | Adds regression tests for the JSON repair behavior. |
| docs/channels/msteams.md | Documents mention-only thread behavior and Graph permission requirement. |
| CHANGELOG.md | Adds changelog entry for the new cross-tenant Graph token acquisition support. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b782db1718
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
b782db1 to
38defac
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38defaca07
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Codex review: found issues before merge. What this changes: The PR adds Maintainer follow-up before merge: This is an active contributor PR in Teams auth and secret-handling code with a concrete security finding, so the next action is maintainer review/requested author fixes rather than an automated replacement or cleanup close. Security review: Security review needs attention: The diff introduces no third-party code or CI/package execution changes, but it does retain a plaintext Teams client secret in a module-level cache key. Review findings:
Review detailsBest possible solution: Land a narrow Teams plugin change that keeps Bot Framework reply tokens on the bot tenant, uses Do we have a high-confidence way to reproduce the issue? Yes for the static behavior: current main has no Is this the best way to solve the issue? No, not merge-ready as written. The split-provider design is the narrow right direction, but the cache key should not contain a raw client secret and the changelog needs the required attribution before merge. Full review comments:
Overall correctness: patch is incorrect Security concerns:
Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against a256745323ab. |
0eb41ae to
6c36b1f
Compare
Add `graphTenantId` config field (and `MSTEAMS_GRAPH_TENANT_ID` env var) so the bot can acquire Bot Framework reply tokens and Graph API tokens from different Azure tenants. This enables thread context fetching when the bot app is registered in one Azure tenant but Teams/M365 data lives in a separate Microsoft 365 tenant. Changes: - `MSTeamsConfig.graphTenantId`: new optional field in config type + zod schema - `MSTeamsSecretCredentials.graphTenantId`: carry through credential resolution - `token.ts`: resolve graphTenantId from config or MSTEAMS_GRAPH_TENANT_ID env - `graph.ts`: use graphTenantId tenant when acquiring Graph-only token - `monitor.ts`: create dedicated graphTokenProvider App when tenants differ - `monitor-handler.types.ts`: add optional graphTokenProvider to handler deps - `message-handler.ts`: use effectiveGraphTokenProvider for thread context calls - `graph-thread.ts`: remove silent error catch in fetchChannelMessage so 403s surface to the caller's Promise.allSettled handler instead of silently returning undefined Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6c36b1f to
a07328f
Compare
Summary
Adds
graphTenantIdconfig field (andMSTEAMS_GRAPH_TENANT_IDenv var) to the Microsoft Teams provider so deployments where the bot app is registered in one Azure tenant but Teams/M365 data lives in a separate Microsoft 365 tenant can acquire tokens from the correct tenant for each operation.Problem
In multi-tenant deployments (bot app registered in Azure AD tenant A, Teams data in M365 tenant B), the existing code used the same Bot Framework
Appinstance to acquire both:This caused 403 errors on all Graph API calls (
ChannelMessage.Read.All, etc.), silently returning no thread context when a bot was @mentioned in a channel thread.Solution
MSTeamsConfig.graphTenantId: new optional config fieldMSTEAMS_GRAPH_TENANT_ID: env var fallback (same precedence pattern as existingTEAMS_TENANT_ID)monitor.ts: creates a dedicatedgraphTokenProviderBot FrameworkAppinstance usinggraphTenantIdwhen it differs fromtenantIdmessage-handler.ts: useseffectiveGraphTokenProvider = graphTokenProvider ?? tokenProviderfor all Graph API token acquisitions — no behavior change for single-tenant setupsgraph-thread.ts: removed silenttry/catchinfetchChannelMessageso Graph 403s propagate to thePromise.allSettledhandler in the message handler instead of returningundefinedsilentlyConfiguration
Single-tenant deployments (most users): no change needed.
Cross-tenant deployments — add to
openclaw.json:{ "providers": { "msteams": { "tenantId": "<azure-tenant-id>", "graphTenantId": "<m365-tenant-id>" } } }Or via environment:
MSTEAMS_GRAPH_TENANT_ID=<m365-tenant-id>Testing
pnpm test)pnpm checkclean (lint, type-check, import cycles)pnpm config:docs:gen)Checklist
pnpm checkclean