> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/punkpeye/fastmcp/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth Proxy

> Built-in OAuth 2.1 proxy with Dynamic Client Registration, PKCE, and token swap

The `auth` option uses FastMCP's built-in **OAuth Proxy** that acts as a secure intermediary between MCP clients and upstream OAuth providers. The proxy handles the complete OAuth 2.1 authorization flow, including Dynamic Client Registration (DCR), PKCE, consent management, and token management with encryption and token swap patterns enabled by default.

## Key Features

* **Secure by Default**: Automatic encryption (AES-256-GCM) and token swap pattern
* **Zero Configuration**: Auto-generates keys and handles OAuth flows automatically
* **Pre-configured Providers**: Built-in support for Google, GitHub, and Azure
* **RFC Compliant**: Implements DCR (RFC 7591), PKCE, and OAuth 2.1
* **Optional JWKS**: Support for RS256/ES256 token verification (via optional `jose` dependency)

## Quick Start

The simplest way to use the OAuth Proxy is through the `auth` option with a pre-configured provider:

```typescript theme={null}
import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";

const server = new FastMCP({
  auth: new GoogleProvider({
    baseUrl: "https://your-server.com",
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  }),
  name: "My Server",
  version: "1.0.0",
});

server.addTool({
  canAccess: requireAuth,
  name: "protected-tool",
  execute: async (_args, { session }) => {
    const { accessToken } = getAuthSession(session);
    // Use accessToken to call upstream APIs
    return "Authenticated!";
  },
});
```

**That's it!** All OAuth endpoints are automatically available:

* `/oauth/register` - Dynamic Client Registration
* `/oauth/authorize` - Authorization endpoint
* `/oauth/callback` - OAuth callback handler
* `/oauth/consent` - User consent screen
* `/oauth/token` - Token exchange endpoint

## Pre-configured Providers

