Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
6ce1157
Add AWS Cognito OAuth provider for FastMCP authentication
stephaneberle9 Sep 19, 2025
a218059
Add a comprehensive test suite for AWS Cognito OAuth provider
stephaneberle9 Sep 19, 2025
9afb431
Add complete documentation for AWS Cognito OAuth authentication in Fa…
stephaneberle9 Sep 19, 2025
d407330
Merge branch 'main' into feat/aws-cognito-auth-provider
stephaneberle9 Sep 19, 2025
c764664
Enhance AWS Cognito OAuth examples with enhanced user profile handling:
stephaneberle9 Sep 19, 2025
a523a96
Adjust versions in docs/integrations/aws-cognito.mdx
stephaneberle9 Sep 22, 2025
71a737c
Fix inconsistent tool name in example code snippet in docs/integratio…
stephaneberle9 Sep 22, 2025
0d5a819
Remove unnecessary hasattr check from client example and docs
stephaneberle9 Sep 22, 2025
dc4ed4a
Have all imports at the top of the file
stephaneberle9 Sep 22, 2025
6a4f2ca
Move more inlined imports to the top of the file
stephaneberle9 Sep 22, 2025
f4310a5
Align AWS Cognito provider with access token standards
stephaneberle9 Sep 22, 2025
c5d9b05
Refactor AWSCognitoTokenVerifier to extend JWTVerifier
stephaneberle9 Sep 22, 2025
593db58
Remove unused timeout_seconds parameter from AWS Cognito provider
stephaneberle9 Sep 22, 2025
f54ac37
Remove documentation of unused timeout_seconds parameter removed from…
stephaneberle9 Sep 22, 2025
385d013
Refactor AWS Cognito provider to use OIDC Discovery and eliminate dom…
stephaneberle9 Sep 22, 2025
121bee5
Update documentation to include AWS in OAuth provider mentions
stephaneberle9 Sep 23, 2025
f7f8faf
Add Keycloak OAuth authentication provider with complete example setup
stephaneberle9 Sep 23, 2025
19c56a8
Implement server-side scope injection and FastMCP compatibility modif…
stephaneberle9 Sep 25, 2025
fb8410a
Configure Keycloak realm for Dynamic Client Registration
stephaneberle9 Sep 25, 2025
4170258
Add comprehensive test suite for Keycloak OAuth authentication provider
stephaneberle9 Sep 25, 2025
8353dbb
Update Keycloak example configuration and realm file for improved
stephaneberle9 Sep 27, 2025
9ac8006
Add fix/workaround for "Client not found" error after
stephaneberle9 Sep 27, 2025
4f5dff5
Add comprehensive Keycloak OAuth integration documentation
stephaneberle9 Sep 27, 2025
e4a0b24
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Sep 27, 2025
14174c1
Fix unit test failures
stephaneberle9 Sep 27, 2025
715aa92
Reorganize Keycloak auth example documentation and setup
stephaneberle9 Oct 6, 2025
1c0121d
Merge remote-tracking branch 'upstream/main' into feat/keycloak-auth-…
stephaneberle9 Oct 6, 2025
986b1f7
Fix KeycloakAuthProvider.get_routes() method signature
stephaneberle9 Oct 6, 2025
58decf3
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Oct 21, 2025
321497d
Fix Keycloak realm import volume path
stephaneberle9 Oct 10, 2025
69c699c
Use correct path-scoped OAuth protected resource endpoint in Keycloak…
stephaneberle9 Oct 21, 2025
8d86a1a
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Oct 21, 2025
edc7407
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Oct 30, 2025
dc4ecae
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Oct 31, 2025
3c8ed2b
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Oct 31, 2025
def60e3
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Nov 2, 2025
eb73267
Fix CodeRabbit review comments for Keycloak OAuth provider
stephaneberle9 Nov 2, 2025
6f5622c
Fix remaining CodeRabbit review issues for Keycloak provider
stephaneberle9 Nov 2, 2025
df735d2
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Nov 3, 2025
ccc8b71
Fix type errors in KeycloakAuthProvider URL handling
stephaneberle9 Nov 3, 2025
67af227
Improve robustness and error handling in KeycloakAuthProvider
stephaneberle9 Nov 3, 2025
57648de
Fix HTTP proxying issues in KeycloakAuthProvider
stephaneberle9 Nov 3, 2025
0c767c7
Enforce provider-level required scopes with custom verifiers
stephaneberle9 Nov 3, 2025
6c55017
Fix proxy robustness issues in KeycloakAuthProvider
stephaneberle9 Nov 3, 2025
e75a34a
Add configurable audience validation to KeycloakAuthProvider
stephaneberle9 Nov 3, 2025
f474f1d
Fix minor issues in Keycloak integration docs and examples
stephaneberle9 Nov 3, 2025
3bcbb36
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Nov 3, 2025
33ac4bd
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Nov 4, 2025
443a12d
feat: Add Docker Compose v1/v2 compatibility to Keycloak startup script
stephaneberle9 Nov 16, 2025
8506312
refactor: Remove manual cache clearing from Keycloak OAuth example
stephaneberle9 Nov 16, 2025
de97d8a
fix: Disable audience validation by default in Keycloak example
stephaneberle9 Nov 16, 2025
2e546e5
feat: Add MCP Inspector compatibility to Keycloak OAuth example
stephaneberle9 Nov 17, 2025
ceeca60
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Nov 17, 2025
3ff1a95
Merge branch 'main' into feat/keycloak-auth-provider
stephaneberle9 Nov 20, 2025
f2eda1a
Simplify Keycloak provider to minimal DCR proxy (option 1 from PR #1937)
stephaneberle9 Nov 21, 2025
f44cd00
Add basic client scope to Keycloak realm config
stephaneberle9 Nov 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
"integrations/github",
"integrations/scalekit",
"integrations/google",
"integrations/keycloak",
"integrations/workos"
]
},
Expand Down
297 changes: 297 additions & 0 deletions docs/integrations/keycloak.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
---
title: Keycloak OAuth 🤝 FastMCP
sidebarTitle: Keycloak
description: Secure your FastMCP server with Keycloak OAuth
icon: shield-check
tag: NEW
---

