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 { Hono } from "hono";
|
||||||
import { db as prodDb } from "../../db/index.ts";
|
import { db as prodDb } from "../../db/index.ts";
|
||||||
import { getUserCount, verifyApiKey } from "../services/auth.service.ts";
|
import { getUserCount, verifyApiKey } from "../services/auth.service.ts";
|
||||||
|
import { verifyAccessToken } from "../services/oauth.service.ts";
|
||||||
import { getCollectionSummary } from "./resources/collection.ts";
|
import { getCollectionSummary } from "./resources/collection.ts";
|
||||||
import {
|
import {
|
||||||
categoryToolDefinitions,
|
categoryToolDefinitions,
|
||||||
@@ -88,20 +89,36 @@ export const mcpRoutes = new Hono();
|
|||||||
// Auth middleware for all MCP requests
|
// Auth middleware for all MCP requests
|
||||||
mcpRoutes.use("/*", async (c, next) => {
|
mcpRoutes.use("/*", async (c, next) => {
|
||||||
const db = c.get("db") ?? prodDb;
|
const db = c.get("db") ?? prodDb;
|
||||||
const apiKey = c.req.header("X-API-Key");
|
|
||||||
|
|
||||||
// Require API key when auth is configured (users exist)
|
// Skip auth if no users exist
|
||||||
if (getUserCount(db) > 0) {
|
if (getUserCount(db) <= 0) {
|
||||||
if (!apiKey) {
|
return next();
|
||||||
return c.json({ error: "API key required" }, 401);
|
|
||||||
}
|
|
||||||
const valid = await verifyApiKey(db, apiKey);
|
|
||||||
if (!valid) {
|
|
||||||
return c.json({ error: "Invalid API key" }, 401);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) => {
|
mcpRoutes.post("/", async (c) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user