Skip to content

fix(msteams): accept Bot Framework audience in JWT validation (#58249)#62674

Merged
BradGroux merged 5 commits intoopenclaw:mainfrom
sudie-codes:fix/msteams-jwt-audience-58249
Apr 10, 2026
Merged

fix(msteams): accept Bot Framework audience in JWT validation (#58249)#62674
BradGroux merged 5 commits intoopenclaw:mainfrom
sudie-codes:fix/msteams-jwt-audience-58249

Conversation

@sudie-codes
Copy link
Copy Markdown
Contributor

Summary

  • Replaces @microsoft/teams.apps JwtValidator with direct jsonwebtoken + jwks-rsa for webhook JWT validation
  • Adds https://api.botframework.com to the accepted audience list, matching the legacy @microsoft/agents-hosting behavior
  • The Teams SDK's JwtValidator hardcodes audience to [clientId, api://clientId] with no way to extend it, causing valid Bot Framework tokens to be rejected

What changed

  • sdk.ts: createBotFrameworkJwtValidator now uses jsonwebtoken.verify() directly with audience [appId, api://appId, https://api.botframework.com] and issuer-specific JWKS endpoints (Bot Framework, Entra, STS Windows)
  • sdk.test.ts: 8 new tests covering audience validation, issuer allowlist, signature failures, missing kid/issuer edge cases
  • package.json: Added jsonwebtoken, jwks-rsa, @types/jsonwebtoken as explicit extension deps

Fixes #58249

Test plan

  • JWT validator tests pass (8/8) — audience, issuer, signature, edge cases
  • Manual verification with a live Teams bot receiving webhook messages
  • Verify tokens with aud: "https://api.botframework.com" are now accepted

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: M labels Apr 7, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 7, 2026

Greptile Summary

This PR replaces the @microsoft/teams.apps JwtValidator with a direct jsonwebtoken + jwks-rsa implementation to include https://api.botframework.com in the accepted audience list, fixing the root cause of #58249 where valid Bot Framework service tokens were rejected. The new validator correctly enforces signature verification (issuer-specific JWKS endpoints), audience, issuer allowlist, and expiry; 8 new tests cover the critical paths including the aud: https://api.botframework.com scenario.

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 style suggestions, no correctness or security issues.

The implementation correctly enforces all JWT security invariants: signature via issuer-specific JWKS, audience allowlist, issuer allowlist, and clock-tolerant expiry. Only two minor P2 findings remain: an unused optional parameter in the return type signature, and a missing test for the STS Windows v1 issuer path.

No files require special attention.

Comments Outside Diff (1)

  1. extensions/msteams/src/sdk.test.ts, line 194-272 (link)

    P2 STS Windows v1 issuer has no test coverage

    BOT_FRAMEWORK_ISSUERS includes a third entry for https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/ but none of the 8 new test cases exercise it. The Bot Framework and Entra v2 issuers are covered — adding a case like jwtState.decodedPayload = { iss: "https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/" } would complete the issuer-allowlist coverage.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/msteams/src/sdk.test.ts
    Line: 194-272
    
    Comment:
    **STS Windows v1 issuer has no test coverage**
    
    `BOT_FRAMEWORK_ISSUERS` includes a third entry for `https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/` but none of the 8 new test cases exercise it. The Bot Framework and Entra v2 issuers are covered — adding a case like `jwtState.decodedPayload = { iss: "https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/" }` would complete the issuer-allowlist coverage.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/msteams/src/sdk.ts
Line: 512-513

Comment:
**Unused `serviceUrl` parameter in return type**

The declared return type includes `validate: (authHeader: string, serviceUrl?: string) => Promise<boolean>`, but the implementation only accepts `authHeader` — the optional second argument is silently dropped. `monitor.ts` also calls `validate(authHeader)` without a service URL. Either remove the parameter from the return-type signature for a clean contract, or accept it as `_serviceUrl` to make the intentional omission explicit.

```suggestion
    async validate(authHeader: string, _serviceUrl?: string): Promise<boolean> {
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/msteams/src/sdk.test.ts
Line: 194-272

Comment:
**STS Windows v1 issuer has no test coverage**

`BOT_FRAMEWORK_ISSUERS` includes a third entry for `https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/` but none of the 8 new test cases exercise it. The Bot Framework and Entra v2 issuers are covered — adding a case like `jwtState.decodedPayload = { iss: "https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/" }` would complete the issuer-allowlist coverage.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(msteams): use jsonwebtoken directly ..." | Re-trigger Greptile

Comment thread extensions/msteams/src/sdk.ts Outdated
Comment on lines +512 to +513
return {
async validate(authHeader: string, serviceUrl?: string): Promise<boolean> {
async validate(authHeader: string): Promise<boolean> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unused serviceUrl parameter in return type

The declared return type includes validate: (authHeader: string, serviceUrl?: string) => Promise<boolean>, but the implementation only accepts authHeader — the optional second argument is silently dropped. monitor.ts also calls validate(authHeader) without a service URL. Either remove the parameter from the return-type signature for a clean contract, or accept it as _serviceUrl to make the intentional omission explicit.

Suggested change
return {
async validate(authHeader: string, serviceUrl?: string): Promise<boolean> {
async validate(authHeader: string): Promise<boolean> {
async validate(authHeader: string, _serviceUrl?: string): Promise<boolean> {
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/sdk.ts
Line: 512-513

Comment:
**Unused `serviceUrl` parameter in return type**

The declared return type includes `validate: (authHeader: string, serviceUrl?: string) => Promise<boolean>`, but the implementation only accepts `authHeader` — the optional second argument is silently dropped. `monitor.ts` also calls `validate(authHeader)` without a service URL. Either remove the parameter from the return-type signature for a clean contract, or accept it as `_serviceUrl` to make the intentional omission explicit.

```suggestion
    async validate(authHeader: string, _serviceUrl?: string): Promise<boolean> {
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 831b89d645

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +10 to +11
"jsonwebtoken": "^9.0.3",
"jwks-rsa": "^4.0.1"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update lockfile alongside new msteams dependencies

Adding jsonwebtoken/jwks-rsa (and new dev typings) in extensions/msteams/package.json without updating pnpm-lock.yaml leaves the workspace lock out of sync; on any fresh checkout or CI lane that runs pnpm install --frozen-lockfile, install will fail because the extensions/msteams importer in the lockfile does not include these new specifiers yet.

Useful? React with 👍 / 👎.

@BradGroux BradGroux merged commit a59a9bf into openclaw:main Apr 10, 2026
33 of 35 checks passed
steipete pushed a commit that referenced this pull request Apr 10, 2026
#62674)

* fix(msteams): use jsonwebtoken directly for JWT validation with correct audience (#58249)

* chore(msteams): regenerate lockfile for jwt deps

* fix(msteams): clean up unused serviceUrl parameter in JWT validator

* test(msteams): cover STS issuer in JWT validation

* fix(msteams): type jwt verify audiences and issuers

---------

Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…aw#58249) (openclaw#62674)

* fix(msteams): use jsonwebtoken directly for JWT validation with correct audience (openclaw#58249)

* chore(msteams): regenerate lockfile for jwt deps

* fix(msteams): clean up unused serviceUrl parameter in JWT validator

* test(msteams): cover STS issuer in JWT validation

* fix(msteams): type jwt verify audiences and issuers

---------

Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…aw#58249) (openclaw#62674)

* fix(msteams): use jsonwebtoken directly for JWT validation with correct audience (openclaw#58249)

* chore(msteams): regenerate lockfile for jwt deps

* fix(msteams): clean up unused serviceUrl parameter in JWT validator

* test(msteams): cover STS issuer in JWT validation

* fix(msteams): type jwt verify audiences and issuers

---------

Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…aw#58249) (openclaw#62674)

* fix(msteams): use jsonwebtoken directly for JWT validation with correct audience (openclaw#58249)

* chore(msteams): regenerate lockfile for jwt deps

* fix(msteams): clean up unused serviceUrl parameter in JWT validator

* test(msteams): cover STS issuer in JWT validation

* fix(msteams): type jwt verify audiences and issuers

---------

Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Teams webhook broken in 2026.3.24+: publicUrl removed breaks JWT validation

2 participants