示例
身份验证
使用 Bearer 令牌身份验证保护您的 MCP 端点。
概述
可以使用 Bearer 令牌身份验证来保护 MCP 端点。本指南将展示如何:
- 为用户生成和管理 API 密钥
- 在 MCP 中间件中验证令牌
- 在您的工具中访问用户上下文
- 配置带有身份验证的 MCP 客户端
提示
为我的 Nuxt MCP 端点添加身份验证 (@nuxtjs/mcp-toolkit)。
1. 选择身份验证策略:Better Auth API 密钥或自定义令牌验证
2. 使用 defineMcpHandler 和中间件函数创建 server/mcp/index.ts
3. 在中间件中,从 getHeader(event, 'authorization') 或 getHeader(event, 'x-api-key') 读取令牌
4. 验证令牌并使用已验证的用户设置 event.context.user
5. 不要抛出 401 错误 — MCP 客户端将进入 OAuth 发现模式。请改用“温和”的方式
6. 通过 useEvent().context.user 在工具中访问用户上下文
7. 在工具上使用 enabled 守卫,向未验证用户隐藏它们
8. 配置 MCP 客户端以在请求头中发送令牌
文档:https://mcp-toolkit.nuxt.dev/examples/authentication
中间件:https://mcp-toolkit.nuxt.dev/advanced/middleware
重要提示: 对于缺失或无效的身份验证,MCP 中间件不应抛出错误。抛出 401 错误会导致 MCP 客户端进入 OAuth 发现模式,去寻找不存在的
.well-known/oauth-* 端点。相反,请使用一种“温和”的方法:在身份验证成功时设置上下文,否则允许请求继续。使用 Better Auth API 密钥
如果您正在使用 Better Auth,可以利用内置的 API Key 插件 获得完整的解决方案。
服务器配置
将 API Key 插件添加到您的 Better Auth 配置中:
server/utils/auth.ts
import { betterAuth } from 'better-auth'
import { apiKey } from '@better-auth/api-key'
export const auth = betterAuth({
// ... 您现有的配置
plugins: [
apiKey({
rateLimit: {
enabled: false, // 禁用速率限制(如果不需要)
},
}),
],
})
API Key 插件默认启用了速率限制。请在开发环境中禁用它,或为生产环境配置适当的限制。
客户端配置
添加客户端插件以使用 API 密钥方法:
composables/auth.ts
import { createAuthClient } from 'better-auth/client'
import { apiKeyClient } from '@better-auth/api-key/client'
const client = createAuthClient({
plugins: [
apiKeyClient(),
],
})
// 创建 API 密钥
const { data } = await client.apiKey.create({ name: 'My MCP Key' })
console.log(data.key) // 保存此密钥 - 仅显示一次!
// 列出 API 密钥
const { data: keys } = await client.apiKey.list()
// 删除 API 密钥
await client.apiKey.delete({ keyId: 'key-id' })
辅助函数
创建一个验证 API 密钥且不抛出错误的辅助函数:
server/utils/auth.ts
export async function getApiKeyUser(event: H3Event) {
const authHeader = getHeader(event, 'authorization')
if (!authHeader?.startsWith('Bearer ')) {
return null
}
const key = authHeader.slice(7)
const result = await auth.api.verifyApiKey({ body: { key } })
if (!result.valid || !result.key) {
return null
}
const user = await db.query.user.findFirst({
where: (users, { eq }) => eq(users.id, result.key!.referenceId),
})
if (!user) {
return null
}
return { user, apiKey: result.key }
}
带身份验证的 MCP 处理程序
创建一个处理程序,在提供有效 API 密钥时设置用户上下文:
server/mcp/index.ts
export default defineMcpHandler({
middleware: async (event) => {
const result = await getApiKeyUser(event)
if (result) {
event.context.user = result.user
event.context.userId = result.user.id
}
},
})
此方法:
- 在身份验证成功时设置
event.context.user和event.context.userId - 当未提供有效令牌时,将上下文保留为
undefined - 工具必须检查用户上下文,如果未通过身份验证则返回错误
在工具中使用上下文
您的工具可以从 event.context 访问已验证的用户。请始终检查用户是否存在,并在未通过身份验证时返回错误消息:
server/mcp/tools/create-todo.ts
import { z } from 'zod'
import { useEvent, createError } from 'h3'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpTool({
name: 'create_todo',
description: 'Create a new todo for the authenticated user',
inputSchema: {
title: z.string().describe('The title of the todo'),
content: z.string().optional().describe('Optional description or content'),
},
handler: async ({ title, content }) => {
const event = useEvent()
const userId = event.context.userId as string
if (!userId) {
throw createError({ statusCode: 401, message: 'Authentication required. Please provide a valid API key.' })
}
const [todo] = await db.insert(schema.todos).values({
title,
content: content || null,
userId,
createdAt: new Date(),
updatedAt: new Date(),
}).returning()
return `Todo created: ${todo.title}`
},
})
server/mcp/tools/list-todos.ts
import { useEvent, createError } from 'h3'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
export default defineMcpTool({
name: 'list_todos',
description: 'List all todos for the authenticated user',
inputSchema: {},
handler: async () => {
const event = useEvent()
const userId = event.context.userId as string
if (!userId) {
throw createError({ statusCode: 401, message: 'Authentication required. Please provide a valid API key.' })
}
const todos = await db.query.todos.findMany({
where: (todos, { eq }) => eq(todos.userId, userId),
})
return todos
},
})
请记住在 Nuxt 配置中启用
asyncContext 以使用 useEvent():nuxt.config.ts
export default defineNuxtConfig({
nitro: {
experimental: {
asyncContext: true,
},
},
})
自定义令牌验证
如果您没有使用 Better Auth,可以实现自己的令牌验证。请记住使用不抛出错误的“温和”方法:
server/utils/auth.ts
import { createHash } from 'node:crypto'
export async function getTokenUser(event: H3Event) {
const authHeader = getHeader(event, 'authorization')
if (!authHeader?.startsWith('Bearer ')) {
return null
}
const token = authHeader.slice(7)
const tokenHash = createHash('sha256').update(token).digest('hex')
// 在您的数据库中查找令牌
const apiToken = await db.query.apiTokens.findFirst({
where: (tokens, { eq }) => eq(tokens.hash, tokenHash),
})
if (!apiToken) {
return null
}
// 检查过期时间
if (apiToken.expiresAt && apiToken.expiresAt < new Date()) {
return null
}
return { userId: apiToken.userId }
}
server/mcp/index.ts
export default defineMcpHandler({
middleware: async (event) => {
const result = await getTokenUser(event)
if (result) {
event.context.userId = result.userId
}
},
})
配置 MCP 客户端
Cursor
将您的 MCP 服务器添加到 .cursor/mcp.json:
.cursor/mcp.json
{
"mcpServers": {
"my-app": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer your-api-key-here"
}
}
}
}
Claude Desktop
添加到您的 Claude Desktop 配置中:
claude_desktop_config.json
{
"mcpServers": {
"my-app": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer your-api-key-here"
}
}
}
}
其他客户端
大多数 MCP 客户端支持自定义请求头。请查阅您所用客户端的文档以获取确切的配置格式。
TypeScript
为了实现类型安全的上下文,请扩展 H3 事件上下文:
server/types.ts
declare module 'h3' {
interface H3EventContext {
user?: {
id: string
name: string
email: string
}
userId?: string
}
}
安全最佳实践
- 始终对令牌进行哈希处理 - 在数据库中存储哈希后的令牌,而非明文
- 设置过期日期 - API 密钥应设置过期时间以限制暴露风险
- 实施速率限制 - 通过每个密钥的请求限制来防止滥用
- 允许撤销密钥 - 用户应能够删除已泄露的密钥
- 记录密钥使用情况 - 跟踪密钥的使用时间以便进行安全审计
后续步骤
- 中间件 - 了解更多中间件选项
- 处理程序 - 创建自定义的身份验证处理程序
- TypeScript - 类型安全的上下文定义