Introduction
This technical guide provides a comprehensive walkthrough for implementing a Model Context Protocol (MCP) server with robust OAuth 2.0 authentication, leveraging Auth0 as the identity provider. The focus is on achieving full compatibility with Claude.ai’s requirements for Dynamic Client Registration (DCR) as specified in RFC 7591. Unlike traditional OAuth integrations that rely on static client credentials, Claude.ai mandates that MCP servers support dynamic, standards-compliant registration and discovery endpoints, enabling third-party clients to obtain credentials and initiate secure authorization flows programmatically.
We will learn how to configure Auth0 to enable OIDC dynamic application registration, promote connections to domain-level for third-party authentication, and expose the necessary OAuth endpoints for automated client onboarding. The guide details the limitations of libraries, such as fastapi_mcp in this context, and demonstrates how to use the fastmcp and mcpauth libraries to implement an MCP server that supports dynamic registration, PKCE, and secure token exchange.
Step-by-step instructions are provided for both the Auth0 dashboard and API configuration
fastapi_mcp library limitations
For OAuth-based MCP servers, Claude.ai requires Dynamic Client Registration (DCR) support as per RFC 7591 and does not yet support a way for users to specify a client ID or secret manually.
The fastapi_mcp library, while excellent for many MCP use cases, has several limitations when it comes to Claude.ai's OAuth requirements:
- No Dynamic Client Registration Support : fastapi_mcp Requires pre-configured client IDs and secrets
- Static Configuration : Cannot handle Claude.ai’s dynamic application creation process
- MCP Inspector Compatibility : OAuth testing via MCP inspector fails with static configurations
- RFC 7591 Compliance : Lacks support for the Dynamic Client Registration standard required by Claude.ai
# This approach with fastapi_mcp does NOT work with Claude.ai OAuth
from fastapi_mcp import FastApiMCP, AuthConfig
# Static configuration - incompatible with Claude.ai
auth_config = AuthConfig(
client_id="static-client-id", # Claude.ai doesn't support this
client_secret="static-secret", # Claude.ai creates these dynamically
setup_proxies=True,
)
Using fastmcp and mcpauth Libraries
To properly support Claude.ai’s OAuth requirements, we use the fastmcp library with mcpauth For OAuth management:
pip install fastmcp mcpauth
These libraries provide:
- Dynamic Client Registration (RFC 7591) support
- Claude.ai compatibility out of the box
- MCP Inspector OAuth testing capabilities
- Proper OAuth 2.0 flows with PKCE support
Auth0 Configuration for Dynamic Client Registration
Step 1: Enable OIDC Dynamic Application Registration
By default, Auth0 disables dynamic application registration for security reasons. To enable it:
- Navigate to Auth0 Dashboard → Tenant Settings
- Go to Advanced Settings tab
- Find “Enable OIDC Dynamic Application Registration”
- Enable this setting
Warning : This setting allows third-party applications to dynamically register applications for your API. Ensure you have proper security measures in place.
Step 2: Grant Auth0 Management API Scopes for Connection Management
After enabling dynamic registration, you must ensure that your Auth0 Management API client has the correct permissions to manage connections. Specifically, you need to grant the update:connections and read:connections scopes to your Management API client. These scopes allow your automation or scripts to promote connections to domain-level and read connection details.
Granting Management API Scopes to Your Client
- Log in to the Auth0 Dashboard at https://manage.auth0.com/.
- Create a Machine to Machine Application :
- Go to Applications → Applications in the sidebar.
- Click Create Application.
- Enter a name (e.g., “MCP Management Client”), select Machine to Machine Applications , and click Create.
- Machine to Machine Applications require at least one authorized API. Select the Auth0 Management API
- Select the permissions read:connections , update:connections , read:clients
- Click Authorize to save the changes.
- Note the Client ID and Client Secret from the settings page-> Basic Information Section
Your machine-to-machine application is now authorized to manage Auth0 connections using the Management API with the necessary scopes.
Your client now has the necessary permissions to manage Auth0 connections via the Management API.
Step 3: Obtain a Management API Token (with the required scopes):
Run the below command to obtain the management API Token
export AUTH0_DOMAIN=asdasda
export AUTH0_CLIENT_ID=adsdasd
export AUTH0_CLIENT_SECRET=asdasdasd
ACCESS_TOKEN=$(curl --silent --request POST \
--url "https://${AUTH0_DOMAIN}/oauth/token" \
--header 'content-type: application/x-www-form-urlencoded' \
--data "grant_type=client_credentials" \
--data "client_id=${AUTH0_CLIENT_ID}" \
--data "client_secret=${AUTH0_CLIENT_SECRET}" \
--data "audience=https://${AUTH0_DOMAIN}/api/v2/" \
--data "scope=update:connections read:connections read:clients" \
| grep -o '"access_token":"[^"]*"' | cut -d':' -f2 | tr -d '"')
Verify the Client’s Granted Scopes :
curl --request GET \
--url "https://${AUTH0_DOMAIN}/api/v2/clients/${AUTH0_CLIENT_ID}" \
--header "authorization: Bearer ${ACCESS_TOKEN}"
Check the grant_types and scopes fields in the response to ensure update:connections and read:connections are present.
Note: These scopes are only for managing Auth0 system resources (connections) and are not related to API resource access for end-user tokens.
Step 4: Create Domain-Level Connections
Claude.ai requires domain-level connections for third-party application authentication. By default, Auth0 connections are not domain-level.
Promote Connection to Domain-Level via Management API
Use the Auth0 Management API to promote your connection to domain-level:
# First, get your Management API token
ACCESS_TOKEN=$(curl --silent --request POST \
--url "https://${AUTH0_DOMAIN}/oauth/token" \
--header 'content-type: application/x-www-form-urlencoded' \
--data "grant_type=client_credentials" \
--data "client_id=${AUTH0_CLIENT_ID}" \
--data "client_secret=${AUTH0_CLIENT_SECRET}" \
--data "audience=https://${AUTH0_DOMAIN}/api/v2/" \
--data "scope=update:connections read:connections" \
| grep -o '"access_token":"[^"]*"' | cut -d':' -f2 | tr -d '"')
#To find your Google OAuth connection ID automatically, you can extract it from the API response using `jq` (a command-line JSON processor):
# List all connections and extract the ID for the google-oauth2 connection
GOOGLE_CONNECTION_ID=$(curl --silent --request GET \
--url "https://${AUTH0_DOMAIN}/api/v2/connections" \
--header "authorization: Bearer ${ACCESS_TOKEN}" \
| jq -r '.[] | select(.name=="google-oauth2") | .id')
echo "Google OAuth connection ID: $GOOGLE_CONNECTION_ID"
This command fetches all connections and filters for the one named "google-oauth2", outputting its "id" field. Use the value of $GOOGLE_CONNECTION_ID in the PATCH request to promote the connection to domain-level.
# promote the connection to domain-level
curl --request PATCH \
--url "https://${AUTH0_DOMAIN}/api/v2/connections/${GOOGLE_CONNECTION_ID}" \
--header "authorization: Bearer ${ACCESS_TOKEN}" \
--header 'content-type: application/json' \
--data '{ "is_domain_connection": true }'
MCP Server Implementation with fastmcp
Server Setup with OAuth Support
from fastmcp import FastMCP
from mcpauth import MCPAuth
import asyncio
import logging
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
......
other code
# Initialize MCP server with OAuth
mcp_auth = MCPAuth(
server=fetch_server_config(
settings["issuer"],
type=AuthServerType.OAUTH # or AuthServerType.OAUTH
)
)
app = FastAPI()
@app.get("/", operation_id="root")
async def root():
"""Root endpoint with API information"""
return {
"message": "Dev.to Markdown Publisher API",
"version": "1.0.0",
}
api_mcp = FastMCP.from_fastapi(app=app, name="API Server")
bearer_auth = mcp_auth.bearer_auth_middleware(
"jwt", required_scopes=["read", "write"]
)
mcp_app = api_mcp.http_app(path='/mcp')
mcp_app.dependency_overrides = getattr(mcp_app, "dependency_overrides", {})
mcp_app.dependency_overrides[None] = bearer_auth
app = FastAPI(lifespan=tools_app.lifespan)
# Mount the MCP SSE app at /mcp
app.mount("/mcp", mcp_app)
# Add the metadata route for OAuth server discovery at /.well-known/oauth-authorization-server
app.add_route("/.well-known/oauth-authorization-server", mcp_auth.metadata_route(), methods=["GET"])
if __name__ == "__main__":
# Run with OAuth support enabled
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
Claude.ai Dynamic Application Registration
When Claude.ai connects to your MCP server with OAuth enabled, it automatically:
- Discovers OAuth Endpoints : Calls /.well-known/oauth-authorization-server
- Registers Dynamic Client : POSTs to /oauth/register endpoint
- Creates Auth0 Application : You’ll see “Claude.ai” app automatically created in Auth0 dashboard
- Initiates OAuth Flow : Redirects user for authentication
- Exchanges Tokens : Completes PKCE flow with dynamic client credentials
Viewing Dynamically Registered Applications
After Claude.ai connects, check your Auth0 dashboard:
- Go to Applications section
- Look for automatically created applications with names like:
- “Claude.ai MCP Client”
These applications are created automatically by Claude.ai through the dynamic registration process.
Conclusion
Implementing OAuth authentication for MCP servers with Claude.ai requires careful attention to dynamic client registration requirements. The combination of fastmcp, mcpauth, and properly configured Auth0 settings enables seamless integration with Claude.ai's OAuth flows while maintaining security best practices.
Key takeaways:
- Use fastmcp and mcpauth instead of fastapi_mcp for Claude.ai compatibility
- Enable OIDC Dynamic Application Registration in Auth0
- Promote connections to domain-level for third-party app support
- Monitor Auth0 dashboard for automatically created Claude.ai applications
- Test thoroughly with MCP Inspector before deploying to production
This setup ensures that your MCP server can authenticate users securely while providing the dynamic client registration capabilities that Claude.ai requires for OAuth integration.
Top comments (0)