Add Keycloak OAuth Provider for Enterprise Authentication and local dev#1937
Add Keycloak OAuth Provider for Enterprise Authentication and local dev#1937stephaneberle9 wants to merge 64 commits intoPrefectHQ:mainfrom
Conversation
- Add AWSCognitoProvider class extending OAuthProxy for Cognito User Pools - Implement JWT token verification using authlib with JWKS validation - Support automatic domain construction from prefix and region - Add comprehensive error handling and debug logging - Include working example server and client with documentation - Follow FastMCP authentication provider patterns and standards - Use existing dependencies (authlib, httpx) without adding new ones
- Add test_aws.py with full test coverage for AWSCognitoProvider - Test provider initialization, configuration, error handling, and Cognito-specific features (domain construction, scopes, claims) - Cover error scenarios: invalid tokens, expired tokens, wrong issuer - Use AsyncMock/MagicMock for comprehensive AWS Cognito API simulation
…stMCP: - Step-by-step setup guide reflecting AWS Cognito's streamlined UI - Traditional web application configuration for server-side auth - JWT token validation and user claims handling - Environment variable configuration options - Code examples for server setup and client testing - Enterprise features including SSO, MFA, and role-based access
- Add get_user_profile tool to server for retrieving authenticated user data - Update client example to demonstrate profile retrieval functionality - Fix mistaken documentation examples and improve error handling and data display - Add commented redirect_path configuration option for better awareness
…ns/aws-cognito.mdx
Remove email, name, and other profile claims from AccessToken as these are not included in AWS Cognito access tokens per documentation. Keep only sub, username, and cognito:groups which are the standard claims available in access tokens for authorization purposes. Update examples and docs.
Replace duplicate JWT verification logic in AWSCognitoTokenVerifier by extending JWTVerifier instead of TokenVerifier. This eliminates ~150 lines of duplicated code including JWKS fetching, caching, token validation, and JWT decoding logic. Key changes: - AWSCognitoTokenVerifier now extends JWTVerifier for core JWT operations - Removed duplicate JWKS/JWT logic and dependencies (httpx, authlib.jose) - Simplified constructor to configure parent with Cognito URLs and RS256 - Override verify_token() to filter claims to Cognito-specific subset - Updated tests to work with new inheritance structure Benefits: - Eliminates code duplication between JWT providers - Leverages existing JWT infrastructure and improvements - Maintains backward compatibility while reducing complexity - Cleaner separation of JWT verification vs Cognito-specific logic
The timeout_seconds parameter is no longer needed after refactoring AWSCognitoTokenVerifier to extend JWTVerifier. HTTP timeouts for JWKS requests are now handled by the parent JWTVerifier class.
… AWS Cognito provider
…ain_prefix This change modernizes the AWS Cognito provider by: - Switching from OAuthProxy to OIDCProxy with automatic OIDC Discovery - Removing the domain_prefix parameter and related configuration - Updating get_token_verifier to instantiate AWSCognitoTokenVerifier directly - Simplifying provider initialization by using Cognito's well-known OIDC endpoints - Updating documentation and examples to reflect the streamlined configuration
Implement KeycloakAuthProvider that extends RemoteAuthProvider to support Keycloak integration using OAuth 2.1/OpenID Connect with Dynamic Client Registration (DCR). The provider automatically discovers OIDC endpoints and forwards authorization server metadata to enable seamless client authentication. Key features: - Automatic OIDC endpoint discovery from Keycloak realm - JWT token verification with JWKS support - Authorization server metadata forwarding for DCR - Configurable scope requirements and custom token verifiers - Environment variable configuration support Includes comprehensive example with: - Docker Compose setup with Keycloak 26.2 - Pre-configured test realm with client and user - Complete server and client demonstration - Automated setup script with health checks - Detailed documentation and troubleshooting guide
…ications in client registration responses for Keycloak OAuth provider Enhances the existing Keycloak authentication provider with automatic scope management to eliminate client-side scope configuration requirements. Key improvements: - Server-side injection of required scopes into client registration and authorization requests - Automatic FastMCP compatibility modifications of Keycloak client registration responses - Updated Keycloak test realm configuration to resolve trusted host and duplicate client scope issues - Enhanced example with proper scope handling and user claim access
Remove hardcoded fastmcp-client configuration and add DCR policy and profile to enable dynamic client registration. This allows clients to register automatically at runtime rather than requiring pre-configured client entries.
Implement complete test coverage for the Keycloak authentication provider with 23 comprehensive tests (16 unit tests, 7 integration tests) covering all aspects of OAuth integration and Dynamic Client Registration (DCR). Key Features Tested: - Dynamic Client Registration (DCR) with scope injection - FastMCP compatibility modifications (auth method, response types) - OAuth proxy architecture for CORS prevention - Server-configured required scopes automatic injection - JWT token verification with JWKS integration - Complete inheritance from RemoteAuthProvider All tests pass with zero warnings and verify the provider is production-ready for both Docker development environments and enterprise Keycloak deployments.
Docker setup - Upgrade Keycloak image from 26.2 to 26.3 - Rename realm-export.json to realm-fastmcp.json for clarity - Simplify docker-compose.yml configuration by removing unnecesary settings and relying on defaults instead - Add Docker network gateway IP (172.17.0.1) to trusted hosts for improved container networking - Update realm configuration to use cleaner policy and profile names
Keycloak restart in auth example - Include detailed comments explaining the Keycloak restart scenario and the "We are sorry... Client not found" error that may show up - Add a code snippet enabling users to easily clear their OAuth cache when running their client in such situations
- Create complete Keycloak integration guide with step-by-step setup instructions - Include Docker setup examples and realm configuration import process - Document Dynamic Client Registration (DCR) configuration and troubleshooting - Provide environment variable configuration options and examples - Include advanced configuration scenarios with custom token verifiers - Add troubleshooting section for resolution of common "Client not found" error in Keycloak restart scenarios - Update docs navigation to include Keycloak integration
jlowin
left a comment
There was a problem hiding this comment.
It feels like there's way too much going on in this PR. Did an LLM author this?
- The example should be a minimal server/client pair like all other auth providers. There should definitely not be a docker-compose file.
- keycloak.py seems to reimplement our oauthproxy AND oidcproxy while still claiming (in the linked issue) to natively support DCR? Ideally this would be a minor amount of configuration against one of FastMCP's establish auth classes.
Split the verbose Keycloak README.md to improve clarity and match the simplicity of other auth provider examples: - Move Keycloak setup details to keycloak/README.md - Simplify main README.md with two clear options: - Option A: Local Keycloak instance (automatic realm import) - Option B: Existing Keycloak instance (manual realm import) - Reorganize Docker files into keycloak/ subdirectory - Rename setup.sh to start-keycloak.sh for clarity - Remove Python venv setup from start script (focus on Keycloak only) - Add prerequisites, troubleshooting, and configuration details to keycloak/README.md - Clarify that realm is auto-imported for local Docker setup - Add note about restarting Keycloak after configuration changes
|
I'm uncomfortable merging this PR due to the architectural complexity and maintenance burden it introduces. This PR is over 2000 lines including the example, uses a radically different architecture from other auth integrations (namely, operational HTTP routes akin to our proxy architectures), and requires modifications that make it fundamentally independent from our core auth architecture, meaning it won't benefit from future shared auth improvements or MCP patterns. The implementation proxies multiple HTTP endpoints with request/response transformations to inject scopes, modify authentication methods, and rewrite metadata URLs. This degree of intervention suggests either: (a) Keycloak's DCR implementation has fundamental incompatibilities with MCP clients, or (b) there's a simpler solution using Keycloak configuration or existing FastMCP abstractions. The point of the remote auth provider is specifically that the server is not acting as an auth broker in any way; is there a fundamental bug in our implementation? I know Keycloak is extremely popular and standards-compliant, which makes me skeptical that it genuinely requires this unique approach. The scope injection in particular raises security questions - if clients aren't requesting required scopes themselves, we may be creating authorization bypass risks. As a path forward, I need you to demonstrate one of the following:
If none of these work, or if option 3 is valid, I'd recommend publishing this provider as Can you test option 1 (minimal RemoteAuthProvider) and report back what specifically breaks? |
|
Thank you for your detailed feedback. I fully understand your concerns and agree that we need to strike a reasonable balance between introducing new features/providers and avoiding unnecessary codebase bloat. I’ll review the options you suggested, and we’ll see which approach makes the most sense. Please just keep in mind that this may take a little time. |
Enhances start-keycloak.sh to automatically detect and support both Docker Compose v1 (docker-compose) and v2 (docker compose) commands. Adds helpful installation instructions if Docker Compose is not found, and displays version-specific commands in the output.
OAuth client now automatically handles stale credentials and re-registers with Keycloak when needed. Manual cache clearing is no longer required.
Keycloak tokens don't include 'aud' claim by default. Changed example to disable audience validation for development (audience=None). Production deployments should configure Keycloak audience mappers and set the FASTMCP_SERVER_AUTH_KEYCLOAK_AUDIENCE environment variable.
Enable MCP Inspector support for the Keycloak OAuth example by addressing three key compatibility requirements: 1. CORS Configuration (server.py): - Add CORSMiddleware with proper headers for browser-based clients - Expose mcp-session-id header required for stateful HTTP transport - Configure allowed headers: mcp-protocol-version, mcp-session-id, Authorization - Reference: https://gofastmcp.com/deployment/http#cors-for-browser-based-clients 2. Trusted Hosts (realm-fastmcp.json): - Add github.com to trusted hosts for MCP Inspector origin - Enable dynamic client registration from Inspector's GitHub-hosted UI 3. Offline Access Support (realm-fastmcp.json): - Add defaultOptionalClientScopes: ["offline_access"] - Grant offline_access realm role to test user - Enable long-lived refresh tokens for Inspector sessions Additional improvements: - Remove FileTokenStorage in favor of automatic retry mechanism - Disable audience validation by default (Keycloak doesn't include aud claim) - Simplify realm configuration (minimal scopes: openid, profile, email, offline_access) - Add comprehensive MCP Inspector documentation to README - Update troubleshooting guide with Inspector-specific restart instructions - Document Docker Compose v2 syntax and realm configuration reload process Both Python client and MCP Inspector now work seamlessly with Keycloak OAuth.
717dcb7 to
2e546e5
Compare
…fectHQ#1937) This commit drastically simplifies the Keycloak provider from a full OAuth proxy to a minimal DCR-only workaround, implementing the suggested "option 1" approach from PR feedback. **Architectural change: 3 proxy routes → 1 proxy route** BEFORE (full OAuth proxy): - /authorize proxy (authorization endpoint with scope injection) - /register proxy (full DCR with request/response modifications) - /.well-known/oauth-authorization-server (metadata with modifications) - Complex OIDC discovery and configuration management AFTER (minimal DCR proxy): - /register proxy (fixes only token_endpoint_auth_method field) - /.well-known/oauth-authorization-server (simple forwarding) - Hard-coded Keycloak URL patterns (no OIDC discovery) - Authorization flows, token issuance, and validation go directly to Keycloak **Why the minimal proxy is needed:** Keycloak ignores the client's requested token_endpoint_auth_method and always returns "client_secret_basic", but MCP requires "client_secret_post" per RFC 9110. The minimal proxy intercepts only DCR responses to fix this single field. **Changes:** - Reduce full OAuth proxy to minimal DCR proxy (as detailed above) - Simplify realm configuration: * Remove clientProfiles/clientPolicies (executors unavailable in Keycloak 26.3+) * Use realm-level default scopes and explicit clientScopes definitions - Update documentation: * Add MCP Compatibility Note explaining the workaround * Update MCP Inspector instructions with scope requirements - Add integration test proving Keycloak's DCR limitation and verifying the fix Addresses: PrefectHQ#1937 (comment)
I've implemented option 1 as closely as possible. The Keycloak provider now adds only 1 additional route (/register) compared to a standard RemoteAuthProvider, making it a truly minimal implementation. Why the DCR proxy is necessary: Keycloak has a bug where it ignores the client's requested The inconsistent |
Adds missing 'basic' client scope to the fastmcp realm configuration to resolve Keycloak warning: "Referenced client scope 'basic' doesn't exist. Ignoring" The scope is configured as an openid-connect protocol scope that is included in tokens but not displayed on the consent screen.
|
Call for Support: Vote on Keycloak Issue #44403 For those following this PR and waiting for native Keycloak OAuth support in FastMCP, I'd like to share an important update. The SituationAs discussed in my previous comment, I've simplified the Keycloak provider to a minimal implementation (option 1 from @jlowin's feedback). The provider now adds only one additional route ( Why is this workaround necessary? Keycloak doesn't handle DCR properly, creating an incompatibility with the MCP specification. Without a fix from Keycloak, we cannot fully implement option 1 (using How You Can HelpI've filed this as a bug with the Keycloak team: keycloak/keycloak#44403 The maintainer has acknowledged the issue but marked it as low priority. If you're interested in seeing clean Keycloak + FastMCP integration, please consider:
Once Keycloak fixes this DCR behavior, we can potentially eliminate the Thanks for your patience and support! |
|
The keycloak issue is now marked as high priority, thanks to other folks voting for it. We did manage to use code based off this PR for a demo of Keycloak that works fully in VS Code: So if other folks do need to use Keycloak, I recommend looking at this PR or our repo for inspiration. Make sure your audience is set up correctly in realms.json, we had a few issues until we adjusted that. |
|
Just heard from @pamelafox that the Keycloak DCR fix was merged! Very exciting. @stephaneberle9 if you revisit this PR, please note that there have been significant changes prepping for FastMCP 3.0. I think the only one that really intersects this is we've removed required support for auth env var settings e.g. |
Test Failure AnalysisSummary: Static analysis ( Root Cause: In Suggested Solution: Add an File: Add this line before the assertions: assert isinstance(provider.token_verifier, JWTVerifier)This tells the type checker that Detailed AnalysisType Checker OutputWhy This Pattern WorksThe
The failing test is the only one missing this assertion. Related Files
|
Add isinstance assertions to narrow token_verifier type from TokenVerifier to JWTVerifier before accessing JWTVerifier-specific attributes (jwks_uri, issuer). This resolves "possibly-missing-attribute" warnings from ty check.
Remove automatic environment variable loading from KeycloakAuthProvider. The provider now requires explicit parameter passing instead of using BaseSettings, aligning with FastMCP 3.0 design principles. Changes: - Remove KeycloakProviderSettings class and pydantic-settings dependency - Remove NotSet/NotSetT sentinel pattern - Simplify __init__ to require realm_url and base_url directly - Add scope parsing for both string and list formats - Remove env var tests and update example to use explicit os.getenv() - Remove "Environment Variables" section from documentation - Maintain FASTMCP_SERVER_AUTH_KEYCLOAK_* prefix in examples for consistency with other auth providers
- Split Testing section into "Running the Server" and "Testing with a Client" subsections - Keep MCP Inspector documentation in Testing section - Add Production Configuration section with Redis-based persistent storage example - Add Features section highlighting DCR, JWT validation, user claims, and enterprise integration
- Add note about Keycloak PR #45309 (merged Jan 12, 2026) that fixes token_endpoint_auth_method - Clarify that KeycloakAuthProvider's proxy workaround is only needed for older versions - Update documentation to explain the workaround becomes unnecessary after upgrade - Update code comments to reflect this is a temporary compatibility layer - Maintain backward compatibility for users on older Keycloak versions
|
Great news about the Keycloak DCR fix! 🎉 That's a significant improvement for the ecosystem. Anyhow, I think we should keep the FastMCP workaround in place for now to support users running older Keycloak versions (which is likely the majority until the fix is released and adopted). The workaround is minimal and will simply have no effect once users upgrade to a Keycloak version that includes the fix. What do you think? As per 9cc1066, I've updated the documentation to reference PR #45309 and clarify that the workaround is only needed for older versions. Separately, I've updated this PR to remove |
…provider # Conflicts: # docs/docs.json
|
@stephaneberle9 appreciate the update, and thanks for removing That said, this PR still takes on more scope than I'm comfortable with in core. With the upstream Keycloak DCR fix already merged, I don't think we should be shipping a proxy workaround for non-compliant versions — that's surface area we'd have to support indefinitely and auth is not an area I want to take any artistic license that we can avoid. Similarly, the example code goes well beyond what the other auth provider examples do; it's not FastMCP's job to demonstrate how to run Keycloak locally. What I'd like to see here is a slim ~50 line provider that configures |
|
Thanks a lot for your honest feedback — I really appreciate the clarity. I fully understand the desire to keep FastMCP’s codebase lean and avoid carrying extra logic for older Keycloak versions. The project’s focus is FastMCP, not FastAuth, and it makes total sense to minimize long‑term maintenance for third‑party auth integrations. The only complication here is timing: the Keycloak fix we depend on is already merged, but it’s scheduled for release with 26.6.0, which won’t be published until the end of March, 2026. Unfortunately, Keycloak doesn’t provide milestone or snapshot builds that we could temporarily depend on in the meantime. Given that, my suggestion would be to wait for the official Keycloak release that includes the fix. Once 26.6.0 is out, I can simplify this PR exactly as proposed, remove the compatibility code, and finalize the integration cleanly. Happy to adjust the plan if you see a better path, but this feels like the most stable and least intrusive approach for FastMCP. |
Description
This PR implements native Keycloak OAuth support for FastMCP, enabling enterprise-grade authentication for organizations using Keycloak identity and access management infrastructure. The implementation provides a complete authentication provider that handles Keycloak User Realms with robust JWT token validation and Dynamic Client Registration (DCR) support. This contribution also significantly simplifies local development by providing a Docker-based Keycloak setup that allows developers to test OAuth flows instantly without external dependencies.
Contributors Checklist
Review Checklist
@coderabbitai ignore