<Tabs>
  <Tab title="Google">
    ```typescript theme={null}
    import { GoogleProvider } from "fastmcp";

    const server = new FastMCP({
      auth: new GoogleProvider({
        baseUrl: "https://your-server.com",
        clientId: process.env.GOOGLE_CLIENT_ID!,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        scopes: ["openid", "profile", "email"],
      }),
      name: "My Server",
      version: "1.0.0",
    });
    ```

    **Setup:**

    1. Go to [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
    2. Create OAuth 2.0 Client ID
    3. Add redirect URI: `https://your-server.com/oauth/callback`
  </Tab>

  <Tab title="GitHub">
    ```typescript theme={null}
    import { GitHubProvider } from "fastmcp";

    const server = new FastMCP({
      auth: new GitHubProvider({
        baseUrl: "https://your-server.com",
        clientId: process.env.GITHUB_CLIENT_ID!,
        clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        scopes: ["read:user", "user:email"],
      }),
      name: "My Server",
      version: "1.0.0",
    });
    ```

    **Setup:**

    1. Go to [GitHub Developer Settings](https://github.com/settings/developers)
    2. Click "New OAuth App"
    3. Set callback URL: `https://your-server.com/oauth/callback`
  </Tab>

  <Tab title="Azure">
    ```typescript theme={null}
    import { AzureProvider } from "fastmcp";

    const server = new FastMCP({
      auth: new AzureProvider({
        baseUrl: "https://your-server.com",
        clientId: process.env.AZURE_CLIENT_ID!,
        clientSecret: process.env.AZURE_CLIENT_SECRET!,
        tenantId: "common", // or specific tenant ID
      }),
      name: "My Server",
      version: "1.0.0",
    });
    ```

    **Setup:**

    1. Go to [Azure Portal](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)
    2. Click "New registration"
    3. Add redirect URI: `https://your-server.com/oauth/callback`
  </Tab>

  <Tab title="Custom">
    ```typescript theme={null}
    import { OAuthProvider } from "fastmcp";

    const server = new FastMCP({
      auth: new OAuthProvider({
        authorizationEndpoint: "https://provider.com/oauth/authorize",
        baseUrl: "https://your-server.com",
        clientId: process.env.OAUTH_CLIENT_ID!,
        clientSecret: process.env.OAUTH_CLIENT_SECRET!,
        scopes: ["openid", "profile"],
        tokenEndpoint: "https://provider.com/oauth/token",
      }),
      name: "My Server",
      version: "1.0.0",
    });
    ```

    Use this for SAP, Auth0, Okta, or any OAuth 2.0 provider.
  </Tab>
</Tabs>

## Advanced Configuration

For more control over OAuth behavior, use the `oauth` option directly with an `OAuthProxy`:

```typescript theme={null}
import { FastMCP } from "fastmcp";
import { OAuthProxy } from "fastmcp/auth";

const authProxy = new OAuthProxy({
  upstreamAuthorizationEndpoint: "https://provider.com/oauth/authorize",
  upstreamTokenEndpoint: "https://provider.com/oauth/token",
  upstreamClientId: process.env.OAUTH_CLIENT_ID!,
  upstreamClientSecret: process.env.OAUTH_CLIENT_SECRET!,
  baseUrl: "https://your-server.com",
  scopes: ["openid", "profile"],
});

const server = new FastMCP({
  name: "My Server",
  oauth: {
    enabled: true,
    authorizationServer: authProxy.getAuthorizationServerMetadata(),
    proxy: authProxy,
  },
});
```

## Token Swap Pattern

Token swap prevents upstream tokens from reaching the client. This is **enabled by default** for enhanced security.

```typescript theme={null}
import { OAuthProxy, DiskStore, JWTIssuer } from "fastmcp/auth";

const authProxy = new OAuthProxy({
  baseUrl: "https://your-server.com",
  upstreamAuthorizationEndpoint: "https://provider.com/oauth/authorize",
  upstreamTokenEndpoint: "https://provider.com/oauth/token",
  upstreamClientId: process.env.OAUTH_CLIENT_ID,
  upstreamClientSecret: process.env.OAUTH_CLIENT_SECRET,

  // Token swap is enabled by default
  // Optionally provide your own signing key (recommended for production)
  jwtSigningKey: await JWTIssuer.deriveKey(process.env.JWT_SECRET, 100000),

  // Use persistent storage
  tokenStorage: new DiskStore({
    directory: "/var/lib/fastmcp/oauth",
  }),
});
```

<Note>
  If you don't provide `jwtSigningKey`, one will be auto-generated. For production, it's recommended to provide your own derived key for consistency across server restarts.
</Note>

### Loading Upstream Tokens

When using token swap, load the upstream tokens in your tools:

```typescript theme={null}
server.addTool({
  name: "call-api",
  description: "Call upstream API with user's token",
  execute: async (args, { session }) => {
    const clientToken = session?.headers?.["authorization"]?.replace(
      "Bearer ",
      "",
    );

    // Load the upstream tokens
    const upstreamTokens = await authProxy.loadUpstreamTokens(clientToken);

    if (upstreamTokens) {
      const response = await fetch("https://api.provider.com/user", {
        headers: {
          Authorization: `Bearer ${upstreamTokens.accessToken}`,
        },
      });

      const data = await response.json();
      return {
        content: [{ type: "text", text: JSON.stringify(data) }],
      };
    }

    throw new Error("No valid token");
  },
});
```

## Persistent Token Storage

Use `DiskStore` for production deployments:

```typescript theme={null}
import { DiskStore } from "fastmcp/auth";

const storage = new DiskStore({
  directory: "/var/lib/fastmcp/oauth",
  cleanupIntervalMs: 60000, // Cleanup every minute
  fileExtension: ".json",
});

const authProxy = new OAuthProxy({
  // ... other config
  tokenStorage: storage,
});
```

**Benefits:**

* Tokens persist across server restarts
* Automatic cleanup of expired entries
* Thread-safe concurrent operations

## Encrypted Token Storage

**Storage is automatically encrypted** with AES-256-GCM:

```typescript theme={null}
import { DiskStore, JWTIssuer } from "fastmcp/auth";

const authProxy = new OAuthProxy({
  // ... other config
  tokenStorage: new DiskStore({ directory: "/var/lib/fastmcp/oauth" }),
  // ← Automatically encrypted!

  // Optional: Provide custom encryption key (recommended for production)
  encryptionKey: await JWTIssuer.deriveKey(
    process.env.ENCRYPTION_SECRET + ":storage",
    100000,
  ),
});
```

<Warning>
  To disable encryption (only for development/testing):

  ```typescript theme={null}
  const authProxy = new OAuthProxy({
    tokenStorage: new MemoryTokenStorage(),
    encryptionKey: false, // Explicitly disable encryption
  });
  ```
</Warning>

## Custom Claims Passthrough

Pass custom claims from upstream tokens (roles, permissions, etc.) to your proxy-issued JWTs. **Enabled by default**:

```typescript theme={null}
import { OAuthProxy } from "fastmcp/auth";

const authProxy = new OAuthProxy({
  // ... other config
  customClaimsPassthrough: {
    // Extract from access token (default: true)
    fromAccessToken: true,

    // Extract from ID token (default: true)
    fromIdToken: true,

    // No prefix by default for RBAC compatibility
    claimPrefix: false,

    // Optional: Only allow specific claims
    allowedClaims: ["role", "roles", "permissions", "email", "groups"],

    // Optional: Block specific claims
    blockedClaims: ["internal_id", "debug_info"],

    // Maximum claim value size (default: 2000 chars)
    maxClaimValueSize: 2000,

    // Allow complex objects/arrays (default: false)
    allowComplexClaims: false,
  },
});
```

### Using Claims for Authorization

```typescript theme={null}
server.addTool({
  name: "admin-dashboard",
  description: "Access admin dashboard",
  canAccess: async ({ session }) => {
    const token = session?.headers?.["authorization"]?.replace("Bearer ", "");
    if (!token) return false;

    // Decode the proxy JWT
    const payload = JSON.parse(
      Buffer.from(token.split(".")[1], "base64url").toString(),
    );

    // Check role claim from upstream IDP
    return payload.role === "admin" || payload.roles?.includes("admin");
  },
  execute: async () => {
    return {
      content: [{ type: "text", text: "Admin dashboard data..." }],
    };
  },
});
```

## Configuration Options

| Option                          | Type              | Default              | Description                                            |
| ------------------------------- | ----------------- | -------------------- | ------------------------------------------------------ |
| `upstreamAuthorizationEndpoint` | `string`          | -                    | **Required** - OAuth provider's authorization endpoint |
| `upstreamTokenEndpoint`         | `string`          | -                    | **Required** - OAuth provider's token endpoint         |
| `upstreamClientId`              | `string`          | -                    | **Required** - Your OAuth client ID                    |
| `upstreamClientSecret`          | `string`          | -                    | **Required** - Your OAuth client secret                |
| `baseUrl`                       | `string`          | -                    | **Required** - Your server's base URL                  |
| `redirectPath`                  | `string`          | `/oauth/callback`    | OAuth callback path                                    |
| `scopes`                        | `string[]`        | Provider defaults    | OAuth scopes to request                                |
| `forwardPkce`                   | `boolean`         | `false`              | Forward PKCE to upstream provider                      |
| `consentRequired`               | `boolean`         | `true`               | Show consent screen                                    |
| `enableTokenSwap`               | `boolean`         | `true`               | Enable token swap pattern                              |
| `jwtSigningKey`                 | `string`          | Auto-generated       | JWT signing key                                        |
| `encryptionKey`                 | `string \| false` | Auto-generated       | Storage encryption key                                 |
| `tokenStorage`                  | `TokenStorage`    | `MemoryTokenStorage` | Token storage backend                                  |
| `transactionTtl`                | `number`          | `600`                | Transaction TTL (seconds)                              |
| `authorizationCodeTtl`          | `number`          | `300`                | Auth code TTL (seconds)                                |
| `accessTokenTtl`                | `number`          | `3600`               | Access token TTL (seconds)                             |
| `refreshTokenTtl`               | `number`          | `2592000`            | Refresh token TTL (seconds)                            |

## Security Best Practices

<Steps>
  <Step title="Use HTTPS in Production">
    ```typescript theme={null}
    const authProxy = new OAuthProxy({
      baseUrl: "https://your-server.com", // Not http://
      // ...
    });
    ```
  </Step>

  <Step title="Derive Keys from Secrets">
    ```typescript theme={null}
    import { JWTIssuer } from "fastmcp/auth";

    const jwtSigningKey = await JWTIssuer.deriveKey(
      process.env.JWT_SECRET,
      100000, // PBKDF2 iterations
    );

    const encryptionKey = await JWTIssuer.deriveKey(
      process.env.ENCRYPTION_SECRET,
      100000,
    );
    ```
  </Step>

  <Step title="Use Different Keys for Different Purposes">
    ```typescript theme={null}
    const jwtKey = await JWTIssuer.deriveKey(process.env.SECRET + ":jwt", 100000);
    const storageKey = await JWTIssuer.deriveKey(process.env.SECRET + ":storage", 100000);
    const consentKey = await JWTIssuer.deriveKey(process.env.SECRET + ":consent", 100000);
    ```
  </Step>

  <Step title="Enable Consent Screen">
    ```typescript theme={null}
    const authProxy = new OAuthProxy({
      consentRequired: true, // Default, but be explicit
      // ...
    });
    ```
  </Step>

  <Step title="Use Persistent Encrypted Storage">
    ```typescript theme={null}
    const storage = new DiskStore({
      directory: "/var/lib/fastmcp/oauth"
    });

    const authProxy = new OAuthProxy({
      tokenStorage: storage,
      encryptionKey: await JWTIssuer.deriveKey(process.env.ENCRYPTION_SECRET, 100000),
      // ...
    });
    ```
  </Step>

  <Step title="Validate Redirect URIs">
    ```typescript theme={null}
    const authProxy = new OAuthProxy({
      allowedRedirectUriPatterns: [
        "https://yourdomain.com/*",
        "http://localhost:*", // Only for development
      ],
      // ...
    });
    ```
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="shield-halved" href="/features/authentication">
    Learn about authentication options and tool authorization
  </Card>

  <Card title="Custom Routes" icon="route" href="/features/custom-routes">
    Add authenticated custom HTTP routes to your server
  </Card>
</CardGroup>
