认证授权服务原型

一个基于JWT认证和范围授权机制的MCP协议服务原型,适用于企业级访问控制场景。

  • by achimstruve
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 achimstruve/MCPAuthPrototype

使用说明

MCP认证原型

安全 模型上下文协议 (MCP)服务器演示企业级访问控制模式。用Python和 FastMCP v2,该原型实现了基于JWT的身份验证和基于范围的工具授权,专为在Google Kubernetes Engine上部署而设计。

这表明了什么

  • 基于令牌的身份验证:每个MCP请求都需要一个有效的JWT承载令牌
  • 基于范围的授权:令牌范围决定客户端可以查看和调用哪些工具
  • 纵深防御:工具列表筛选和工具调用验证(两个独立的检查)
  • 结构化审计日志记录:每个身份验证决策都以JSON格式记录在云日志系统中
  • 12因素配置:通过环境变量进行所有设置
  • Kubernetes就绪:健康和准备状态探测端点

建筑

Client (Claude Code, MCP client)
  │
  │  Authorization: Bearer <jwt>
  ▼
┌─────────────────────────────────┐
│  FastMCP Server (port 8080)     │
│                                 │
│  ┌───────────────────────────┐  │
│  │  AuthMiddleware           │  │
│  │  1. Extract Bearer token  │  │
│  │  2. Validate JWT (sig+exp)│  │
│  │  3. Filter tools by scope │  │
│  │  4. Block unauthorized    │  │
│  └───────────────────────────┘  │
│                                 │
│  ┌───────────┐ ┌─────────────┐  │
│  │ get_public│ │get_confiden-│  │
│  │ _info     │ │tial_info    │  │
│  │           │ │             │  │
│  │ scope:    │ │ scope:      │  │
│  │ public:   │ │ confidenti- │  │
│  │ read      │ │ al:read     │  │
│  └───────────┘ └─────────────┘  │
│                                 │
│  /health  /ready  /mcp          │
└─────────────────────────────────┘

访问控制矩阵

令牌范围 可见工具 可以调用
["public:read"] get_public_info 只有 get_public_info 只有
["public:read", "confidential:read"] 两种工具 两种工具
[] 无 无
无令牌/过期/无效 拒绝(AuthError) 拒绝(AuthError)

快速开始

先决条件

安装并运行

# Install dependencies
uv sync

# Start the server
uv run python -m src.server

服务器启动于 http://localhost:8080 与:

  • MCP端点: POST /mcp (流式HTTP传输)
  • 健康检查: GET /health
  • 准备状态检查: GET /ready

生成令牌

# Public access only
uv run python -m scripts.generate_token --sub alice --scope public:read

# Full access
uv run python -m scripts.generate_token --sub bob --scope public:read confidential:read

# Expired token (for testing rejection)
uv run python -m scripts.generate_token --sub charlie --scope public:read --exp-hours -1

注: 令牌必须使用服务器使用的相同密钥进行签名。默认情况下,两者 使用 dev-secret-change-me如果您使用自定义密钥运行服务器(例如。, MCP_JWT_SECRET_KEY=my-secret),您必须生成具有匹配项的令牌 --secret 标志:

uv run python -m scripts.generate_token --sub alice --scope public:read --secret my-secret

与克劳德代码连接

# Generate a token
TOKEN=$(uv run python -m scripts.generate_token --sub myuser --scope public:read confidential:read 2>&1 | grep "^Token:" | cut -d' ' -f2)

# Add the MCP server to Claude Code
claude mcp add --transport http mcp-auth-prototype http://localhost:8080/mcp \
  --header "Authorization: Bearer $TOKEN"

卷曲测试

# Generate a token
TOKEN=$(uv run python -m scripts.generate_token --sub alice --scope public:read 2>&1 | grep "^Token:" | cut -d' ' -f2)

# Initialize MCP session
curl -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'

发展

# Run tests
uv run pytest -v

# Lint
uv run ruff check .

码头工人

在部署到Kubernetes之前,在本地构建和测试容器映像。

塑造形象

