Tag: least-privilege

  • Securing an MCP Server with Auth0: OAuth 2.1 and Per-Tool Authorization

    Securing an MCP Server with Auth0: OAuth 2.1 and Per-Tool Authorization

    This lab demonstrates how to secure an MCP server with Auth0 using OAuth 2.1 bearer tokens and enforce least-privilege authorization at the individual tool level through OAuth scopes.

    Configure the API

    Create a Custom API in Auth0 to represent the protected MCP resource server.

    Applications > APIs > Identity Access MCP API > Settings

    API configuration

    • Name: Identity Access MCP API
    • Identifier: https://mcp.auth-lab.com/identity-access
    • JWT Profile: RFC 9068
    • Signing Algorithm: RS256
    • User-delegated access: Per-app authorization

    This API identifier is used as the OAuth audience for access tokens issued to the MCP client.

    Define API Permissions

    Define granular OAuth permissions for the operations exposed by the MCP server.

    Applications > APIs > Identity Access MCP API > Permissions

    read:access_requests
    create:access_requests

    Configure the Application

    Create a dedicated OAuth client for MCP Inspector. The application will authenticate users through Auth0 and request delegated access to the protected MCP API.

    Applications > Applications > Create Application

    Name: Identity Access MCP Inspector
    Application Type: Regular Web Application
    Ownership: Third-party (Strict security controls)

    Configure Callback URLs

    Add the MCP Inspector OAuth callback URLs so Auth0 can redirect the user back to the local client after authentication.

    Applications > Applications > “Identity Access MCP Inspector” > Settings > Application URIs

    Configure Application Access

    Grant the MCP Inspector delegated access to the API permissions required by the protected MCP tools.

    Applications > APIs > “Identity Access MCP API”> Application Access > “Identity Access MCP Inspector” > Edit

    Configure the MCP Server

    A local FastMCP server is used as the protected resource. It exposes two identity-related tools.

    Define the MCP Tools

    list_access_requests
    → returns mock identity access requests
    create_access_request
    → creates a new mock access request

    Configure OAuth Protection and Token Validation

    Configure the MCP server as an OAuth protected resource and validate Auth0-issued bearer access tokens.

    from mcp.server.auth.settings import AuthSettings
    ...
    mcp = FastMCP(
    "Identity Access MCP Server",
    token_verifier=Auth0TokenVerifier(),
    auth=AuthSettings(
    issuer_url=AnyHttpUrl(f"https://{AUTH0_DOMAIN}/"),
    resource_server_url=AnyHttpUrl(
    f"{MCP_SERVER_URL.rstrip('/')}/mcp"
    ),
    required_scopes=[],
    ),
    )

    Auth0 acts as the authorization server, while the MCP server validates the token before allowing access to protected MCP operations.

    Verify OAuth Protected Resource Metadata

    The MCP server exposes OAuth Protected Resource Metadata so clients can discover Auth0 as the authorization server.

    Enforce Per-Tool Authorization

    Each MCP tool enforces its own OAuth scope, allowing authorization to be applied at the individual tool level.

    @mcp.tool()
    def list_access_requests(...):
    ...
    require_scope("read:access_requests")
    ...
    @mcp.tool()
    def create_access_request(...):
    ...
    require_scope("create:access_requests")
    ...

    Test the Authorization Flow

    Configure MCP Inspector with the “Identity Access MCP Inspector” credentials, required scopes, and API audience.

    MCP Inspector > identity-access-mcp-server > Settings > OAuth Settings

    This configuration allows MCP Inspector to request an Auth0 access token for the protected MCP API.

    Authenticate with Auth0

    Connect the MCP client and authenticate through Auth0 Universal Login

    Auth0 presents the requested API permissions to the user before granting delegated access to the MCP client.

    MCP Server
    → Valid Auth0 access token required
    list_access_requests
    → read:access_requests
    create_access_request
    → create:access_requests

    When the client requests only read:access_requests, the read operation succeeds while create_access_request is denied because the token does not contain create:access_requests.

    This lab demonstrates how Auth0 can secure an MCP server with OAuth 2.1 and enforce granular authorization at the individual tool level. The result is a simple but practical model for protecting AI-accessible operations with scoped bearer tokens and explicit user consent.