import { VersionBadge } from "/snippets/version-badge.mdx"

<VersionBadge version="2.12.4" />

This guide shows you how to secure your FastMCP server using **Keycloak OAuth**. This integration uses the [**Remote OAuth**](/servers/auth/remote-oauth) pattern with Dynamic Client Registration (DCR), where Keycloak handles user login and your FastMCP server validates the tokens.

<Note>
**MCP Compatibility Note**: While Keycloak has built-in support for Dynamic Client Registration (DCR), there is an important MCP compatibility limitation. Keycloak ignores the client's requested `token_endpoint_auth_method` and always returns `client_secret_basic`, but the MCP specification requires `client_secret_post`. The KeycloakAuthProvider works around this issue by acting as a minimal proxy that intercepts DCR responses from Keycloak and fixes this field automatically. All other OAuth functionality (authorization, token issuance, user authentication) is handled directly by Keycloak.
</Note>

## Configuration

### Prerequisites

Before you begin, you will need:
1. A **[Keycloak](https://keycloak.org/)** server instance running (can be localhost for development, e.g., `http://localhost:8080`)

<Tip>
To spin up Keycloak instantly on your local machine, use Docker:

```bash
docker run --rm \
--name keycloak-fastmcp \
-p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin123 \
quay.io/keycloak/keycloak:26.3 \
start-dev
```

Then access the admin console at `http://localhost:8080` with username `admin` and password `admin123`.
</Tip>

<Tip>
If you prefer using Docker Compose instead, you may want to have a look at the [`docker-compose.yaml`](https://github.com/jlowin/fastmcp/blob/main/examples/auth/keycloak_auth/keycloak/docker-compose.yml) file included in the Keycloak auth example.
</Tip>

2. Administrative access to create and configure a Keycloak realm
3. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`)

### Step 1: Configure Keycloak for Dynamic Client Registration (DCR)

<Steps>
<Step title="Prepare the Realm Configuration">
Before importing, you should review and customize the pre-configured realm file:

1. Download the FastMCP Keycloak realm configuration: [`realm-fastmcp.json`](https://github.com/jlowin/fastmcp/blob/main/examples/auth/keycloak_auth/keycloak/realm-fastmcp.json)
2. Open the file in a text editor and customize as needed:
- **Realm name and display name**: Change `"realm": "fastmcp"` and `"displayName": "FastMCP Realm"` to match your project
- **Trusted hosts configuration**: Look for `"trusted-hosts"` section and update IP addresses for secure client registration:
- `localhost`: For local development
- `172.17.0.1`: Docker network gateway IP address (required when Keycloak is run with Docker and MCP server directly on localhost)
- `172.18.0.1`: Docker Compose network gateway IP address (required when Keycloak is run with Docker Compose and MCP server directly on localhost)
- `github.com`: Required for MCP Inspector compatibility
- For production, replace these with your actual domain names
- **Default scopes**: The configuration sets `openid`, `profile`, and `email` as default scopes for all clients
3. **Review the test user**: The file includes a test user (`testuser` with password `password123`). You may want to:
- Change the credentials for security
- Replace with more meaningful user accounts
- Or remove and create users later through the admin interface

<Warning>
**Production Security**: Always review and customize the configuration before importing, especially realm names, trusted hosts, and user credentials.
</Warning>
</Step>

<Step title="Import the Realm Configuration">
<Note>
The following instructions are based on **Keycloak 26.3**. Menu items, tabs, and interface elements may be slightly different in other Keycloak versions, but the core configuration concepts remain the same.
</Note>

1. In the left-side navigation, click **Manage realms** (if not visible, click the hamburger menu (☰) in the top-left corner to expand the navigation)
2. Click **Create realm**
3. In the "Create realm" dialog:
- Drag your `realm-fastmcp.json` file into the **Resource file** box (or use the "Browse" button to find and select it)
- Keycloak will automatically read the realm name (`fastmcp`) from the file
- Click the **Create** button

That's it! This single action will create the `fastmcp` realm and instantly configure everything from the file:
- The realm settings with default scopes for all clients (`openid`, `profile`, `email`)
- The "Trusted Hosts" client registration policy for secure Dynamic Client Registration (DCR)
- The test user with their credentials
</Step>

<Step title="Verify the Configuration">
After import, verify your realm is properly configured:

1. **Check the realm URL**: `http://localhost:8080/realms/fastmcp`
2. **Verify DCR policies**: Navigate to **Clients** → **Client registration** to see the imported `"Trusted Hosts"` policy with the trusted hosts you have configured earlier
3. **Test user access**: The imported test user can be used for initial testing

<Note>
Your realm is now ready for FastMCP integration with Dynamic Client Registration fully configured!
</Note>
</Step>
</Steps>

### Step 2: FastMCP Configuration

<Warning>
**Security Best Practice**: For production environments, always configure the `audience` parameter. Without audience validation, your server will accept tokens issued for *any* audience, including tokens meant for completely different services.

**Important**: Keycloak doesn't include the `aud` claim in tokens by default. For the example below to work out-of-the-box, audience validation is disabled. For production, configure Keycloak audience mappers and set `audience` to your resource server identifier (typically your server's base URL) to ensure tokens are specifically intended for your server.
</Warning>

Create your FastMCP server file and use the KeycloakAuthProvider to handle all the OAuth integration automatically:

```python server.py
from fastmcp import FastMCP
from fastmcp.server.auth.providers.keycloak import KeycloakAuthProvider
from fastmcp.server.dependencies import get_access_token

# The KeycloakAuthProvider automatically discovers Keycloak endpoints
# and configures JWT token validation
auth_provider = KeycloakAuthProvider(
realm_url="http://localhost:8080/realms/fastmcp", # Your Keycloak realm URL
base_url="http://localhost:8000", # Your server's public URL
required_scopes=["openid", "profile"], # Required OAuth scopes
# audience="http://localhost:8000", # For production: configure Keycloak audience mappers first
)

# Create FastMCP server with auth
mcp = FastMCP(name="My Keycloak Protected Server", auth=auth_provider)

@mcp.tool
async def get_access_token_claims() -> dict:
"""Get the authenticated user's access token claims."""
token = get_access_token()
return {
"sub": token.claims.get("sub"),
"name": token.claims.get("name"),
"preferred_username": token.claims.get("preferred_username"),
"scope": token.claims.get("scope")
}
```

## Testing

To test your server, you can use the `fastmcp` CLI to run it locally. Assuming you've saved the above code to `server.py` (after replacing the realm URL and base URL with your actual values!), you can run the following command:

```bash
fastmcp run server.py --transport http --port 8000
```

Now, you can use a FastMCP client to test that you can reach your server after authenticating:

```python
import asyncio
from fastmcp import Client

async def main():
async with Client("http://localhost:8000/mcp/", auth="oauth") as client:
# First-time connection will open Keycloak login in your browser
print("✓ Authenticated with Keycloak!")

# Test the protected tool
result = await client.call_tool("get_access_token_claims")
print(f"User: {result['preferred_username']}")

if __name__ == "__main__":
asyncio.run(main())
```

When you run the client for the first time:
1. Your browser will open to Keycloak's authorization page
2. After you log in and authorize the app, you'll be redirected back
3. The client receives the token and can make authenticated requests

<Info>
The client caches tokens locally, so you won't need to re-authenticate for subsequent runs unless the token expires or you explicitly clear the cache.
</Info>

### Testing with MCP Inspector

The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) provides an interactive web UI to explore and test your MCP server.

