返回顶部
m

mcp-oauthMCP OAuth认证

Add OAuth 2.0 PKCE authentication to a remote MCP server. Use this skill whenever the user wants to add authentication to an MCP server, protect MCP tools with OAuth, implement login flow for an MCP connector, add user auth to an MCP endpoint, or set up token-based access for MCP. Also triggers on: 'MCP OAuth', 'MCP authentication', 'withMcpAuth', 'MCP login flow', 'protect MCP endpoint', 'MCP token auth', 'dynamic client registration MCP', 'Claude connector OAuth'. Even if the user just says 'a

作者: admin | 来源: ClawHub
源自
ClawHub
版本
V 1.0.0
安全检测
已通过
148
下载量
免费
免费
0
收藏
概述
安装方式
版本历史

mcp-oauth

MCP服务器的OAuth 2.0 PKCE认证

为远程MCP服务器添加生产级OAuth认证。本方案实现了完整的MCP授权规范——包括发现、动态客户端注册、PKCE授权、令牌交换和刷新。

适用场景

当您的MCP服务器需要访问用户特定数据(如账户信息、文件、播放列表)时。如果没有认证机制,任何知晓您服务器URL的人都能访问任意用户的数据。OAuth让每个用户使用自己的凭证进行认证,并获取专属令牌。

架构概览

您的MCP服务器扮演两个角色:

  1. 1. OAuth服务器(面向MCP客户端如Claude、Smithery)——颁发您自己的令牌
  2. OAuth客户端(对接上游服务如Tidal、GitHub、Slack等)——交换获取它们的令牌

MCP客户端 (Claude) → 您的OAuth服务器 → 上游服务 (如Tidal)
│ │ │
│ 1. 发现OAuth │ │
│ 2. 注册客户端 │ │
│ 3. 授权 │──→ 4. 重定向至 │
│ │ 上游登录 ──→ │
│ │ ←── 5. 回调 ────────│
│ ←── 6. 授权码 │ │
│ 7. 交换令牌 │ │
│ 8. 调用工具 ──────→│──→ 9. API调用 ──────→│

必需端点

1. OAuth发现

app/.well-known/oauth-authorization-server/route.ts:

typescript
import { NextResponse } from next/server;

const SITEURL = process.env.NEXTPUBLICSITEURL || https://your-domain.com;

export async function GET() {
return NextResponse.json({
issuer: SITE_URL,
authorizationendpoint: ${SITEURL}/api/authorize,
tokenendpoint: ${SITEURL}/api/token,
registrationendpoint: ${SITEURL}/api/register,
responsetypessupported: [code],
granttypessupported: [authorizationcode, refreshtoken],
codechallengemethods_supported: [S256],
tokenendpointauthmethodssupported: [none],
}, {
headers: {
Access-Control-Allow-Origin: *,
Access-Control-Allow-Methods: GET, OPTIONS,
},
});
}

export async function OPTIONS() {
return new NextResponse(null, {
status: 204,
headers: {
Access-Control-Allow-Origin: *,
Access-Control-Allow-Methods: GET, OPTIONS,
Access-Control-Allow-Headers: Content-Type, Authorization,
},
});
}

2. 受保护资源元数据

app/.well-known/oauth-protected-resource/route.ts:

typescript
import { protectedResourceHandler, metadataCorsOptionsRequestHandler } from mcp-handler;

const SITEURL = process.env.NEXTPUBLICSITEURL || https://your-domain.com;

export const GET = protectedResourceHandler({
authServerUrls: [SITE_URL],
resourceUrl: SITE_URL,
});

export const OPTIONS = metadataCorsOptionsRequestHandler();

3. 动态客户端注册 (RFC 7591)

MCP客户端在启动授权流程前自行注册。

app/api/register/route.ts:

typescript
import { NextRequest, NextResponse } from next/server;
import crypto from crypto;

export async function POST(req: NextRequest) {
const body = await req.json().catch(() => ({}));
const clientId = crypto.randomBytes(16).toString(hex);

return NextResponse.json({
client_id: clientId,
clientname: body.clientname || MCP客户端,
redirecturis: body.redirecturis || [],
granttypes: [authorizationcode, refresh_token],
response_types: [code],
tokenendpointauth_method: none,
}, { status: 201 });
}

4. 授权端点

验证请求,在Redis中存储会话,重定向到上游OAuth。

app/api/authorize/route.ts:

typescript
import { NextRequest, NextResponse } from next/server;

