feat: add Bearer token auth to MCP alongside API key auth
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/
|
||||
import { Hono } from "hono";
|
||||
import { db as prodDb } from "../../db/index.ts";
|
||||
import { getUserCount, verifyApiKey } from "../services/auth.service.ts";
|
||||
import { verifyAccessToken } from "../services/oauth.service.ts";
|
||||
import { getCollectionSummary } from "./resources/collection.ts";
|
||||
import {
|
||||
categoryToolDefinitions,
|
||||
@@ -88,20 +89,36 @@ export const mcpRoutes = new Hono();
|
||||
// Auth middleware for all MCP requests
|
||||
mcpRoutes.use("/*", async (c, next) => {
|
||||
const db = c.get("db") ?? prodDb;
|
||||
const apiKey = c.req.header("X-API-Key");
|
||||
|
||||
// Require API key when auth is configured (users exist)
|
||||
if (getUserCount(db) > 0) {
|
||||
if (!apiKey) {
|
||||
return c.json({ error: "API key required" }, 401);
|
||||
}
|
||||
const valid = await verifyApiKey(db, apiKey);
|
||||
if (!valid) {
|
||||
return c.json({ error: "Invalid API key" }, 401);
|
||||
}
|
||||
// Skip auth if no users exist
|
||||
if (getUserCount(db) <= 0) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return next();
|
||||
// Try Bearer token first (OAuth)
|
||||
const authHeader = c.req.header("Authorization");
|
||||
if (authHeader?.startsWith("Bearer ")) {
|
||||
const token = authHeader.slice(7);
|
||||
if (verifyAccessToken(db, token)) {
|
||||
return next();
|
||||
}
|
||||
return c.json({ error: "invalid_token" }, 401);
|
||||
}
|
||||
|
||||
// Try API key (existing flow)
|
||||
const apiKey = c.req.header("X-API-Key");
|
||||
if (apiKey) {
|
||||
const valid = await verifyApiKey(db, apiKey);
|
||||
if (valid) {
|
||||
return next();
|
||||
}
|
||||
return c.json({ error: "Invalid API key" }, 401);
|
||||
}
|
||||
|
||||
// No auth provided — return 401 with WWW-Authenticate to trigger OAuth flow
|
||||
return c.text("Unauthorized", 401, {
|
||||
"WWW-Authenticate": 'Bearer resource_metadata="/.well-known/oauth-authorization-server"',
|
||||
});
|
||||
});
|
||||
|
||||
mcpRoutes.post("/", async (c) => {
|
||||
|
||||
Reference in New Issue
Block a user