# Build the Docker image
docker build -t mcp-auth-prototype:local .

多阶段构建创建了一个仅包含运行时依赖项的最小~150MB映像。

运行容器

# Run with a custom JWT secret (required for production)
docker run -p 8080:8080 -e MCP_JWT_SECRET_KEY=my-secret mcp-auth-prototype:local

# Run with debug logging
docker run -p 8080:8080 \
  -e MCP_JWT_SECRET_KEY=my-secret \
  -e MCP_LOG_LEVEL=debug \
  mcp-auth-prototype:local

测试容器

# Verify health endpoint
curl http://localhost:8080/health

# Verify readiness endpoint
curl http://localhost:8080/ready

# Generate a token (must use --secret matching the container's MCP_JWT_SECRET_KEY)
TOKEN=$(uv run python -m scripts.generate_token --sub alice --scope public:read --secret my-secret 2>&1 | grep "^Token:" | cut -d' ' -f2)

# Test MCP initialization against the container
curl -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'

项目结构

mcp-auth-prototype/
├── src/
│   ├── server.py          # MCP server, auth middleware, health endpoints
│   ├── auth.py            # JWT validation and scope extraction
│   ├── tools.py           # Tool-to-scope mapping registry
│   └── config.py          # Environment-based configuration (pydantic-settings)
├── documents/
│   ├── public.md          # Sample public company document
│   └── confidential.md    # Sample confidential strategy document
├── scripts/
│   └── generate_token.py  # CLI utility to mint JWT tokens
├── tests/
│   ├── conftest.py        # Shared test fixtures (token factories)
│   ├── test_auth.py       # Unit tests for JWT validation (16 tests)
│   └── test_tools.py      # Integration tests for tool authorization (6 tests)
├── helm/
│   └── mcp-server/            # Helm chart for Kubernetes deployment
│       ├── Chart.yaml         # Chart metadata (name, version)
│       ├── values.yaml        # Default configuration values
│       ├── values-dev.yaml    # Dev environment overrides
│       └── templates/
│           ├── _helpers.tpl       # Reusable Go template helpers
│           ├── deployment.yaml    # Deployment (2 replicas, probes, env vars)
│           ├── service.yaml       # ClusterIP Service on port 8080
│           ├── configmap.yaml     # Document content (public.md, confidential.md)
│           ├── serviceaccount.yaml # K8s ServiceAccounts with Workload Identity
│           ├── secretstore.yaml   # ESO connection to GCP Secret Manager
│           └── externalsecret.yaml # Syncs JWT key from GCP to K8s Secret
├── terraform/             # Infrastructure as Code
│   ├── main.tf            # Provider and backend configuration
│   ├── variables.tf       # Input variables
│   ├── outputs.tf         # Output values
│   ├── gke.tf             # GKE cluster definition
│   ├── artifact-registry.tf  # Container registry
│   ├── secret-manager.tf  # Secret Manager resources
│   ├── iam.tf             # Service accounts and IAM bindings
│   └── github-wif.tf     # Workload Identity Federation for GitHub Actions
├── .github/
│   └── workflows/
│       └── ci.yaml        # CI pipeline (lint, test, build, push, update Helm)
├── argocd/
│   └── application.yaml   # ArgoCD Application (GitOps auto-sync)
├── pyproject.toml         # Dependencies and tool configuration
└── uv.lock                # Locked dependency versions

CI/CD管道

每一次推动 main 触发自动管道:

git push ──▶ GitHub Actions ──▶ ArgoCD ──▶ GKE Cluster
              │                   │
              ├─ Lint (ruff)      ├─ Detects values.yaml change
              ├─ Test (pytest)    ├─ Renders Helm chart
              ├─ Build image      └─ Rolling update (zero downtime)
              ├─ Push to Artifact Registry (git SHA tag)
              └─ Update helm/mcp-server/values.yaml
  • 没有存储凭据:GitHub Actions通过工作负载身份联合会(OIDC令牌交换)向GCP进行身份验证
  • 不可变图像标签Docker镜像被标记为git commit SHA(例如。, a1b2c3d),不 latest
  • GitOps:ArgoCD不断地将集群状态与Git中的状态进行协调,包括在有人手动修改集群时进行自我修复

