openclaw - ✅(Solved) Fix [Bug] MS Teams SingleTenant bot: JWT validation fails (issuer + audience mismatch) [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#64270Fetched 2026-04-11 06:15:37
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
cross-referenced ×2referenced ×2

When using a SingleTenant Azure Bot with the MS Teams channel, incoming webhook requests from Azure Bot Service are rejected with 401 Unauthorized. This affects all new bot deployments since Microsoft deprecated MultiTenant bot creation after 2025-07-31.

Error Message

Error Flow

Root Cause

Two issues in the JWT validation chain:

1. Issuer mismatch in validateIssuer() (jwt-validator)

The allowedTenantIds issuer check only accepts:

https://login.microsoftonline.com/{tenantId}/

But SingleTenant Bot Framework tokens use the v1 issuer:

https://sts.windows.net/{tenantId}/

2. Audience mismatch in entraValidator (graph-users / msteams extension)

The entraValidator in createBotFrameworkJwtValidator() does not include https://api.botframework.com in its accepted audiences. The default audience list is [clientId, "api://botid-{clientId}", "api://{clientId}"], but Bot Framework tokens always have aud: "https://api.botframework.com".

Fix Action

Workaround

Patch two files in the container:

jwt-validator-*.js - extend issuer check:

// Before:
if (!allowedTenantIds.some((tenantId) => iss.startsWith(`https://login.microsoftonline.com/${tenantId}/`)))

// After:
if (!allowedTenantIds.some((tenantId) => iss.startsWith(`https://login.microsoftonline.com/${tenantId}/`) || iss.startsWith(`https://sts.windows.net/${tenantId}/`)))

graph-users-*.js - add BF audience to entraValidator:

const entraValidator = new JwtValidator({
    clientId: creds.appId,
    tenantId: creds.tenantId,
    audience: ["https://api.botframework.com"],  // ADD THIS
    validateIssuer: { allowedTenantIds: [creds.tenantId] },
    jwksUriOptions: { type: "uri", uri: "https://login.microsoftonline.com/common/discovery/v2.0/keys" }
});

PR fix notes

PR #64276: fix(msteams): use dynamic tenant ID for sts.windows.net JWT issuer

Description (problem / solution / changelog)

Summary

  • The sts.windows.net issuer entry in BOT_FRAMEWORK_ISSUERS was hardcoded to a single tenant ID (d6d49420-f39b-4df7-a1dc-d59a935871db), causing JWT validation to reject tokens from all other SingleTenant bot deployments
  • Changed the static string to a dynamic function (tenantId) => \https://sts.windows.net/${tenantId}/\`` matching the pattern used by the login.microsoftonline.com entry
  • Updated the corresponding test to use the test credential's tenantId instead of the hardcoded value

Context

SingleTenant Azure Bot registrations (required since Microsoft deprecated MultiTenant after 2025-07-31) issue tokens with issuer https://sts.windows.net/{tenantId}/ (Azure AD v1 endpoint). The current code only accepts this issuer for one specific tenant, so every other SingleTenant deployment gets 401 Unauthorized on all incoming Bot Framework webhooks.

Fixes #64270

Test plan

  • Existing test "validates a token with STS Windows issuer" now uses dynamic creds.tenantId instead of hardcoded UUID
  • Verify vitest run --config vitest.extension-msteams.config.ts passes
  • Deploy a SingleTenant bot and confirm incoming Teams messages no longer return 401

🤖 Generated with Claude Code

Changed files

  • extensions/msteams/src/sdk.test.ts (modified, +2/-2)
  • extensions/msteams/src/sdk.ts (modified, +1/-1)

PR #64641: fix(msteams): accept SingleTenant sts.windows.net issuer in JWT validator (#64270)

Description (problem / solution / changelog)

Summary

Fixes SingleTenant bot JWT validation failures. SingleTenant has been Microsoft's default bot type since 2025-07-31, so every new Teams bot deployment was hitting 401 Unauthorized on incoming webhooks.

Root cause

The sts.windows.net v1 issuer entry in BOT_FRAMEWORK_ISSUERS was hardcoded to one specific tenant UUID (d6d49420-f39b-4df7-a1dc-d59a935871db). SingleTenant tokens carry issuer https://sts.windows.net/{tenantId}/ scoped to the bot's own tenant, so every other deployment failed issuer validation.

The second half of the bug report, missing https://api.botframework.com from the audience list, was already fixed in #62674. The current createBotFrameworkJwtValidator builds allowedAudiences = [appId, api://appId, https://api.botframework.com], so no further audience changes are needed. Verified against extensions/msteams/src/sdk.ts.

Fix

  • Change the sts.windows.net entry from a hardcoded string to (tenantId) => \https://sts.windows.net/${tenantId}/\``, matching the shape of the existing login.microsoftonline.com/.../v2.0 v2 entry. createBotFrameworkJwtValidator already resolves function-form issuers against creds.tenantId, so no caller changes are needed.
  • Rename the existing STS Windows test to explicitly reference SingleTenant and #64270, and have it read creds.tenantId so it exercises the tenant-aware path.
  • Add a regression guardrail test: the previously hardcoded UUID must be rejected when the bot is configured for a different tenant. This prevents silently accepting cross-tenant tokens if the string ever gets reintroduced.

Note on related PR

#64276 (by @mschaepers, the issue reporter) proposes the same sts.windows.net fix. This PR builds on their approach with identical production semantics, plus an extra regression test for the cross-tenant case and a code comment that documents why the function form exists. If #64276 lands first, this PR becomes a small test-only follow-up.

Test plan

  • pnpm test extensions/msteams/src/sdk.test.ts (13 passed, including the two new SingleTenant cases)
  • CI green

Fixes #64270

Generated with Claude Code

Changed files

  • extensions/msteams/src/sdk.test.ts (modified, +19/-5)
  • extensions/msteams/src/sdk.ts (modified, +5/-1)

Code Example

https://login.microsoftonline.com/{tenantId}/

---

https://sts.windows.net/{tenantId}/

---

1. botFrameworkValidator -> SigningKeyNotFoundError
   (key not at login.botframework.com, signed with Azure AD keys)
2. entraValidator -> finds key at login.microsoftonline.com/common
   -> signature valid
   -> issuer check FAILS (sts.windows.net not accepted)
   -> OR audience check FAILS (api.botframework.com not in list)

---

// Before:
if (!allowedTenantIds.some((tenantId) => iss.startsWith(`https://login.microsoftonline.com/${tenantId}/`)))

// After:
if (!allowedTenantIds.some((tenantId) => iss.startsWith(`https://login.microsoftonline.com/${tenantId}/`) || iss.startsWith(`https://sts.windows.net/${tenantId}/`)))

---

const entraValidator = new JwtValidator({
    clientId: creds.appId,
    tenantId: creds.tenantId,
    audience: ["https://api.botframework.com"],  // ADD THIS
    validateIssuer: { allowedTenantIds: [creds.tenantId] },
    jwksUriOptions: { type: "uri", uri: "https://login.microsoftonline.com/common/discovery/v2.0/keys" }
});
RAW_BUFFERClick to expand / collapse

Description

When using a SingleTenant Azure Bot with the MS Teams channel, incoming webhook requests from Azure Bot Service are rejected with 401 Unauthorized. This affects all new bot deployments since Microsoft deprecated MultiTenant bot creation after 2025-07-31.

Root Cause

Two issues in the JWT validation chain:

1. Issuer mismatch in validateIssuer() (jwt-validator)

The allowedTenantIds issuer check only accepts:

https://login.microsoftonline.com/{tenantId}/

But SingleTenant Bot Framework tokens use the v1 issuer:

https://sts.windows.net/{tenantId}/

2. Audience mismatch in entraValidator (graph-users / msteams extension)

The entraValidator in createBotFrameworkJwtValidator() does not include https://api.botframework.com in its accepted audiences. The default audience list is [clientId, "api://botid-{clientId}", "api://{clientId}"], but Bot Framework tokens always have aud: "https://api.botframework.com".

Error Flow

1. botFrameworkValidator -> SigningKeyNotFoundError
   (key not at login.botframework.com, signed with Azure AD keys)
2. entraValidator -> finds key at login.microsoftonline.com/common
   -> signature valid
   -> issuer check FAILS (sts.windows.net not accepted)
   -> OR audience check FAILS (api.botframework.com not in list)

Workaround

Patch two files in the container:

jwt-validator-*.js - extend issuer check:

// Before:
if (!allowedTenantIds.some((tenantId) => iss.startsWith(`https://login.microsoftonline.com/${tenantId}/`)))

// After:
if (!allowedTenantIds.some((tenantId) => iss.startsWith(`https://login.microsoftonline.com/${tenantId}/`) || iss.startsWith(`https://sts.windows.net/${tenantId}/`)))

graph-users-*.js - add BF audience to entraValidator:

const entraValidator = new JwtValidator({
    clientId: creds.appId,
    tenantId: creds.tenantId,
    audience: ["https://api.botframework.com"],  // ADD THIS
    validateIssuer: { allowedTenantIds: [creds.tenantId] },
    jwksUriOptions: { type: "uri", uri: "https://login.microsoftonline.com/common/discovery/v2.0/keys" }
});

Environment

  • OpenClaw version: 2026.4.9 (Docker image ghcr.io/openclaw/openclaw:latest)
  • Azure Bot: SingleTenant (msaAppType: "SingleTenant")
  • Channel: MS Teams
  • Azure AD Tenant: Single-org (AzureADMyOrg)

Expected Behavior

SingleTenant bot tokens should be accepted by the msteams channel JWT validator, as the docs state SingleTenant is the recommended approach.

extent analysis

TL;DR

To fix the 401 Unauthorized issue with SingleTenant Azure Bot and MS Teams channel, update the jwt-validator and graph-users files to correctly validate the issuer and audience in the JWT token.

Guidance

  • Update the jwt-validator file to extend the issuer check to include https://sts.windows.net/{tenantId}/ in addition to https://login.microsoftonline.com/{tenantId}/.
  • Update the graph-users file to add https://api.botframework.com to the accepted audiences in the entraValidator.
  • Verify that the updated files are correctly patched in the container.
  • Test the bot deployment again to ensure that the 401 Unauthorized issue is resolved.

Example

The updated code snippets for the jwt-validator and graph-users files are provided in the issue description and can be used as a reference for the changes.

Notes

The provided workaround is specific to the OpenClaw version 2026.4.9 and may not apply to other versions. Additionally, the changes are made to the container files, so ensure that the updates are persisted and applied correctly.

Recommendation

Apply the workaround by patching the jwt-validator and graph-users files as described, as this is the recommended approach to resolve the 401 Unauthorized issue with SingleTenant Azure Bot and MS Teams channel.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug] MS Teams SingleTenant bot: JWT validation fails (issuer + audience mismatch) [2 pull requests, 1 participants]