**Prerequisites**: Node.js must be installed on your system.

1. Launch the Inspector:
```bash
npx -y @modelcontextprotocol/inspector
```

2. In the Inspector UI (opens in your browser), enter your server URL: `http://localhost:8000/mcp`
3. In the **Authentication** section's **OAuth 2.0 Flow** area, locate the **Scope** field
4. In the **Scope** field, enter: `openid profile` (these must exactly match the `required_scopes` configured in your KeycloakAuthProvider or `FASTMCP_SERVER_AUTH_KEYCLOAK_REQUIRED_SCOPES` environment variable)
5. Click **Connect** - your browser will open for Keycloak authentication
6. Log in with your test user credentials (e.g., `testuser` / `password123`)
7. After successful authentication, the Inspector will connect to your server

<Note>
The MCP Inspector requires explicit scope configuration because it doesn't automatically request the scopes defined in Keycloak's client policies. This is the correct OAuth behavior - clients should explicitly request the scopes they need.
</Note>

### Automatic Client Re-registration

<Info>
If you restart Keycloak or change the realm configuration, your FastMCP client will automatically detect if the cached OAuth client credentials are no longer valid and will re-register with Keycloak automatically. You don't need to manually clear any caches - just run your client again and it will handle the re-registration process seamlessly.

This automatic retry mechanism ensures a smooth developer experience when working with Dynamic Client Registration (DCR).
</Info>

