feat(server): runtime-neutral requireBearerAuth for web-standard hosts (#2420)
This commit is contained in:
parent
79dc162efc
commit
7635115d01
2 changed files with 595 additions and 129 deletions
|
|
@ -64,8 +64,8 @@ The codemod ([`@modelcontextprotocol/codemod`](https://github.com/modelcontextpr
|
|||
mechanically applies every rename whose mapping is fixed. The mappings are the
|
||||
**source of truth** — they live in the codemod package and are not reproduced here:
|
||||
|
||||
| Mapping | Source file |
|
||||
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| Mapping | Source file |
|
||||
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `@modelcontextprotocol/sdk/...` import paths → v2 packages | [`mappings/importMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts) |
|
||||
| Symbol renames (`McpError` → `ProtocolError`, `JSONRPCError` → `JSONRPCErrorResponse`, …) | [`mappings/symbolMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/symbolMap.ts) |
|
||||
| `setRequestHandler(Schema, …)` → `setRequestHandler('method/string', …)` | [`mappings/schemaToMethodMap.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/codemod/src/migrations/v1-to-v2/mappings/schemaToMethodMap.ts) |
|
||||
|
|
@ -395,7 +395,9 @@ A few transports need a decision the codemod can't make:
|
|||
import path changes.
|
||||
- **Server auth split.** Resource Server helpers (`requireBearerAuth`,
|
||||
`mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`)
|
||||
→ `@modelcontextprotocol/express`. Authorization Server helpers (`mcpAuthRouter`,
|
||||
→ `@modelcontextprotocol/express`; the runtime-neutral core (`requireBearerAuth`
|
||||
for web-standard `fetch` hosts, `verifyBearerToken`, `bearerAuthChallengeResponse`,
|
||||
`OAuthTokenVerifier`) is also exported from `@modelcontextprotocol/server`. Authorization Server helpers (`mcpAuthRouter`,
|
||||
`OAuthServerProvider`, `ProxyOAuthServerProvider`, `allowedMethods`,
|
||||
`authenticateClient`, `metadataHandler`, `createOAuthMetadata`,
|
||||
`authorizationHandler` / `tokenHandler` / `revocationHandler` /
|
||||
|
|
@ -1036,7 +1038,8 @@ if (error instanceof OAuthError && error.code === OAuthErrorCode.InvalidClient)
|
|||
```
|
||||
|
||||
⚠ **Token verifiers must throw the v2 `OAuthError`.** `requireBearerAuth` (from
|
||||
`@modelcontextprotocol/express`) classifies the error your
|
||||
`@modelcontextprotocol/express`, or from `@modelcontextprotocol/server` on
|
||||
web-standard hosts) classifies the error your
|
||||
`OAuthTokenVerifier.verifyAccessToken()` throws: a v2
|
||||
`OAuthError(OAuthErrorCode.InvalidToken)` produces the proper `401` +
|
||||
`WWW-Authenticate` challenge, while the legacy `InvalidTokenError` (from
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
shape: how-to
|
||||
description: 'Require a bearer token on a server you run: verification, protected-resource metadata, and per-tool scopes.'
|
||||
---
|
||||
|
||||
# Require authorization
|
||||
|
||||
Protecting a server you run → this page. Signing a user in from a client you build → [Authenticate a user with OAuth](../clients/oauth.md). No user present → [Authenticate without a user](../clients/machine-auth.md).
|
||||
|
|
@ -42,6 +43,25 @@ A request with a missing, malformed, or expired token gets `401` with the OAuth
|
|||
The Authorization Server helpers (`mcpAuthRouter`, `ProxyOAuthServerProvider`, …) are frozen in `@modelcontextprotocol/server-legacy/auth`. Use a dedicated identity provider for new servers; this page only covers the resource-server half.
|
||||
:::
|
||||
|
||||
## Require a bearer token on a web-standard host
|
||||
|
||||
On hosts whose HTTP surface is a `fetch(request)` handler — Cloudflare Workers, Deno, Bun, Hono — the gate is `requireBearerAuth` from `@modelcontextprotocol/server`: no framework, only web-standard `Request` and `Response`.
|
||||
|
||||
```ts source="../../examples/guides/serving/authorization.web.examples.ts#requireBearerAuth_webStandard"
|
||||
const gate = requireBearerAuth({ verifier, requiredScopes: ['mcp'] });
|
||||
const handler = createMcpHandler(buildServer);
|
||||
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const auth = await gate(request);
|
||||
if (auth instanceof Response) return auth;
|
||||
return handler.fetch(request, { authInfo: auth });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The gate resolves to the verified `AuthInfo` — pass it to the handler as `{ authInfo }` and handlers read it as `ctx.http.authInfo` — or to the ready-to-return challenge `Response`. Status codes, error bodies, and the `WWW-Authenticate` challenge (including `resourceMetadataUrl`) are identical to the Express middleware: both are adapters over one core, so a verifier written for one serves the other unchanged.
|
||||
|
||||
## Verify tokens your way
|
||||
|
||||
`verifyAccessToken` is the one function you supply: take the raw token string, return an `AuthInfo`. Local JWT verification, [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662) introspection, or a call to your identity provider all fit behind it.
|
||||
|
|
@ -107,6 +127,7 @@ Responding `403 insufficient_scope` at the HTTP layer instead triggers the clien
|
|||
|
||||
## Recap
|
||||
|
||||
- `requireBearerAuth` from `@modelcontextprotocol/server` is the same gate for web-standard `fetch` hosts; the Express middleware adapts the same core.
|
||||
- `requireBearerAuth` plus a `verifyAccessToken` you write turn an Express-mounted MCP route into an OAuth resource server; the SDK never issues tokens.
|
||||
- Missing, invalid, or expired tokens get `401 invalid_token`; a token missing a `requiredScopes` entry gets `403 insufficient_scope`; both carry a `WWW-Authenticate: Bearer` challenge.
|
||||
- `mcpAuthMetadataRouter` publishes the RFC 9728 document that challenge points at, plus a mirror of the AS metadata.
|
||||
|
|
|
|||
Loading…
Reference in a new issue