This commit is contained in:
CHEVALLIER Abel
2025-11-13 16:23:22 +01:00
parent de9c515a47
commit cb235644dc
34924 changed files with 3811102 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
import { RequestHandler } from "express";
import { OAuthServerProvider } from "../provider.js";
import { Options as RateLimitOptions } from "express-rate-limit";
export type AuthorizationHandlerOptions = {
provider: OAuthServerProvider;
/**
* Rate limiting configuration for the authorization endpoint.
* Set to false to disable rate limiting for this endpoint.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function authorizationHandler({ provider, rateLimit: rateLimitConfig }: AuthorizationHandlerOptions): RequestHandler;
//# sourceMappingURL=authorize.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW5E,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAkBF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,2BAA2B,GAAG,cAAc,CAkH1H"}

View File

@@ -0,0 +1,149 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.authorizationHandler = authorizationHandler;
const zod_1 = require("zod");
const express_1 = __importDefault(require("express"));
const express_rate_limit_1 = require("express-rate-limit");
const allowedMethods_js_1 = require("../middleware/allowedMethods.js");
const errors_js_1 = require("../errors.js");
// Parameters that must be validated in order to issue redirects.
const ClientAuthorizationParamsSchema = zod_1.z.object({
client_id: zod_1.z.string(),
redirect_uri: zod_1.z.string().optional().refine((value) => value === undefined || URL.canParse(value), { message: "redirect_uri must be a valid URL" }),
});
// Parameters that must be validated for a successful authorization request. Failure can be reported to the redirect URI.
const RequestAuthorizationParamsSchema = zod_1.z.object({
response_type: zod_1.z.literal("code"),
code_challenge: zod_1.z.string(),
code_challenge_method: zod_1.z.literal("S256"),
scope: zod_1.z.string().optional(),
state: zod_1.z.string().optional(),
resource: zod_1.z.string().url().optional(),
});
function authorizationHandler({ provider, rateLimit: rateLimitConfig }) {
// Create a router to apply middleware
const router = express_1.default.Router();
router.use((0, allowedMethods_js_1.allowedMethods)(["GET", "POST"]));
router.use(express_1.default.urlencoded({ extended: false }));
// Apply rate limiting unless explicitly disabled
if (rateLimitConfig !== false) {
router.use((0, express_rate_limit_1.rateLimit)({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: new errors_js_1.TooManyRequestsError('You have exceeded the rate limit for authorization requests').toResponseObject(),
...rateLimitConfig
}));
}
router.all("/", async (req, res) => {
var _a;
res.setHeader('Cache-Control', 'no-store');
// In the authorization flow, errors are split into two categories:
// 1. Pre-redirect errors (direct response with 400)
// 2. Post-redirect errors (redirect with error parameters)
// Phase 1: Validate client_id and redirect_uri. Any errors here must be direct responses.
let client_id, redirect_uri, client;
try {
const result = ClientAuthorizationParamsSchema.safeParse(req.method === 'POST' ? req.body : req.query);
if (!result.success) {
throw new errors_js_1.InvalidRequestError(result.error.message);
}
client_id = result.data.client_id;
redirect_uri = result.data.redirect_uri;
client = await provider.clientsStore.getClient(client_id);
if (!client) {
throw new errors_js_1.InvalidClientError("Invalid client_id");
}
if (redirect_uri !== undefined) {
if (!client.redirect_uris.includes(redirect_uri)) {
throw new errors_js_1.InvalidRequestError("Unregistered redirect_uri");
}
}
else if (client.redirect_uris.length === 1) {
redirect_uri = client.redirect_uris[0];
}
else {
throw new errors_js_1.InvalidRequestError("redirect_uri must be specified when client has multiple registered URIs");
}
}
catch (error) {
// Pre-redirect errors - return direct response
//
// These don't need to be JSON encoded, as they'll be displayed in a user
// agent, but OTOH they all represent exceptional situations (arguably,
// "programmer error"), so presenting a nice HTML page doesn't help the
// user anyway.
if (error instanceof errors_js_1.OAuthError) {
const status = error instanceof errors_js_1.ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new errors_js_1.ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
return;
}
// Phase 2: Validate other parameters. Any errors here should go into redirect responses.
let state;
try {
// Parse and validate authorization parameters
const parseResult = RequestAuthorizationParamsSchema.safeParse(req.method === 'POST' ? req.body : req.query);
if (!parseResult.success) {
throw new errors_js_1.InvalidRequestError(parseResult.error.message);
}
const { scope, code_challenge, resource } = parseResult.data;
state = parseResult.data.state;
// Validate scopes
let requestedScopes = [];
if (scope !== undefined) {
requestedScopes = scope.split(" ");
const allowedScopes = new Set((_a = client.scope) === null || _a === void 0 ? void 0 : _a.split(" "));
// Check each requested scope against allowed scopes
for (const scope of requestedScopes) {
if (!allowedScopes.has(scope)) {
throw new errors_js_1.InvalidScopeError(`Client was not registered with scope ${scope}`);
}
}
}
// All validation passed, proceed with authorization
await provider.authorize(client, {
state,
scopes: requestedScopes,
redirectUri: redirect_uri,
codeChallenge: code_challenge,
resource: resource ? new URL(resource) : undefined,
}, res);
}
catch (error) {
// Post-redirect errors - redirect with error parameters
if (error instanceof errors_js_1.OAuthError) {
res.redirect(302, createErrorRedirect(redirect_uri, error, state));
}
else {
const serverError = new errors_js_1.ServerError("Internal Server Error");
res.redirect(302, createErrorRedirect(redirect_uri, serverError, state));
}
}
});
return router;
}
/**
* Helper function to create redirect URL with error parameters
*/
function createErrorRedirect(redirectUri, error, state) {
const errorUrl = new URL(redirectUri);
errorUrl.searchParams.set("error", error.errorCode);
errorUrl.searchParams.set("error_description", error.message);
if (error.errorUri) {
errorUrl.searchParams.set("error_uri", error.errorUri);
}
if (state) {
errorUrl.searchParams.set("state", state);
}
return errorUrl.href;
}
//# sourceMappingURL=authorize.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { RequestHandler } from "express";
import { OAuthMetadata, OAuthProtectedResourceMetadata } from "../../../shared/auth.js";
export declare function metadataHandler(metadata: OAuthMetadata | OAuthProtectedResourceMetadata): RequestHandler;
//# sourceMappingURL=metadata.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/metadata.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AAIxF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,8BAA8B,GAAG,cAAc,CAaxG"}

View File

@@ -0,0 +1,21 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.metadataHandler = metadataHandler;
const express_1 = __importDefault(require("express"));
const cors_1 = __importDefault(require("cors"));
const allowedMethods_js_1 = require("../middleware/allowedMethods.js");
function metadataHandler(metadata) {
// Nested router so we can configure middleware and restrict HTTP method
const router = express_1.default.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use((0, cors_1.default)());
router.use((0, allowedMethods_js_1.allowedMethods)(['GET']));
router.get("/", (req, res) => {
res.status(200).json(metadata);
});
return router;
}
//# sourceMappingURL=metadata.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/metadata.ts"],"names":[],"mappings":";;;;;AAKA,0CAaC;AAlBD,sDAAkD;AAElD,gDAAwB;AACxB,uEAAiE;AAEjE,SAAgB,eAAe,CAAC,QAAwD;IACtF,wEAAwE;IACxE,MAAM,MAAM,GAAG,iBAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,kFAAkF;IAClF,MAAM,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;IAEnB,MAAM,CAAC,GAAG,CAAC,IAAA,kCAAc,EAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC3B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}

View File

@@ -0,0 +1,29 @@
import { RequestHandler } from "express";
import { OAuthRegisteredClientsStore } from "../clients.js";
import { Options as RateLimitOptions } from "express-rate-limit";
export type ClientRegistrationHandlerOptions = {
/**
* A store used to save information about dynamically registered OAuth clients.
*/
clientsStore: OAuthRegisteredClientsStore;
/**
* The number of seconds after which to expire issued client secrets, or 0 to prevent expiration of client secrets (not recommended).
*
* If not set, defaults to 30 days.
*/
clientSecretExpirySeconds?: number;
/**
* Rate limiting configuration for the client registration endpoint.
* Set to false to disable rate limiting for this endpoint.
* Registration endpoints are particularly sensitive to abuse and should be rate limited.
*/
rateLimit?: Partial<RateLimitOptions> | false;
/**
* Whether to generate a client ID before calling the client registration endpoint.
*
* If not set, defaults to true.
*/
clientIdGeneration?: boolean;
};
export declare function clientRegistrationHandler({ clientsStore, clientSecretExpirySeconds, rateLimit: rateLimitConfig, clientIdGeneration, }: ClientRegistrationHandlerOptions): RequestHandler;
//# sourceMappingURL=register.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/register.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAIlD,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAS5E,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;OAEG;IACH,YAAY,EAAE,2BAA2B,CAAC;IAE1C;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;IAE9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAIF,wBAAgB,yBAAyB,CAAC,EACxC,YAAY,EACZ,yBAAgE,EAChE,SAAS,EAAE,eAAe,EAC1B,kBAAyB,GAC1B,EAAE,gCAAgC,GAAG,cAAc,CA0EnD"}

View File

@@ -0,0 +1,79 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.clientRegistrationHandler = clientRegistrationHandler;
const express_1 = __importDefault(require("express"));
const auth_js_1 = require("../../../shared/auth.js");
const node_crypto_1 = __importDefault(require("node:crypto"));
const cors_1 = __importDefault(require("cors"));
const express_rate_limit_1 = require("express-rate-limit");
const allowedMethods_js_1 = require("../middleware/allowedMethods.js");
const errors_js_1 = require("../errors.js");
const DEFAULT_CLIENT_SECRET_EXPIRY_SECONDS = 30 * 24 * 60 * 60; // 30 days
function clientRegistrationHandler({ clientsStore, clientSecretExpirySeconds = DEFAULT_CLIENT_SECRET_EXPIRY_SECONDS, rateLimit: rateLimitConfig, clientIdGeneration = true, }) {
if (!clientsStore.registerClient) {
throw new Error("Client registration store does not support registering clients");
}
// Nested router so we can configure middleware and restrict HTTP method
const router = express_1.default.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use((0, cors_1.default)());
router.use((0, allowedMethods_js_1.allowedMethods)(["POST"]));
router.use(express_1.default.json());
// Apply rate limiting unless explicitly disabled - stricter limits for registration
if (rateLimitConfig !== false) {
router.use((0, express_rate_limit_1.rateLimit)({
windowMs: 60 * 60 * 1000, // 1 hour
max: 20, // 20 requests per hour - stricter as registration is sensitive
standardHeaders: true,
legacyHeaders: false,
message: new errors_js_1.TooManyRequestsError('You have exceeded the rate limit for client registration requests').toResponseObject(),
...rateLimitConfig
}));
}
router.post("/", async (req, res) => {
res.setHeader('Cache-Control', 'no-store');
try {
const parseResult = auth_js_1.OAuthClientMetadataSchema.safeParse(req.body);
if (!parseResult.success) {
throw new errors_js_1.InvalidClientMetadataError(parseResult.error.message);
}
const clientMetadata = parseResult.data;
const isPublicClient = clientMetadata.token_endpoint_auth_method === 'none';
// Generate client credentials
const clientSecret = isPublicClient
? undefined
: node_crypto_1.default.randomBytes(32).toString('hex');
const clientIdIssuedAt = Math.floor(Date.now() / 1000);
// Calculate client secret expiry time
const clientsDoExpire = clientSecretExpirySeconds > 0;
const secretExpiryTime = clientsDoExpire ? clientIdIssuedAt + clientSecretExpirySeconds : 0;
const clientSecretExpiresAt = isPublicClient ? undefined : secretExpiryTime;
let clientInfo = {
...clientMetadata,
client_secret: clientSecret,
client_secret_expires_at: clientSecretExpiresAt,
};
if (clientIdGeneration) {
clientInfo.client_id = node_crypto_1.default.randomUUID();
clientInfo.client_id_issued_at = clientIdIssuedAt;
}
clientInfo = await clientsStore.registerClient(clientInfo);
res.status(201).json(clientInfo);
}
catch (error) {
if (error instanceof errors_js_1.OAuthError) {
const status = error instanceof errors_js_1.ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new errors_js_1.ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
});
return router;
}
//# sourceMappingURL=register.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/register.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
import { OAuthServerProvider } from "../provider.js";
import { RequestHandler } from "express";
import { Options as RateLimitOptions } from "express-rate-limit";
export type RevocationHandlerOptions = {
provider: OAuthServerProvider;
/**
* Rate limiting configuration for the token revocation endpoint.
* Set to false to disable rate limiting for this endpoint.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function revocationHandler({ provider, rateLimit: rateLimitConfig, }: RevocationHandlerOptions): RequestHandler;
//# sourceMappingURL=revoke.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"revoke.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/revoke.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAIlD,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAS5E,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,SAAS,EAAE,eAAe,GAC3B,EAAE,wBAAwB,GAAG,cAAc,CA8D3C"}

View File

@@ -0,0 +1,65 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.revocationHandler = revocationHandler;
const express_1 = __importDefault(require("express"));
const cors_1 = __importDefault(require("cors"));
const clientAuth_js_1 = require("../middleware/clientAuth.js");
const auth_js_1 = require("../../../shared/auth.js");
const express_rate_limit_1 = require("express-rate-limit");
const allowedMethods_js_1 = require("../middleware/allowedMethods.js");
const errors_js_1 = require("../errors.js");
function revocationHandler({ provider, rateLimit: rateLimitConfig, }) {
if (!provider.revokeToken) {
throw new Error("Auth provider does not support revoking tokens");
}
// Nested router so we can configure middleware and restrict HTTP method
const router = express_1.default.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use((0, cors_1.default)());
router.use((0, allowedMethods_js_1.allowedMethods)(["POST"]));
router.use(express_1.default.urlencoded({ extended: false }));
// Apply rate limiting unless explicitly disabled
if (rateLimitConfig !== false) {
router.use((0, express_rate_limit_1.rateLimit)({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 50, // 50 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: new errors_js_1.TooManyRequestsError("You have exceeded the rate limit for token revocation requests").toResponseObject(),
...rateLimitConfig,
}));
}
// Authenticate and extract client details
router.use((0, clientAuth_js_1.authenticateClient)({ clientsStore: provider.clientsStore }));
router.post("/", async (req, res) => {
res.setHeader("Cache-Control", "no-store");
try {
const parseResult = auth_js_1.OAuthTokenRevocationRequestSchema.safeParse(req.body);
if (!parseResult.success) {
throw new errors_js_1.InvalidRequestError(parseResult.error.message);
}
const client = req.client;
if (!client) {
// This should never happen
throw new errors_js_1.ServerError("Internal Server Error");
}
await provider.revokeToken(client, parseResult.data);
res.status(200).json({});
}
catch (error) {
if (error instanceof errors_js_1.OAuthError) {
const status = error instanceof errors_js_1.ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new errors_js_1.ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
});
return router;
}
//# sourceMappingURL=revoke.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"revoke.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/revoke.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
import { RequestHandler } from "express";
import { OAuthServerProvider } from "../provider.js";
import { Options as RateLimitOptions } from "express-rate-limit";
export type TokenHandlerOptions = {
provider: OAuthServerProvider;
/**
* Rate limiting configuration for the token endpoint.
* Set to false to disable rate limiting for this endpoint.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHandlerOptions): RequestHandler;
//# sourceMappingURL=token.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/token.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAIrD,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW5E,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAmBF,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,mBAAmB,GAAG,cAAc,CA4G1G"}

View File

@@ -0,0 +1,113 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.tokenHandler = tokenHandler;
const zod_1 = require("zod");
const express_1 = __importDefault(require("express"));
const cors_1 = __importDefault(require("cors"));
const pkce_challenge_1 = require("pkce-challenge");
const clientAuth_js_1 = require("../middleware/clientAuth.js");
const express_rate_limit_1 = require("express-rate-limit");
const allowedMethods_js_1 = require("../middleware/allowedMethods.js");
const errors_js_1 = require("../errors.js");
const TokenRequestSchema = zod_1.z.object({
grant_type: zod_1.z.string(),
});
const AuthorizationCodeGrantSchema = zod_1.z.object({
code: zod_1.z.string(),
code_verifier: zod_1.z.string(),
redirect_uri: zod_1.z.string().optional(),
resource: zod_1.z.string().url().optional(),
});
const RefreshTokenGrantSchema = zod_1.z.object({
refresh_token: zod_1.z.string(),
scope: zod_1.z.string().optional(),
resource: zod_1.z.string().url().optional(),
});
function tokenHandler({ provider, rateLimit: rateLimitConfig }) {
// Nested router so we can configure middleware and restrict HTTP method
const router = express_1.default.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use((0, cors_1.default)());
router.use((0, allowedMethods_js_1.allowedMethods)(["POST"]));
router.use(express_1.default.urlencoded({ extended: false }));
// Apply rate limiting unless explicitly disabled
if (rateLimitConfig !== false) {
router.use((0, express_rate_limit_1.rateLimit)({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 50, // 50 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: new errors_js_1.TooManyRequestsError('You have exceeded the rate limit for token requests').toResponseObject(),
...rateLimitConfig
}));
}
// Authenticate and extract client details
router.use((0, clientAuth_js_1.authenticateClient)({ clientsStore: provider.clientsStore }));
router.post("/", async (req, res) => {
res.setHeader('Cache-Control', 'no-store');
try {
const parseResult = TokenRequestSchema.safeParse(req.body);
if (!parseResult.success) {
throw new errors_js_1.InvalidRequestError(parseResult.error.message);
}
const { grant_type } = parseResult.data;
const client = req.client;
if (!client) {
// This should never happen
throw new errors_js_1.ServerError("Internal Server Error");
}
switch (grant_type) {
case "authorization_code": {
const parseResult = AuthorizationCodeGrantSchema.safeParse(req.body);
if (!parseResult.success) {
throw new errors_js_1.InvalidRequestError(parseResult.error.message);
}
const { code, code_verifier, redirect_uri, resource } = parseResult.data;
const skipLocalPkceValidation = provider.skipLocalPkceValidation;
// Perform local PKCE validation unless explicitly skipped
// (e.g. to validate code_verifier in upstream server)
if (!skipLocalPkceValidation) {
const codeChallenge = await provider.challengeForAuthorizationCode(client, code);
if (!(await (0, pkce_challenge_1.verifyChallenge)(code_verifier, codeChallenge))) {
throw new errors_js_1.InvalidGrantError("code_verifier does not match the challenge");
}
}
// Passes the code_verifier to the provider if PKCE validation didn't occur locally
const tokens = await provider.exchangeAuthorizationCode(client, code, skipLocalPkceValidation ? code_verifier : undefined, redirect_uri, resource ? new URL(resource) : undefined);
res.status(200).json(tokens);
break;
}
case "refresh_token": {
const parseResult = RefreshTokenGrantSchema.safeParse(req.body);
if (!parseResult.success) {
throw new errors_js_1.InvalidRequestError(parseResult.error.message);
}
const { refresh_token, scope, resource } = parseResult.data;
const scopes = scope === null || scope === void 0 ? void 0 : scope.split(" ");
const tokens = await provider.exchangeRefreshToken(client, refresh_token, scopes, resource ? new URL(resource) : undefined);
res.status(200).json(tokens);
break;
}
// Not supported right now
//case "client_credentials":
default:
throw new errors_js_1.UnsupportedGrantTypeError("The grant type is not supported by this authorization server.");
}
}
catch (error) {
if (error instanceof errors_js_1.OAuthError) {
const status = error instanceof errors_js_1.ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new errors_js_1.ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
});
return router;
}
//# sourceMappingURL=token.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/token.ts"],"names":[],"mappings":"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"}