## Environment Variables

For production deployments, use environment variables instead of hardcoding credentials.

### Provider Selection

Setting this environment variable allows the Keycloak provider to be used automatically without explicitly instantiating it in code.

<Card>
<ParamField path="FASTMCP_SERVER_AUTH" default="Not set">
Set to `fastmcp.server.auth.providers.keycloak.KeycloakAuthProvider` to use Keycloak authentication.
</ParamField>
</Card>

### Keycloak-Specific Configuration

These environment variables provide default values for the Keycloak provider, whether it's instantiated manually or configured via `FASTMCP_SERVER_AUTH`.

<Card>
<ParamField path="FASTMCP_SERVER_AUTH_KEYCLOAK_REALM_URL" required>
Your Keycloak realm URL (e.g., `http://localhost:8080/realms/fastmcp` or `https://keycloak.example.com/realms/myrealm`)
</ParamField>

<ParamField path="FASTMCP_SERVER_AUTH_KEYCLOAK_BASE_URL" required>
Public URL of your FastMCP server (e.g., `https://your-server.com` or `http://localhost:8000` for development)
</ParamField>

<ParamField path="FASTMCP_SERVER_AUTH_KEYCLOAK_REQUIRED_SCOPES" default="None">
Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid profile` or `["openid","profile","email"]`)
</ParamField>

<ParamField path="FASTMCP_SERVER_AUTH_KEYCLOAK_AUDIENCE" default="None">
Audience(s) for JWT token validation. For production deployments, set this to your resource server identifier (typically your server's base URL) to ensure tokens are intended for your server. Without this, tokens issued for any audience will be accepted, which is a security risk.
</ParamField>
</Card>

Example `.env` file:
```bash
# Use the Keycloak provider
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.keycloak.KeycloakAuthProvider

# Keycloak configuration
FASTMCP_SERVER_AUTH_KEYCLOAK_REALM_URL=http://localhost:8080/realms/fastmcp
FASTMCP_SERVER_AUTH_KEYCLOAK_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_KEYCLOAK_REQUIRED_SCOPES=openid,profile,email
FASTMCP_SERVER_AUTH_KEYCLOAK_AUDIENCE=https://your-server.com # Recommended for production
```

With environment variables set, your server code simplifies to:

```python server.py
from fastmcp import FastMCP

# Authentication is automatically configured from environment
mcp = FastMCP(name="My Keycloak Protected Server")

@mcp.tool
async def protected_operation() -> str:
"""Perform a protected operation."""
# Your tool implementation here
return "Operation completed successfully"
```

## Advanced Configuration

### Custom Token Verifier

For advanced use cases, you can provide a custom token verifier:

```python
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.server.auth.providers.keycloak import KeycloakAuthProvider

# Custom JWT verifier with specific audience
custom_verifier = JWTVerifier(
jwks_uri="http://localhost:8080/realms/fastmcp/protocol/openid-connect/certs",
issuer="http://localhost:8080/realms/fastmcp",
audience="my-specific-client",
required_scopes=["api:read", "api:write"]
)

auth_provider = KeycloakAuthProvider(
realm_url="http://localhost:8080/realms/fastmcp",
base_url="http://localhost:8000",
token_verifier=custom_verifier
)
```
10 changes: 10 additions & 0 deletions examples/auth/keycloak_auth/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Keycloak Configuration
FASTMCP_SERVER_AUTH_KEYCLOAK_BASE_URL=http://localhost:8000
FASTMCP_SERVER_AUTH_KEYCLOAK_REALM_URL=http://localhost:8080/realms/fastmcp

# Optional: Specific scopes
FASTMCP_SERVER_AUTH_KEYCLOAK_REQUIRED_SCOPES=openid,profile

# Optional: Audience validation (recommended for production)
# If not set, defaults to base_url
# FASTMCP_SERVER_AUTH_KEYCLOAK_AUDIENCE=http://localhost:8000
Loading
Loading