Skip to content

msteams: v2026.4.26 ships broken JWT validator (jwt.verify is not a function); fix b3bc60ae missed the cut #73754

@mroboff

Description

@mroboff

Summary

The msteams plugin bundled with openclaw@2026.4.26 (which pins @openclaw/msteams@2026.4.25) silently drops every inbound Teams message because jwt.verify is not a function. Outbound messages from the bot continue to work, masking the failure — bots appear "online" but never receive user messages.

A fix has already landed on main in commit b3bc60ae40 ("fix(msteams): unwrap jwt runtime deps", 2026-04-27 20:53 UTC), but it was not included in the v2026.4.26 release which was cut a few hours later. So users upgrading to v2026.4.26 still hit this regression.

Reproduction

  1. Install openclaw@2026.4.26 (bundles @openclaw/msteams@2026.4.25)
  2. Configure a fully working Bot Channels / Teams app with valid appId, appPassword, tenantId, public messaging endpoint
  3. Send any message to the bot in a Teams DM

Expected: Bot receives the message and an agent reply is dispatched.
Actual: Endpoint returns HTTP 401 silently, no agent run, no log output (even at logging.level=debug). Outbound from the bot still works, so the failure is invisible without packet/inbound instrumentation.

Root cause

In the compiled bundle dist/extensions/msteams/graph-users-Bgd5K8gB.js:

// Compiled (broken):
botFrameworkJwtDepsPromise ??= Promise.all([import("jsonwebtoken"), import("jwks-rsa")])
  .then(([jwt, { JwksClient }]) => ({ jwt, JwksClient }));

jsonwebtoken is a CommonJS module. Dynamic import() of a CJS module in Node ESM yields a namespace where verify only exists on .default (while decode is also exposed on the namespace, which is why jwt.decode(token) succeeds and jwt.verify(...) throws TypeError: jwt.verify is not a function).

The try { ... } catch { return false; } in createBotFrameworkJwtValidator.validate then silently swallows the TypeError, returning a falsy validation result and 401-ing the request.

The TS source already has the safe wrappers (loadJsonwebtokenRuntime, loadJwksClientRuntime) introduced in b3bc60ae40, but the plugin shipped at @openclaw/msteams@2026.4.25 (bundled in openclaw@2026.4.26) was built before that commit landed, and the bundler appears to have inlined the destructure rather than calling the helpers.

Diagnostic capture (what we observed)

With local instrumentation patches:

[MSTEAMS_DIAG] inbound POST /api/messages ua=Microsoft-SkypeBotApi (Microsoft-BotFramework/3.0)
[MSTEAMS_DIAG] JWT header={"alg":"RS256","kid":"Pr1J4k5mkxHV31woViVQirfzm5s","typ":"JWT"}
[MSTEAMS_DIAG] JWT claims iss=https://api.botframework.com aud=<our appId> serviceurl=https://smba.trafficmanager.net/amer/<tenant>/ exp=… (valid)
[JWT_DIAG] resolving kid=… from https://login.botframework.com/v1/.well-known/keys
[JWT_DIAG] got public key, length=450
[JWT_DIAG] EXCEPTION during verify: name=TypeError message=jwt.verify is not a function
[MSTEAMS_DIAG] JWT validation FAILED (returned falsy)

Token claims are correct, JWKS fetch is correct, kid resolves, public key extraction works. The verify call itself throws.

Local hot-fix that restored two-way comms

// dist/extensions/msteams/graph-users-Bgd5K8gB.js
botFrameworkJwtDepsPromise ??= Promise.all([import("jsonwebtoken"), import("jwks-rsa")])
  .then(([jwtMod, { JwksClient }]) => ({
    jwt: jwtMod.default ?? jwtMod,
    JwksClient,
  }));

After this single-line fix, JWT validation passes, conversations register, agent runs dispatch, and replies flow back to Teams.

Severity

High for any 1:1 or group Teams use case. The failure is invisible (silent 401, no logs even at debug), and the bot looks healthy from the outside because outbound works. We hit ~12 days of dead inbound before pinpointing it.

Suggested actions

  1. Cut a v2026.4.27 (or v2026.4.26.1) that includes b3bc60ae40 and republishes @openclaw/msteams with the matching version bump.
  2. Consider upgrading the silent catch { return false; } in createBotFrameworkJwtValidator.validate to log the error at warn level — silent JWT validation failures are nearly impossible to diagnose without source patching.
  3. Add a JWT validation smoke test against a fixture token in CI to catch this regression.

Environment

  • openclaw: 2026.4.26
  • @openclaw/msteams: 2026.4.25
  • Node: v22.22.0
  • OS: Debian 13 (Linux 6.12.74+deb13+1-amd64)
  • Bot Framework: standard Microsoft Teams channel via Azure Bot resource (Single Tenant)

Happy to provide a PR for both the source-level guarantee that the bundler can't strip the wrappers, and the catch-block logging improvement, if it would help. Thanks for OpenClaw — once we got past this, two-way comms have been rock solid. 🦞

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions