Skip to content

[Bug]: Gateway JWT middleware blocks Bot Framework webhooks in msteams plugin #14436

@dev-sfx

Description

@dev-sfx

Summary

The OpenClaw gateway applies JWT authentication middleware globally to all routes, which blocks legitimate Microsoft Bot Framework webhooks at /api/messages. This prevents the msteams plugin from receiving webhooks from Azure Bot Service.

Environment

  • OpenClaw version: 2026.2.9
  • Node.js: v24.13.0
  • OS: Windows 10.0.26100 (x64)
  • Plugin: msteams (built-in)

Steps to Reproduce

  1. Configure msteams plugin with valid Azure Bot credentials:
    "channels": {
      "msteams": {
        "enabled": true,
        "appId": "<valid-app-id>",
        "appPassword": "<valid-password>",
        "tenantId": "<valid-tenant-id>"
      }
    }

Start OpenClaw gateway (msteams plugin creates webhook server on port 3978)

Configure Azure Bot Service messaging endpoint: https:///api/messages

Send a test message to the bot in Microsoft Teams

Expected Behavior

  • Bot Framework sends webhook with Microsoft's JWT token
  • msteams plugin's Bot Framework adapter validates the Microsoft JWT
  • Webhook is processed successfully
  • Bot responds in Teams

Actual Behavior

  • Bot Framework sends webhook with Microsoft's JWT token
  • OpenClaw gateway JWT middleware intercepts the request first
  • Gateway expects OpenClaw's own JWT token format
  • Request is rejected with: {"jwt-auth-error":"authorization header not found"} (401)
  • Webhook never reaches the Bot Framework adapter
  • Bot does not respond

Log evidence:

$ curl -X POST http://localhost:3978/api/messages -H "Content-Type: application/json" -d "{}" {"jwt-auth-error":"authorization header not found"}

Root Cause

The gateway applies JWT authentication middleware to all routes on the msteams webhook server. The Bot Framework adapter at /api/messages has its own JWT validation logic (validates Microsoft-signed tokens), but the OpenClaw JWT middleware intercepts requests before they reach the adapter.

Two incompatible JWT systems:

  1. OpenClaw Gateway JWT - Internal authentication for OpenClaw API endpoints
  2. Bot Framework JWT - Microsoft's authentication for webhook callbacks

The /api/messages endpoint should bypass OpenClaw JWT and only use Bot Framework JWT validation.

Workaround

Created a reverse proxy that strips OpenClaw JWT requirements:

// Proxy: localhost:4000 -> localhost:3978
app.all('/api/messages', async (req, res) => {
  const headers = { ...req.headers };
  delete headers['x-openclaw-token'];
  
  const response = await axios({
    method: req.method,
    url: 'http://localhost:3978/api/messages',
    data: req.body,
    headers
  });
  
  res.status(response.status).send(response.data);
});

Architecture with workaround:

Bot Service → Cloudflare Tunnel → Proxy (4000) → OpenClaw msteams (3978)
                                   ↑ JWT bypass here

Suggested Fix

The msteams plugin should exclude /api/messages from OpenClaw JWT middleware:

// Option 1: Exclude path from gateway JWT
app.use((req, res, next) => {
  if (req.path === '/api/messages') {
    return next(); // Skip OpenClaw JWT for Bot Framework endpoint
  }
  openclawJwtAuth(req, res, next); // Apply JWT to other routes
});

app.post('/api/messages', adapter.process); // Bot Framework handles JWT here

// Option 2: Register route BEFORE middleware
app.post('/api/messages', adapter.process); // Register first
app.use(openclawJwtAuth); // Apply JWT to everything else

The Bot Framework adapter already validates Microsoft JWTs internally via ConfigurationBotFrameworkAuthentication, so no additional authentication is needed on this route.

Additional Context

Bot Framework JWT validation (already built-in):

import { CloudAdapter, ConfigurationBotFrameworkAuthentication } from '@microsoft/agents-hosting';

const auth = new ConfigurationBotFrameworkAuthentication({
  MicrosoftAppId: config.appId,
  MicrosoftAppPassword: config.appPassword,
  MicrosoftAppTenantId: config.tenantId
});

const adapter = new CloudAdapter(auth);
// Adapter validates Microsoft's JWT automatically in adapter.process()

This is a blocker for Teams integration - the bot cannot receive any messages from Azure Bot Service without bypassing OpenClaw's gateway entirely.

Related Documentation

Bot Framework Authentication
CloudAdapter JWT Validation

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingstaleMarked as stale due to inactivity

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