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
|
|
@ -2,7 +2,8 @@
|
|||
/**
|
||||
* Companion example for `docs/serving/authorization.md`.
|
||||
*
|
||||
* Every `ts` fence on that page is synced from a `//#region` in this file
|
||||
* Every `ts` fence on that page except the web-standard one (sourced from
|
||||
* `authorization.web.examples.ts`) is synced from a `//#region` in this file
|
||||
* (`pnpm sync:snippets --check`). The Express middleware needs a listening
|
||||
* HTTP server and a real authorization server to exercise, so this file only
|
||||
* typechecks:
|
||||
|
|
|
|||
44
examples/guides/serving/authorization.web.examples.ts
Normal file
44
examples/guides/serving/authorization.web.examples.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// docs: typecheck-only
|
||||
/**
|
||||
* Companion example for the web-standard section of
|
||||
* `docs/serving/authorization.md`.
|
||||
*
|
||||
* Lives beside `authorization.examples.ts` in its own module because both
|
||||
* packages export a `requireBearerAuth`: the Express one is demonstrated
|
||||
* there, the web-standard one from `@modelcontextprotocol/server` here. Like
|
||||
* its sibling, this file only typechecks:
|
||||
*
|
||||
* pnpm --filter @modelcontextprotocol/examples typecheck
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
import type { AuthInfo, OAuthTokenVerifier } from '@modelcontextprotocol/server';
|
||||
import { createMcpHandler, McpServer, OAuthError, OAuthErrorCode, requireBearerAuth } from '@modelcontextprotocol/server';
|
||||
|
||||
declare function verifyJwt(token: string): Promise<{ sub: string; scopes: string[]; exp: number }>;
|
||||
|
||||
const verifier: OAuthTokenVerifier = {
|
||||
async verifyAccessToken(token): Promise<AuthInfo> {
|
||||
const payload = await verifyJwt(token).catch(() => {
|
||||
throw new OAuthError(OAuthErrorCode.InvalidToken, 'unknown token');
|
||||
});
|
||||
return { token, clientId: payload.sub, scopes: payload.scopes, expiresAt: payload.exp };
|
||||
}
|
||||
};
|
||||
|
||||
function buildServer(): McpServer {
|
||||
return new McpServer({ name: 'protected-server', version: '1.0.0' });
|
||||
}
|
||||
|
||||
//#region 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 });
|
||||
}
|
||||
};
|
||||
//#endregion requireBearerAuth_webStandard
|
||||
Loading…
Reference in a new issue