Conversation
Soffi AI SummaryThis 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:
Commits
Updated: 2026-07-23 14:53 UTC |
| def handle_activity(_, _), do: :ok | ||
|
|
||
| @impl true | ||
| def child_spec(%ChatConnection{}), |
There was a problem hiding this comment.
we ideally do have a way to use a websocket based handler here, is that not supported by teams at all?
Greptile SummaryThis 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.
Confidence Score: 5/5The 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.
|
| 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
| require Logger | ||
|
|
||
| @issuer "https://api.botframework.com" | ||
| @metadata_url "https://login.botframework.com/v1/.well-known/openidconfiguration" |
There was a problem hiding this comment.
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:
- Cache the jwks document appropriately (we don't want to round tripe each webhook)
- traverse openid configuration -> jwks url, etc
I think the Oidcc module does a lot of that but could have been done elsewhere.
| end | ||
|
|
||
| def implement(%__MODULE__{tool: %WorkbenchTool{configuration: %Configuration{teams: %TeamsConnection{} = conn}}, text: text}) do | ||
| case reply_context() do |
There was a problem hiding this comment.
refactor this to be a with comprehension (almost all nested case's can be with comprehensions)
| @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 |
There was a problem hiding this comment.
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).
Teams chatbot, like the slack one
Test Plan
Test environment: https://console.your-env.onplural.sh/
Checklist
Plural Flow: console