> ## Documentation Index
> Fetch the complete documentation index at: https://learn.orbexa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP security model and OAuth 2.1 integration

> MCP security model — OAuth 2.1 integration, PKCE, RFC specification references, permission management, transport security, and data privacy

# Security and Authentication

## 7.1 MCP Security Model

MCP's security responsibilities are distributed across different layers:

| Layer                       | Responsible Party  | Content                                            |
| --------------------------- | ------------------ | -------------------------------------------------- |
| **Transport Security**      | Transport layer    | HTTPS encryption, certificate verification         |
| **Identity Authentication** | OAuth 2.1 + Server | Verify caller identity                             |
| **Permission Control**      | Server             | Control tool access for different users            |
| **Data Privacy**            | Server + Host      | Prevent sensitive data leakage to AI models        |
| **Tool Safety**             | Host               | Require user confirmation for high-risk tool calls |

## 7.2 OAuth 2.1 Authentication

The MCP specification defines the authentication mechanism for remote Servers based on **OAuth 2.1** (draft-ietf-oauth-v2-1-13). This is not a new authentication scheme invented by MCP; it relies entirely on existing OAuth standards and related RFC specifications.

### Referenced RFC Standards

| RFC                      | Name                          | Role in MCP                                                                          |
| ------------------------ | ----------------------------- | ------------------------------------------------------------------------------------ |
| draft-ietf-oauth-v2-1-13 | OAuth 2.1                     | Core authorization framework                                                         |
| RFC 7636 (PKCE)          | Proof Key for Code Exchange   | Prevents authorization code interception attacks (MCP mandates the S256 method)      |
| RFC 8414                 | Authorization Server Metadata | Client auto-discovers authorization server endpoints and capabilities                |
| RFC 7591                 | Dynamic Client Registration   | Client auto-registers on first connection                                            |
| RFC 9728                 | Protected Resource Metadata   | Server declares its authentication requirements and associated authorization servers |
| RFC 8707                 | Resource Indicators           | Client specifies the target resource when requesting tokens                          |

### Complete Authentication Flow

```
Step 1: Client sends request to MCP Server endpoint
        -> Server returns 401 Unauthorized

Step 2: Client fetches Protected Resource Metadata (RFC 9728)
        GET https://mcp-server.example.com/.well-known/oauth-protected-resource
        -> Obtains authorization_servers list

Step 3: Client fetches Authorization Server Metadata (RFC 8414)
        GET https://auth.example.com/.well-known/oauth-authorization-server
        -> Obtains authorization_endpoint, token_endpoint, etc.

Step 4: Dynamic Client Registration (RFC 7591, if needed)
        POST https://auth.example.com/register
        -> Obtains client_id (no client_secret, as this is a public client)

Step 5: Authorization Code + PKCE (S256)
        a. Client generates code_verifier and code_challenge
        b. Guides user to the authorization_endpoint
        c. User authorizes, Client obtains authorization_code
        d. Client exchanges code + code_verifier for access_token

Step 6: Subsequent requests carry Bearer Token
        POST https://mcp-server.example.com/mcp
        Authorization: Bearer eyJhbG...
        Mcp-Session-Id: session-abc123
```

### PKCE (Proof Key for Code Exchange)

MCP **mandates** PKCE and requires the `S256` method (`plain` is not allowed). PKCE prevents authorization codes from being abused if intercepted by a man-in-the-middle:

```
// 1. Client generates a random code_verifier
code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

// 2. Compute code_challenge = BASE64URL(SHA256(code_verifier))
code_challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

// 3. Authorization request includes code_challenge
GET /authorize?
  response_type=code&
  client_id=my-client&
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256&
  redirect_uri=http://localhost:8080/callback

// 4. Token exchange includes code_verifier
POST /token
  grant_type=authorization_code&
  code=AUTH_CODE&
  code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
```

### Token Type

MCP uses **Bearer Tokens**. The access\_token is passed in the HTTP request header:

```
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
```

## 7.3 Server-Side Authentication Implementation

