Skip to content

Commit cf39df2

Browse files
nickytonlineclaude
andcommitted
feat(security): apply rate limiting to OAuth endpoints
- Apply comprehensive rate limiting to /oauth/authorize and callback endpoints (100 req/15min) - Apply stricter rate limiting to /oauth/token endpoint (10 req/15min) - Include structured logging for rate limit violations with IP and user agent tracking - Use JSON-RPC 2.0 compliant error responses for rate limit exceeded scenarios - Protect against OAuth abuse and brute force attacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 10ec97e commit cf39df2

File tree

1 file changed

+66
-3
lines changed

1 file changed

+66
-3
lines changed

src/index.ts

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import express from "express";
2+
import rateLimit from "express-rate-limit";
23
import { randomUUID } from "node:crypto";
34
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
45
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -151,6 +152,68 @@ const mcpHandler = async (req: express.Request, res: express.Response) => {
151152
const config = getConfig();
152153
let oauthProvider: OAuthProvider | null = null;
153154

155+
// Rate limiting for OAuth endpoints
156+
const oauthRateLimit = rateLimit({
157+
windowMs: 15 * 60 * 1000, // 15 minutes
158+
max: 100, // Limit each IP to 100 requests per windowMs
159+
message: {
160+
jsonrpc: "2.0",
161+
id: null,
162+
error: {
163+
code: -32000,
164+
message: "Too many requests, please try again later"
165+
}
166+
},
167+
standardHeaders: true,
168+
legacyHeaders: false,
169+
handler: (req, res) => {
170+
logger.warn("Rate limit exceeded for OAuth endpoint", {
171+
ip: req.ip,
172+
path: req.path,
173+
userAgent: req.get('User-Agent')
174+
});
175+
res.status(429).json({
176+
jsonrpc: "2.0",
177+
id: null,
178+
error: {
179+
code: -32000,
180+
message: "Too many requests, please try again later"
181+
}
182+
});
183+
}
184+
});
185+
186+
// Stricter rate limiting for token endpoint
187+
const tokenRateLimit = rateLimit({
188+
windowMs: 15 * 60 * 1000, // 15 minutes
189+
max: 10, // Limit each IP to 10 token requests per windowMs
190+
message: {
191+
jsonrpc: "2.0",
192+
id: null,
193+
error: {
194+
code: -32000,
195+
message: "Too many token requests, please try again later"
196+
}
197+
},
198+
standardHeaders: true,
199+
legacyHeaders: false,
200+
handler: (req, res) => {
201+
logger.warn("Rate limit exceeded for token endpoint", {
202+
ip: req.ip,
203+
path: req.path,
204+
userAgent: req.get('User-Agent')
205+
});
206+
res.status(429).json({
207+
jsonrpc: "2.0",
208+
id: null,
209+
error: {
210+
code: -32000,
211+
message: "Too many token requests, please try again later"
212+
}
213+
});
214+
}
215+
});
216+
154217
// Setup OAuth endpoints and provider when authentication is enabled
155218
if (config.ENABLE_AUTH) {
156219
const baseUrl = config.BASE_URL;
@@ -170,9 +233,9 @@ if (config.ENABLE_AUTH) {
170233
// Extract path from redirect URI for route registration
171234
const redirectPath = new URL(config.OAUTH_REDIRECT_URI!).pathname;
172235

173-
app.get("/oauth/authorize", createAuthorizeHandler());
174-
app.get(redirectPath, createCallbackHandler(oauthProvider));
175-
app.post("/oauth/token", express.urlencoded({ extended: true }), createTokenHandler(oauthProvider));
236+
app.get("/oauth/authorize", oauthRateLimit, createAuthorizeHandler());
237+
app.get(redirectPath, oauthRateLimit, createCallbackHandler(oauthProvider));
238+
app.post("/oauth/token", tokenRateLimit, express.urlencoded({ extended: true }), createTokenHandler(oauthProvider));
176239

177240
logger.info("OAuth 2.1 endpoints registered", {
178241
discovery: [

0 commit comments

Comments
 (0)