Skip to content

teams chatbot#3864

Open
kinjalh wants to merge 7 commits into
masterfrom
teams-bot
Open

teams chatbot#3864
kinjalh wants to merge 7 commits into
masterfrom
teams-bot

Conversation

@kinjalh

@kinjalh kinjalh commented Jul 16, 2026

Copy link
Copy Markdown
Member

Teams chatbot, like the slack one

Test Plan

Test environment: https://console.your-env.onplural.sh/

Checklist

  • I have added a meaningful title and summary to convey the impact of this PR to a user.
  • If required, I have updated the Plural documentation accordingly.
  • I have added tests to cover my changes.
  • I have deployed the agent to a test environment and verified that it works as expected (required only when changing agent code).

Plural Flow: console

@kinjalh kinjalh added the enhancement New feature or request label Jul 16, 2026
@soffi-ai

soffi-ai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Soffi AI Summary

This PR adds Microsoft Teams chatbot support to Plural Console, mirroring the existing Slack chatbot integration. The motivation is to give teams using Microsoft Teams the same AI-powered workbench chatbot experience previously only available for Slack users.

Key changes:

  • New Teams chat backend (lib/console/chat/impl/teams.ex, teams/auth.ex, teams/token.ex, teams/connector.ex): Implements the Teams chat provider — OAuth2-based authentication against the Microsoft identity platform, token acquisition/caching, webhook connector, and a full Teams implementation of the Chat.Impl behaviour for receiving and dispatching messages.

  • OIDC provider configuration (lib/console/oidc/provider_configuration.ex): Added a generic OIDC provider configuration module to support the Teams OAuth2 flow (Microsoft identity endpoint discovery).

  • Teams AI tool (lib/console/ai/tools/workbench/integration/teams/reply.ex): A new workbench tool that allows the AI agent to reply to Teams messages in-thread, analogous to the existing Slack reply tool. Includes the corresponding JSON tool-call schema (priv/tools/workbench/integration/teams/reply.json) and a DB migration to add a reply_context column to chatbot messages for threading.

  • Webhook support (lib/console_web/controllers/webhook_controller.ex, router.ex): Extended the webhook controller to handle inbound Teams activity payloads (bot framework Activity objects), validating HMAC signatures and dispatching to the Teams chat impl.

  • Frontend form refactor (ChatbotConnectionForm.tsx): The chatbot connection form was significantly refactored to support both Slack and Teams provider types. The previously hardcoded Slack-only form is now replaced with a type selector (dropdown) and conditionally rendered SlackFields / TeamsFields sub-components. On edit, the type is locked. The buildAttributes helper centralizes attribute construction and validation logic per provider type.

  • Setup guide (assets/public/setup-guides/chatbots/teams.md): Added a Markdown setup guide for configuring a Teams chatbot connection, surfaced in the UI via chatbotSetupGuide.ts.

  • Chat system updates (lib/console/chat/utils.ex, impl.ex, registrar.ex): Extended utilities and the chat registrar to recognize and route Teams connections alongside Slack.

  • Security fixes in related modules (commit 282fac5) and cache/merge improvements across subsequent commits.

Commits

Commit Summary
4bb317c Core Teams chatbot implementation: new Elixir modules for Teams OAuth2 auth, token management, webhook connector, and chat impl; new AI workbench reply tool; DB migration for reply threading context; frontend form refactored to support both Slack and Teams provider types with conditional field rendering and a setup guide.
282fac5 Security fixes applied to the Teams integration — likely hardening HMAC webhook validation and credential handling introduced in the initial implementation.
8faf250 Cache-related changes, likely adding or tuning caching for Teams OAuth tokens or chat provider connections.
d0ae0a6 Resolved merge conflicts after rebasing onto main.
dfe9a7c Additional merge conflict resolution from integrating upstream changes.
26e98a5 Lint fixes to satisfy golangci-lint and/or ESLint after the Teams integration changes.
80c4bfa Replaced locally-run formatter with pulling up-to-date formatting from master, avoiding stale format diffs.

Deploy in Soffi


Updated: 2026-07-23 14:53 UTC

@kinjalh

kinjalh commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@greptile

def handle_activity(_, _), do: :ok

@impl true
def child_spec(%ChatConnection{}),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we ideally do have a way to use a websocket based handler here, is that not supported by teams at all?

@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements a Microsoft Teams chatbot integration, mirroring the existing Slack chatbot but using an inbound Bot Framework webhook model instead of Slack's outbound Socket Mode.

  • Adds JWT authentication (TeamsAuth) using oidcc against the Bot Framework OIDC discovery endpoint, with serviceurl claim pinning. Algorithms are constrained to those declared in the provider configuration (not the JWT header), addressing the algorithm-confusion concern from prior review rounds.
  • Adds an inbound webhook route (POST /ext/v1/webhooks/teams/:id), a Bot Framework connector for outbound replies, a connector-token cache module, and a new teams_reply workbench tool. The ChatbotMessage schema is extended with nullable reply-coordinate fields (service_url, conversation_id, activity_id) and a matching migration.
  • The edit-mode Teams credential form silently drops clientId/tenantId changes when no new clientSecret is entered; the affected fields lack a hint to inform users that all three must be supplied together to persist updates.