```typescript theme={null}
import express from "express";

const app = express();

// OAuth token verification middleware
async function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) {
    // Return 401, guiding the Client to perform OAuth authentication
    return res.status(401).json({
      error: "unauthorized"
    });
  }

  // Verify token (call authorization server introspection endpoint or local JWT verification)
  const user = await verifyToken(token);
  if (!user) return res.status(403).json({ error: "invalid_token" });

  req.user = user;
  next();
}

// Protected Resource Metadata endpoint (RFC 9728)
app.get("/.well-known/oauth-protected-resource", (req, res) => {
  res.json({
    resource: "https://mcp-server.example.com",
    authorization_servers: ["https://auth.example.com"],
    bearer_methods_supported: ["header"]
  });
});

// MCP endpoint (authentication required)
app.post("/mcp", authenticate, async (req, res) => {
  const result = await handleMcpRequest(req.body, req.user);
  res.json(result);
});
```

## 7.4 Permission Control

Different tools require different permission levels:

```typescript theme={null}
// Check permissions during tool execution
server.tool("create_return", "Initiate a return", schema, async (args, context) => {
  // Check if the user has permission to operate on this order
  const order = await db.orders.findById(args.orderId);
  if (order.userId !== context.user.id) {
    return {
      content: [{ type: "text", text: "No permission to operate on this order" }],
      isError: true
    };
  }
  // Execute return logic
  const returnRequest = await db.returns.create({
    orderId: args.orderId,
    reason: args.reason,
    userId: context.user.id
  });
  return {
    content: [{
      type: "text",
      text: `Return request created: ${returnRequest.id}`
    }]
  };
});
```

### Tool-Level Permission Matrix

It is recommended to define clear permission requirements for each tool:

| Permission Level | Description                    | Example Tools                           |
| ---------------- | ------------------------------ | --------------------------------------- |
| Public           | No authentication required     | `search_products`, `get_categories`     |
| Logged in        | Requires a valid access\_token | `get_order`, `list_orders`              |
| Owner            | Can only operate on own data   | `create_return`, `update_profile`       |
| Admin            | Requires admin privileges      | `update_inventory`, `manage_promotions` |

## 7.5 Data Privacy

**Principle**: Data returned by the Server will be processed by the AI model. Do not include sensitive information in tool responses that should not be "seen" by the AI.

| Safe to Return                   | Should Not Return                    |
| -------------------------------- | ------------------------------------ |
| Product names, prices, inventory | User passwords, payment credentials  |
| Order status, shipping info      | Full credit card numbers             |
| Public company information       | Internal cost prices, profit margins |
| Redacted user information        | Full government ID numbers           |

### Resource Annotations for Privacy Control

Use the `audience` field in Annotations to control the target audience of a resource:

```json theme={null}
{
  "uri": "commerce://analytics/daily",
  "annotations": {
    "audience": ["assistant"],
    "priority": 0.3
  }
}
```

`audience: ["assistant"]` indicates the resource is intended for AI model processing only and should not be directly displayed to users. The Host can use this to decide whether to hide the resource in the UI.

## 7.6 Transport Security

### Remote Servers (Streamable HTTP)

* **HTTPS required**: Production environments must use TLS encryption
* **Certificate verification**: Clients should verify the Server's TLS certificate
* **CORS**: If the Client is a browser application, the Server needs appropriate CORS policies

### Local Servers (stdio)

stdio local Servers are inherently secure (no network exposure), but still require attention to:

* Do not hardcode database passwords in code; use environment variables
* Limit filesystem access scope (in conjunction with the Roots mechanism)
* Do not log sensitive data
* Run the Server process with minimal privileges

## 7.7 Security Checklist

When developing an MCP Server, use the following checklist to ensure security:

* Does the remote Server use HTTPS
* Is OAuth 2.1 authentication implemented (including PKCE S256)
* Do sensitive tools check user permissions
* Is returned data properly redacted
* Are environment variables used for storing secrets
* Are logs free of sensitive information
* Are input parameters validated and sanitized
* Is request rate limiting implemented

***

**Next Chapter**: [Testing and Debugging](/en/book-5/ch8-testing) — MCP Inspector usage guide