设计决策

文档存储:ConfigMap(原型)与生产替代方案

在此原型中,文档内容(public.md, confidential.md)直接内联在Helm图表中的Kubernetes ConfigMap中。这在这里是合适的,因为:

  • 我们只有2个小型静态文档(总共约1KB)
  • 它使Helm chart保持独立且易于理解
  • 赫尔姆 .Files.Get 函数无法读取图表目录外的文件

这种方法无法扩展。 ConfigMgr限制为1MB,文档更改需要完整的Helm升级(这会触发pod滚动更新),并且没有版本控制或独立的生命周期管理。

文件密集型系统的生产替代方案:

模式 何时使用 如何工作
对象存储(GCS/S3) 最常见。独立的文档生命周期,许多文档 应用程序在运行时通过Workload Identity从云存储桶中获取。支持版本控制、CDN、细粒度IAM。
数据库(PostgreSQL/Firestore) 文档需要元数据、搜索、关系 应用程序根据请求查询数据库。完整的CRUD、索引、事务处理。
Git仓库+sidecar GitOps繁重的组织,文档作为代码 sidecar/init容器克隆了一个单独的文档仓库。Git的版本历史记录,供审查的PR。
内容API微服务 大规模、多消费者 专用服务管理文档。MCP服务器变成了一个精简的编排层。

关键原则: 将文档生命周期与应用程序生命周期解耦。 MCP服务器应可独立于内容更新进行部署。

配置

所有设置都是从环境变量中读取的 MCP_ 前缀:

变量 默认值 描述
MCP_HOST 0.0.0.0 要绑定的网络接口
MCP_PORT 8080 服务器端口
MCP_LOG_LEVEL info 记录冗长(debug, info, warning, error)
MCP_JWT_SECRET_KEY dev-secret-change-me JWT签名密钥(在生产中覆盖)
MCP_JWT_ALGORITHM HS256 JWT签名算法
MCP_DOCUMENTS_DIR documents 文档文件的路径

您还可以在 .env 文件(gitignored)。

技术栈

组件 技术 目的
MCP服务器 FastMCP v2 带中间件挂钩的MCP协议
身份验证 PyJWT JWT令牌验证
配置 媒染剂设置 键入环境变量配置
HTTP服务器 Uvicorn ASGI服务器
测试 pytest+httpx 单元和集成测试
Linting 拉夫 快速Python linter
包管理器 紫外线 快速Python包管理器
基础设施 地形 基础设施作为GCP资源的代码
容器注册表 GCP工件注册表 Docker镜像存储
编排 谷歌Kubernetes引擎 容器编排
秘密 GCP秘密管理器+ESO 安全秘密管理
CI GitHub操作 自动剥皮、测试、构建、推送
CD ArgoCD GitOps持续部署
CI→GCP身份验证 工作负载身份联合 基于OIDC的身份验证,无存储密钥

路线图

看 实施_加载图.md 完整的建造计划。当前状态:

  • [x] 第0阶段:项目脚手架
  • [x] 第一阶段:配备工具的MCP服务器
  • [x] 第2阶段:身份验证和授权
  • [x] 第3阶段:测试
  • [x] 第四阶段:Docker化
  • [x] 第五阶段:GCP基础设施+地形+GKE
  • [x] 第6阶段:Helm图表
  • [x] 第7阶段:GitHub操作CI管道
  • [x] 第8阶段:ArgoCD
  • [x] 第9阶段:端到端验证
  • []第10阶段:TLS入口(HTTPS)——入口控制器、证书管理器、Let’s Encrypt、加密外部访问
  • []第11阶段:OAuth2令牌服务——通过Google OAuth2、开发人员CLI、Claude Code集成发行生产令牌
  • []第12阶段:自动缩放和弹性——HPA、集群自动缩放、PDB、负载平衡、使用Locust进行负载测试