Confidence Score: 5/5

The auth flow is well-implemented, the webhook handler is correct, and all previous security concerns have been addressed in the current implementation.

The JWT validation uses oidcc with algorithms sourced from Microsoft's OIDC provider configuration rather than the JWT header, and the serviceurl claim absence is now treated as a hard error. The core inbound webhook and outbound connector logic look correct. The only non-trivial finding is a UX quirk in the Teams edit form where clientId/tenantId edits are silently ignored unless the secret is also changed, which does not affect runtime correctness of the bot.

The TTL unit inconsistency in lib/console/chat/teams/token.ex (flagged in a prior review) remains unaddressed and is worth a follow-up.

Important Files Changed

Filename Overview
lib/console/chat/teams/auth.ex New JWT verification module for Bot Framework tokens; uses oidcc with provider-configuration-derived algorithms (not the JWT header), serviceurl claim absence correctly returns an error, and provider config is cached.
lib/console/chat/teams/token.ex New connector-token caching module; expiry/1 returns seconds (Unix delta) while the fallback uses :timer.minutes(15) (milliseconds), matching a TTL unit inconsistency previously flagged.
lib/console/chat/teams/connector.ex New Bot Framework connector for outbound replies; correctly URL-encodes conversation IDs and uses bearer token from Token module.
lib/console/chat/impl/teams.ex New Teams chat impl; correctly extracts channel ID, cleans mention text, delegates to handle_mention/4 with reply coordinates, and implements channel search via Graph API.
lib/console_web/controllers/webhook_controller.ex Adds Teams webhook endpoint; validates JWT via TeamsAuth, dispatches to TeamsBot, and correctly returns 401 on auth errors vs 200+ignored for unknown connections.
assets/src/components/settings/chatbots/ChatbotConnectionForm.tsx Extends connection form for Teams; buildAttributes has a silent-drop issue where clientId/tenantId edits are lost in edit mode when clientSecret is not re-entered.
lib/console/chat/utils.ex Adds arity-4 handle_mention/4 that merges provider-specific reply coordinates into the chatbot message, backwards-compatible with existing arity-3 callers.
lib/console/schema/chatbot_message.ex Adds service_url, conversation_id, activity_id fields for Teams reply coordinates; migration and changeset both updated correctly.
lib/console/chat/registrar.ex Correctly filters DynamicSupervisor children to only Slack connections; Teams webhook-based connections are intentionally excluded.
lib/console/ai/tools/workbench/integration/teams/reply.ex New Teams reply tool that reads serviceUrl/conversationId from ChatbotMessage context and delegates to Connector.reply.
priv/repo/migrations/20260715120000_add_chatbot_message_reply_context.exs Adds nullable service_url, conversation_id, activity_id columns to chatbot_messages; non-breaking change.

Reviews (3): Last reviewed commit: "security fixes" | Re-trigger Greptile

Comment thread lib/console/chat/teams/auth.ex Outdated
require Logger

@issuer "https://api.botframework.com"
@metadata_url "https://login.botframework.com/v1/.well-known/openidconfiguration"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you need to do bot token verification (that seems a bit odd for a webhook to me, usually that's just an hmac signature to verify), you ideally fetch the jwks from this url and use that to verify the jwt. There should be existing code which does this actually, we'd need it to:

  1. Cache the jwks document appropriately (we don't want to round tripe each webhook)
  2. traverse openid configuration -> jwks url, etc

I think the Oidcc module does a lot of that but could have been done elsewhere.

@michaeljguarino

Copy link
Copy Markdown
Member

@greptileai

end

def implement(%__MODULE__{tool: %WorkbenchTool{configuration: %Configuration{teams: %TeamsConnection{} = conn}}, text: text}) do
case reply_context() do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

refactor this to be a with comprehension (almost all nested case's can be with comprehensions)

Comment thread lib/console/chat/teams/auth.ex Outdated
@decorate cacheable(cache: @cache_adapter, key: :teams_bf_oidc_config, opts: [ttl: @ttl], match: &ok?/1)
def provider_configuration() do
with {:ok, {conf, _}} <- Oidcc.ProviderConfiguration.load_configuration(@config_issuer, @quirks),
{:ok, {jwks, _}} <- Oidcc.ProviderConfiguration.load_jwks(conf.jwks_uri) do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i'm pretty sure either the oidc configuration or the jwks uri return a refresh after timestamp, we ideally dynamically cache the result based on that so it ttl's about when it needs a refresh. We do soemthing similar in the TokenExchange module, you likely can tell the ai to implement something like that, although tbh, might also be nice to have a general module for oidc provider configuration with nebulex caching (instead of the supervised worker it usually requires).

@kinjalh
kinjalh marked this pull request as ready for review July 21, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants