Skip to content

fix(msteams): include tenantId and aadObjectId on proactive sends (#58774)#63949

Merged
BradGroux merged 2 commits intoopenclaw:mainfrom
sudie-codes:fix/msteams-proactive-403-58774
Apr 10, 2026
Merged

fix(msteams): include tenantId and aadObjectId on proactive sends (#58774)#63949
BradGroux merged 2 commits intoopenclaw:mainfrom
sudie-codes:fix/msteams-proactive-403-58774

Conversation

@sudie-codes
Copy link
Copy Markdown
Contributor

Summary

Fixes HTTP 403 on proactive Teams messages by capturing tenantId and aadObjectId from inbound activities and forwarding them on outbound proactive sends.

Root cause

The Bot Framework's connector requires tenantId and aadObjectId in the conversation reference for proactive (bot-initiated) sends. The msteams plugin was discarding these from inbound activities, so when cron / scheduled messages tried to send proactively, the resulting connector request was rejected with 403.

Fix

  • Extract tenantId from activity.channelData?.tenant?.id at inbound time (channel activities often leave conversation.tenantId unset, so channelData.tenant.id is the canonical source)
  • Extract aadObjectId from activity.from?.aadObjectId at inbound time
  • Store both on StoredConversationReference
  • Forward them on outbound proactive sends in messenger.ts and on the Bot Framework connector request in sdk.ts (via channelData.tenant.id, conversation.tenantId, and recipient.aadObjectId)

Fixes #58774

Test plan

  • Unit tests for inbound capture (channel activity with channelData.tenant.id, plus graceful degradation when channelData is missing)
  • Unit tests for outbound forwarding via buildConversationReference and sendMSTeamsMessages
  • Full msteams suite passes (583 tests)
  • Manual: trigger a cron announce to a Teams channel and verify it succeeds

@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: M labels Apr 9, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6858d9ed6a

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +35 to 37
aadObjectId?: string;
/** Team ID for channel messages (when available). */
teamId?: string;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore threadId on StoredConversationReference

This change drops threadId from StoredConversationReference, but thread handling still writes and reads it (monitor-handler/message-handler.ts and messenger.ts rely on it for channel thread routing/fallback). That leaves the contract inconsistent and causes type errors where threadId is accessed, while also making it easy for future call sites to lose thread-root metadata and regress threaded replies.

Useful? React with 👍 / 👎.

Comment on lines +123 to +124
...(tenantId ? { tenantId } : {}),
...(aadObjectId ? { aadObjectId } : {}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve tenant and AAD IDs across sparse inbound activities

The new fields are only emitted when truthy (...(tenantId ? ...), ...(aadObjectId ? ...)), but store upserts replace the reference object and only preserve timezone in mergeStoredConversationReference. If a later activity in the same conversation arrives without channelData.tenant.id or from.aadObjectId, this code clears previously captured IDs, so subsequent proactive sends can again omit required tenant/AAD metadata and hit 403.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 9, 2026

Greptile Summary

This PR fixes HTTP 403 on proactive Teams sends by capturing tenantId (from channelData.tenant.id, the canonical Bot Framework source) and aadObjectId (from activity.from.aadObjectId) on inbound activities and forwarding them through StoredConversationReferencebuildConversationReferencecontinueConversation → the outbound connector request. The implementation includes proper fallback to conversation.tenantId for backward compatibility with older stored references, and has solid unit-test coverage for the inbound capture and outbound forwarding paths.

Confidence Score: 5/5

Safe to merge; the fix is well-scoped, backward-compatible, and the remaining finding is a minor hardening suggestion.

All findings are P2 or lower. The only concern is that the outbound recipient field only carries aadObjectId and not id — this could be a gap for some connector implementations, but the PR author reports the 403 is resolved, indicating the connector accepts aadObjectId alone in the tested paths. No logic errors, data-loss risks, or security issues were found.

extensions/msteams/src/sdk.ts — the recipient field in createSendContext.sendActivity omits recipient.id; minor hardening only.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/msteams/src/sdk.ts
Line: 204-206

Comment:
**`recipient` missing `id` field on proactive sends**

When `recipientAadObjectId` is set, the outbound activity's `recipient` only carries `aadObjectId` and omits `id` (the Teams user ID, e.g. `29:…`). Some Bot Framework connector implementations may expect `recipient.id` to be present alongside `aadObjectId` for personal-DM proactive sends. The user ID is already available from `reference.user?.id` in `continueConversation`.

Consider threading `recipientId` through `createSendContext` alongside `recipientAadObjectId` to avoid a potential gap, even if the current connector accepts `aadObjectId` alone.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(msteams): capture and forward tenant..." | Re-trigger Greptile

Comment thread extensions/msteams/src/sdk.ts Outdated
Comment on lines +204 to +206
...(params.recipientAadObjectId
? { recipient: { aadObjectId: params.recipientAadObjectId } }
: {}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 recipient missing id field on proactive sends

When recipientAadObjectId is set, the outbound activity's recipient only carries aadObjectId and omits id (the Teams user ID, e.g. 29:…). Some Bot Framework connector implementations may expect recipient.id to be present alongside aadObjectId for personal-DM proactive sends. The user ID is already available from reference.user?.id in continueConversation.

Consider threading recipientId through createSendContext alongside recipientAadObjectId to avoid a potential gap, even if the current connector accepts aadObjectId alone.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/sdk.ts
Line: 204-206

Comment:
**`recipient` missing `id` field on proactive sends**

When `recipientAadObjectId` is set, the outbound activity's `recipient` only carries `aadObjectId` and omits `id` (the Teams user ID, e.g. `29:…`). Some Bot Framework connector implementations may expect `recipient.id` to be present alongside `aadObjectId` for personal-DM proactive sends. The user ID is already available from `reference.user?.id` in `continueConversation`.

Consider threading `recipientId` through `createSendContext` alongside `recipientAadObjectId` to avoid a potential gap, even if the current connector accepts `aadObjectId` alone.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 631134a8c5

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +42 to +43
...(existing?.aadObjectId && !incoming.aadObjectId
? { aadObjectId: existing.aadObjectId }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve aadObjectId only when sender identity matches

mergeStoredConversationReference now carries forward the previous top-level aadObjectId whenever the new activity omits it, but it does this without checking whether incoming.user.id changed. In shared conversations, a sparse activity from a different user can produce a mixed reference (user.id from user B with aadObjectId from user A), and createMSTeamsAdapter then forwards both as recipient metadata on proactive sends. That can generate invalid recipient identity tuples and cause proactive sends to fail or route incorrectly; gate this carry-forward on matching user IDs (or keep user and top-level AAD fields synchronized).

Useful? React with 👍 / 👎.

@BradGroux BradGroux force-pushed the fix/msteams-proactive-403-58774 branch from 631134a to ccbd053 Compare April 10, 2026 15:59
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccbd053439

ℹ️ 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".

Comment on lines +421 to +423
tenantId,
recipientId,
recipientAadObjectId,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restrict recipient identity fields to personal conversations

continueConversation now forwards recipientId/recipientAadObjectId for every conversation type, and createSendContext always emits them on outbound activities. In channel/group proactive sends, reference.user is the last sender rather than the conversation target, so this attaches user-specific recipient metadata to channel posts; Teams only supports aadObjectId routing for personal scope, so channel announcements can be rejected or misrouted. Only set recipient identity fields when conversationType is personal.

Useful? React with 👍 / 👎.

@BradGroux BradGroux merged commit 2b5b581 into openclaw:main Apr 10, 2026
30 of 32 checks passed
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…enclaw#58774) (openclaw#63949)

* fix(msteams): capture and forward tenantId/aadObjectId on proactive sends (openclaw#58774)

* msteams: preserve tenantId/aadObjectId on sparse merges, thread recipientId on proactive sends
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…enclaw#58774) (openclaw#63949)

* fix(msteams): capture and forward tenantId/aadObjectId on proactive sends (openclaw#58774)

* msteams: preserve tenantId/aadObjectId on sparse merges, thread recipientId on proactive sends
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…enclaw#58774) (openclaw#63949)

* fix(msteams): capture and forward tenantId/aadObjectId on proactive sends (openclaw#58774)

* msteams: preserve tenantId/aadObjectId on sparse merges, thread recipientId on proactive sends
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Microsoft Teams: HTTP 403 on proactive messages in v2026.3.31 + RSC consent blocks bot installation in team

2 participants