export async function GET(req: NextRequest) {
const params = req.nextUrl.searchParams;
const redirectUri = params.get(redirect_uri);
const state = params.get(state);
const codeChallenge = params.get(code_challenge);

if (!redirectUri || !state || !codeChallenge) {
return NextResponse.json(
{ error: invalidrequest, errordescription: 缺少必需参数 },
{ status: 400 },
);
}

// 验证redirect_uri——仅允许已知的MCP客户端
const url = new URL(redirectUri);
const isAllowed =
url.hostname === claude.ai ||
url.hostname === claude.com ||
url.hostname === api.smithery.ai ||
url.hostname === localhost ||
url.hostname === 127.0.0.1;

if (!isAllowed) {
return NextResponse.json(
{ error: invalidrequest, errordescription: redirect_uri不被允许 },
{ status: 400 },
);
}

// 为上游OAuth生成PKCE
const upstreamVerifier = crypto.randomBytes(32).toString(base64url);
const upstreamChallenge = crypto
.createHash(sha256)
.update(upstreamVerifier)
.digest(base64url);
const sessionId = crypto.randomBytes(16).toString(hex);

// 存储在Redis中(10分钟TTL)
await redis.set(session:${sessionId}, JSON.stringify({
redirectUri, state, codeChallenge,
upstreamVerifier, upstreamState: sessionId,
}), { ex: 600 });

// 重定向到上游OAuth(替换为您的服务)
const upstreamUrl = new URL(https://upstream-service.com/authorize);
upstreamUrl.searchParams.set(clientid, YOURCLIENT_ID);
upstreamUrl.searchParams.set(response_type, code);
upstreamUrl.searchParams.set(redirecturi, ${SITEURL}/api/callback);
upstreamUrl.searchParams.set(code_challenge, upstreamChallenge);
upstreamUrl.searchParams.set(codechallengemethod, S256);
upstreamUrl.searchParams.set(state, sessionId);

return NextResponse.redirect(upstreamUrl.toString());
}

5. 回调(来自上游)

app/api/callback/route.ts:

typescript
export async function GET(req: NextRequest) {
const code = req.nextUrl.searchParams.get(code);
const state = req.nextUrl.searchParams.get(state);

// 从Redis查找会话
const session = JSON.parse(await redis.get(session:${state}));
if (!session) return NextResponse.json({ error: 会话已过期 }, { status: 400 });

// 用授权码交换上游令牌
const tokens = await exchangeUpstreamCode(code, session.upstreamVerifier);

// 在Redis中存储上游令牌(30天TTL)
const userId = crypto.randomBytes(16).toString(hex);
await redis.set(user:${userId}:tokens, JSON.stringify(tokens), { ex: 2592000 });

// 为MCP客户端生成我们的授权码
const mcpAuthCode = crypto.randomBytes(16).toString(hex);
await redis.set(auth_code:${mcpAuthCode}, userId, { ex: 300 });

// 清理并重定向回MCP客户端
await redis.del(session:${state});
const redirect = new URL(session.redirectUri);
redirect.searchParams.set(code, mcpAuthCode);
redirect.searchParams.set(state, session.state);
return NextResponse.redirect(redirect.toString());
}

6. 令牌交换

app/api/token/route.ts:

typescript
export async function POST(req: NextRequest) {
const body = Object.fromEntries(await req.formData());

if (body.granttype === authorizationcode) {
const userId = await redis.get(auth_code:${body.code});
if (!userId) return NextResponse.json({ error: invalid_grant }, { status: 400 });
await redis.del(auth_code:${body.code});

const accessToken = crypto.randomBytes(16).toString(hex);
const refreshToken = crypto.randomBytes(16).toString(hex);
await redis.set(mcp_token:${accessToken}, userId, { ex: 86400 });
await redis.set(refresh:${refreshToken}, userId, { ex: 2592000 });

return NextResponse.json({
access

标签

skill ai

通过对话安装

该技能支持在以下平台通过对话安装:

OpenClaw WorkBuddy QClaw Kimi Claude

方式一:安装 SkillHub 和技能

帮我安装 SkillHub 和 mcp-oauth-1776076981 技能

方式二:设置 SkillHub 为优先技能安装源

设置 SkillHub 为我的优先技能安装源,然后帮我安装 mcp-oauth-1776076981 技能

通过命令行安装

skillhub install mcp-oauth-1776076981

下载

⬇ 下载 mcp-oauth v1.0.0(免费)

文件大小: 4.86 KB | 发布时间: 2026-4-17 15:19

v1.0.0 最新 2026-4-17 15:19
- Initial release of mcp-oauth, providing OAuth 2.0 PKCE authentication for remote MCP servers.
- Implements OAuth discovery, dynamic client registration, authorization endpoint, token exchange, and refresh flows as per MCP authorization spec.
- Includes code samples for Next.js server routes to enable:
- Standards-based OAuth discovery and protected resource metadata endpoints.
- Dynamic client registration (RFC 7591).
- Secure authorization and callback logic for integrating with upstream OAuth services.
- Protects MCP endpoints and tools with user-level token-based access.
- Designed to trigger for any user request regarding adding authentication or OAuth to an MCP server.

Archiver·手机版·闲社网·闲社论坛·羊毛社区· 多链控股集团有限公司 · 苏ICP备2025199260号-1

Powered by Discuz! X5.0   © 2024-2025 闲社网·线报更新论坛·羊毛分享社区·http://xianshe.com

p2p_official_large
返回顶部