-
-
Notifications
You must be signed in to change notification settings - Fork 69.6k
[Bug]: Teams webhook broken in 2026.3.24+: publicUrl removed breaks JWT validation #58249
Description
Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
Description
After the Teams SDK migration in PR #51808 (2026.3.24+), Microsoft Teams webhook messages no longer arrive at OpenClaw. The webhook endpoint responds with 401 Unauthorized to all inbound requests.
Root Cause
In version 2026.3.23 and earlier, OpenClaw's Teams configuration included a gateway.publicUrl field:
{
"gateway": {
"publicUrl": "[tailscale-domain]"
}
}
This field was removed in 2026.3.24+ when the Teams extension was migrated to the official Microsoft Teams SDK (PR #51808). However, the Teams SDK requires knowledge of the public webhook URL for proper JWT token validation:
- Microsoft Bot Framework sends a JWT token signed with the bot's credentials
- The Teams SDK's createServiceTokenValidator() must validate this token
- Token validation requires the service URL (the public webhook endpoint) to verify the token claim
- Without publicUrl, OpenClaw cannot construct the correct service URL, causing validation to fail with 401 Unauthorized
Impact
• Teams DMs and channel messages do not reach the bot
• The webhook endpoint (/api/messages) accepts requests but rejects them as unauthorized
• Users cannot interact with the OpenClaw bot in Teams
• Downgrade to 2026.3.23 or earlier is required as a workaround
Environment
• OpenClaw version: 2026.3.28 (and 2026.3.24-2026.3.27)
• Teams plugin version: 2026.3.13+
• Configuration: Teams webhook on port 3978, Tailscale routing to /teams/api/messages
Steps to Reproduce
- Update OpenClaw to 2026.3.24 or later
- Configure Teams bot with endpoint https://[public-url]/teams/api/messages
- Send a DM or message to the bot in Teams
- Observe: No message received, webhook logs show 401 Unauthorized
Potential Fix
Option A: Restore publicUrl field (Recommended)
Add publicUrl back to the gateway config schema and Teams extension:
// openclaw.json
{
"gateway": {
"port": [PORT],
"publicUrl": "[URL]"
},
"channels": {
"msteams": {
"webhook": { "port": [PORT], "path": "/api/messages" }
}
}
}
Then in the Teams extension, use publicUrl to construct the service URL for JWT validation:
// src/extensions/msteams/bot-adapter.ts
const validator = createServiceTokenValidator({
microsoftAppId: appId,
microsoftAppPassword: appPassword,
// Use gateway.publicUrl + webhook.path as the service URL
serviceUrl: ${config.gateway?.publicUrl}${config.channels?.msteams?.webhook?.path || '/api/messages'}
});
Option B: Auto-detect service URL from request headers
Extract the service URL from incoming webhook requests (Teams includes this in the HTTP headers or JWT claims):
// Fallback: read serviceUrl from the JWT or request payload
const incomingServiceUrl = activity.serviceUrl || request.headers['x-service-url'];
const validator = createServiceTokenValidator({
microsoftAppId: appId,
microsoftAppPassword: appPassword,
serviceUrl: incomingServiceUrl
});
Option C: Make serviceUrl optional in validator
If the Teams SDK's token validator supports optional service URL verification, disable strict URL validation for local/development setups:
const validator = createServiceTokenValidator({
microsoftAppId: appId,
microsoftAppPassword: appPassword,
validateServiceUrl: false // Accept tokens from any service URL
});
(Note: This is less secure and should only be used if the validator library supports it.)
Recommendation
Option A is the cleanest fix: Restore publicUrl as an optional gateway field and pass it to the Teams extension. This maintains backward compatibility and allows Teams webhook routing to work correctly.
If that's not viable, Option B (auto-detect from request) is a good fallback since Microsoft Bot Framework includes the service URL in webhook requests.
───
Workaround for users: Downgrade to OpenClaw 2026.3.23 or earlier until this is fixed.
Steps to reproduce
Steps to Reproduce
Prerequisites
• OpenClaw 2026.3.24 or later (any version affected by the Teams SDK migration)
• Microsoft Teams bot registered in Azure Bot Service
• Bot credentials (App ID and Client Secret)
• Public URL accessible from Microsoft Bot Framework (e.g., via Tailscale, ngrok, or public domain)
Setup
- Configure OpenClaw with Teams
Edit openclaw.json
cat > /root/.openclaw/openclaw.json << 'EOF'
{
"channels": {
"msteams": {
"enabled": true,
"appId": "your-app-id-here",
"appPassword": "your-client-secret-here",
"tenantId": "your-tenant-id-here",
"webhook": {
"port": [PORT],
"path": "/api/messages"
},
"dmPolicy": "open",
"groupPolicy": "open"
}
},
"gateway": {
"port": [PORT]
}
}
EOF
2. Set up public routing (via Tailscale or equivalent)
Route HTTPS traffic to the local webhook
tailscale serve --set-path /teams --bg 127.0.0.1:[PORT]
tailscale serve --bg 127.0.0.1:[PORT]
Verify
tailscale serve status
Output should show:
https://[your-tailscale-hostname]/ proxy http://127.0.0.1:[PORT]/
https://[your-tailscale-hostname]/teams proxy http://127.0.0.1:[PORT]/
-
Configure Teams Bot in Azure Portal
-
Navigate to Azure Bot Service → Your bot
-
Go to Configuration → Messaging endpoint
-
Set the endpoint to: https://[your-tailscale-hostname]/teams/api/messages
-
Save changes
-
Start OpenClaw Gateway
openclaw gateway start
or restart if already running
openclaw gateway restart
Reproduce the Issue
-
Send a Test Message
-
Open Microsoft Teams
-
Find your bot in the chat list (or add it to a channel)
-
Send a direct message to the bot: "Hello"
Expected behavior
Expected Behavior (2026.3.23 and earlier)
• Bot receives the message
• Bot responds with a reply
Actual behavior
Actual Behavior (2026.3.24+)
• Message is sent to Teams
• No response from the bot
• Check OpenClaw logs for 401 Unauthorized errors
OpenClaw version
2026.3.28 (also affects 2026.3.24 - 2026.3.27)
Operating system
Ubuntu 24.04.4 LTS
Install method
npm
Model
| Component | Configuration | | ---------------- | --------------------------------------------------------------------------------------------------------------------------- | | Default Agent | main (claude-haiku-4-5-20251001) | | Available Models |
- anthropic/claude-sonnet-4-6
- anthropic/claude-haiku-4-5-20251001
- anthropic/claude-opus-4-6
Provider / routing chain
Microsoft Teams Bot Message ↓ HTTPS Request to [tailscale-domain]/teams/api/messages ↓ Tailscale Magic DNS (resolves to IP) ↓ Tailscale Serve Proxy (routes /teams → http://127.0.0.1:[PORT]) ↓ OpenClaw Gateway (port [PORT], Teams extension) ↓ Teams SDK createServiceTokenValidator() ↓ ❌ FAILS: 401 Unauthorized (JWT validation fails - no publicUrl available)
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
Agent does not respond to bot messages after upgrading.
Additional information
No response