Skip to content

Security hardening: MCP server auth#540

Merged
corinagum merged 8 commits into
mainfrom
cg/mcp-auth-hardening
May 7, 2026
Merged

Security hardening: MCP server auth#540
corinagum merged 8 commits into
mainfrom
cg/mcp-auth-hardening

Conversation

@corinagum

@corinagum corinagum commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Addresses security scan finding #14 (MCP server mounted without auth). TypeScript half of a 2-SDK PR set (.NET PR is on the same branch name).

Note: Earlier revisions of this branch also addressed finding #15 (MCP client URL validation). #15 has been dropped — the SSRF risk against MCP client URLs is low-likelihood and the default private-network filter created friction for legitimate on-prem MCP customers. Reverted in commit fd3444f8.

#14 — MCP server auth

New opt-in requireAuth hook on McpPluginOptions:

new McpPlugin({
  requireAuth: (req) => req.headers.authorization === 'Bearer ...'
})

Hook is called once per inbound MCP request via an Express-level prefix middleware on /mcp (covers any current or future handler registered under that path). false/throw returns 401. When unset, all requests are accepted and a one-time startup warning fires when the SSE transport comes up.

Design doc

design/mcp-server-auth-options.md

E2E verified

  • Middleware gating: /mcp returns 401 without auth, SSE handshake on correct bearer
  • Activity path (/api/messages): bot responded to a devtools chat message with REQUIRE_AUTH=1 active simultaneously
  • Startup warning fires when requireAuth is unset; silent when set

@corinagum
corinagum marked this pull request as ready for review April 29, 2026 19:24
Copilot AI review requested due to automatic review settings April 29, 2026 19:24
@corinagum
corinagum force-pushed the cg/mcp-auth-hardening branch from 3332a5f to 9e5624e Compare April 29, 2026 19:27

Copilot AI left a comment

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds security hardening for MCP integrations by introducing opt-in MCP server authentication and default URL validation to mitigate client-side SSRF risks.

Changes:

  • Add requireAuth hook to McpPlugin to gate inbound MCP /mcp requests with 401 on failure.
  • Add validateMcpServerUrl URL gate (scheme + DNS/private-network filtering) and expose allowPrivateNetwork / validateUrl params in the MCP client.
  • Add/adjust Jest tests to cover URL validation and server auth behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
external/mcpclient/src/url-validation.ts Implements default URL scheme + private-network/DNS-based validation and error type.
external/mcpclient/src/url-validation.spec.ts Adds unit tests for URL validation behavior and edge cases.
external/mcpclient/src/mcp-client-types.ts Extends client params with allowPrivateNetwork and validateUrl.
external/mcpclient/src/mcp-client-plugin.ts Wires URL validation into MCP client creation flow.
external/mcpclient/src/mcp-client-plugin.spec.ts Stubs DNS lookup to keep existing tests deterministic/offline.
external/mcp/src/plugin.ts Adds requireAuth option, startup warning, and auth middleware for MCP SSE route prefix.
external/mcp/src/plugin.spec.ts Adds unit tests for new checkAuth behavior with various outcomes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread external/mcpclient/src/url-validation.ts Outdated
Comment thread external/mcpclient/src/url-validation.ts Outdated
Comment thread external/mcp/src/plugin.ts Outdated
Comment thread external/mcp/src/plugin.ts Outdated
@corinagum corinagum changed the title Security hardening: MCP server auth + client URL validation Security hardening: MCP server auth May 5, 2026
corinagum added 7 commits May 6, 2026 16:22
Per team decision, the MCP client SSRF guard adds friction for legitimate
on-prem MCP customers and the underlying risk is low-likelihood. Reverting
the client-side URL validation work; the McpPlugin server auth (#14) and the
Express-level path-prefix middleware refactor are kept.

Removes:
- external/mcpclient/src/url-validation.ts (and spec)
- McpClientPluginParams.allowPrivateNetwork + validateUrl
- validateMcpServerUrl call in makeMcpClient
- DNS mock added to mcp-client-plugin.spec.ts
@corinagum
corinagum force-pushed the cg/mcp-auth-hardening branch from fd3444f to 2d8f3a3 Compare May 6, 2026 23:22
In checkAuth, after the requireAuth try/catch, return early without
writing a 401 if req.aborted is set. The client has already disconnected;
attempting to set headers on a destroyed stream produces noisy warnings
without changing observable behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@corinagum
corinagum merged commit 642cdd1 into main May 7, 2026
7 checks passed
@corinagum
corinagum deleted the cg/mcp-auth-hardening branch May 7, 2026 17:30
corinagum added a commit to microsoft/teams-sdk that referenced this pull request May 27, 2026
## Summary

Adds a Trust Model page under Essentials → App Configuration documenting
the layered authentication model the SDK uses for inbound JSON Web
Tokens.

## Why

A scanner flagged the SDK's `JsonWebToken` accessor class because it
does not perform signature verification at the point of token parsing. A
cross-SDK audit confirmed this is intentional architecture: signature
verification runs at the HTTP boundary (via the activity pipeline's
token validator), and the accessor class is a typed view over
already-validated payloads. Every consumer of decoded claims is
downstream of a validator pass.

This page makes the architectural invariant explicit for SDK consumers,
reviewers, and future contributors.

## What's covered

- What the SDK validates automatically on `/api/messages` (signature,
issuer, audience, lifetime, algorithm)
- What downstream code can rely on (typed accessors over validated
tokens)
- How to add custom authentication to non-default surfaces (MCP,
callbacks, webhooks) — references the `requireAuth` hook recently added
in microsoft/teams.ts#540 / microsoft/teams.net#448
- Anti-patterns to avoid (constructing the accessor from untrusted input
as a basis for authorization decisions)

## Build verified

`npm run build` clean.

## Related PRs

- TypeScript: microsoft/teams.ts#586
- Python: microsoft/teams.py#432
- .NET: microsoft/teams.net#517

---------

Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants