使用说明
Auth0 Protected FastMCP Server
A production-ready MCP (Model Context Protocol) server with Auth0 authentication and authorization.
Features
✅ RS256 Algorithm Enforcement - Only accepts RS256 signed JWT tokens
✅ Comprehensive Token Validation - Validates audience, issuer, iat, and exp claims
✅ Client ID/App Claim Support - Requires at least one identifier claim
✅ Scope-Based Authorization - Fine-grained access control with OAuth scopes
✅ Standard HTTP Errors - Returns 401 Unauthorized and 403 Forbidden
✅ Complete Test Suite - Comprehensive testing utilities included
Quick Start
1. Install Dependencies
pip install -r requirements.txt
2. Configure Environment
Copy the .env template and update with your Auth0 details:
cp .env.example .env
Edit .env:
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://api.your-mcp-server.com
3. Run the Server
python auth_server.py
4. Run Tests
# Run full test suite
python test_agent.py
# Run example usage
python test_agent.py example
Project Structure
.
├── auth_server.py # Main MCP server with Auth0 middleware
├── test_agent.py # Test agent and utilities
├── requirements.txt # Python dependencies
├── .env # Configuration (create from template)
└── README.md # This file
Available Tools
1. whoami (Requires tool:whoami scope)
Returns authenticated user/service information.
await session.call_tool("whoami", {})
2. read_data (Requires tool:read scope)
Read data from a resource.
await session.call_tool("read_data", {"resource": "user-profile"})
3. write_data (Requires tool:write scope)
Write data to a resource.
await session.call_tool("write_data", {
"resource": "user-profile",
"data": "new data"
})
4. get_server_info (Any valid token)
Get server information.
await session.call_tool("get_server_info", {})
Auth0 Setup
1. Create an API
- Go to Auth0 Dashboard > Applications > APIs
- Click "Create API"
- Enter name and identifier (your audience URL)
- Select RS256 signing algorithm
2. Add Permissions (Scopes)
In your API settings, add these scopes:
tool:whoami- Access whoami tooltool:read- Read datatool:write- Write data
3. Create Application
- Go to Applications > Applications
- Click "Create Application"
- Select "Machine to Machine Applications"
- Authorize for your API
- Grant necessary permissions
Getting Tokens from Auth0
Using cURL
curl --request POST \
--url https://YOUR_DOMAIN/oauth/token \
--header 'content-type: application/json' \
--data '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "https://api.your-mcp-server.com",
"grant_type": "client_credentials"
}'
Using Python
import requests
def get_auth0_token(domain, client_id, client_secret, audience):
url = f"https://{domain}/oauth/token"
payload = {
"client_id": client_id,
"client_secret": client_secret,
"audience": audience,
"grant_type": "client_credentials"
}
response = requests.post(url, json=payload)
return response.json()["access_token"]
token = get_auth0_token(
"your-tenant.auth0.com",
"your_client_id",
"your_client_secret",
"https://api.your-mcp-server.com"
)
Testing
The test suite validates all security requirements:
- ✓ Valid tokens with required scopes
- ✗ Tokens without required scopes (403)
- ✗ Missing iat claim (401)
- ✗ Missing exp claim (401)
- ✗ Missing client_id/app claim (401)
- ✗ Expired tokens (401)
- ✗ Wrong audience (401)
- ✗ Wrong issuer (401)
- ✗ Wrong algorithm (401)
Security Features
Token Validation
- Algorithm Check: Enforces RS256
- Signature Verification: Validates against JWKS
- Audience Validation: Matches configured audience
- Issuer Validation: Verifies Auth0 issuer
- Timestamp Validation: Checks iat and exp
- Identity Validation: Requires client_id or app claim
- Scope Enforcement: Per-tool scope requirements
Error Responses
401 Unauthorized
{
"error": "unauthorized",
"error_description": "Token has expired",
"status_code": 401
}
403 Forbidden
{
"error": "insufficient_scope",
"error_description": "Missing required scopes: tool:whoami",
"required_scopes": ["tool:whoami"],
"status_code": 403
}
Adding Custom Tools
@mcp.tool()
@auth_middleware
@require_scopes(['tool:custom'])
async def my_custom_tool(
param: str,
token_payload: dict
) -> dict:
"""
Custom tool with scope requirement
"""
return {
"result": f"Processed: {param}",
"user": token_payload.get('sub')
}
Troubleshooting
"Unable to find a signing key"
- Check AUTH0_DOMAIN is correct
- Verify JWKS endpoint is accessible
"Invalid audience"
- Ensure AUTH0_AUDIENCE matches token's aud claim
- Check Auth0 API identifier
"Missing required scopes"
- Grant permissions in Auth0 application
- Include scopes in token request
License
MIT License - See LICENSE file for details
Support
- Auth0 Documentation: https://auth0.com/docs
- FastMCP: https://github.com/jlowin/fastmcp
- Issues: Create an issue in this repository