-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add Keycloak OAuth Provider for Enterprise Authentication and local dev #1937
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add Keycloak OAuth Provider for Enterprise Authentication and local dev #1937
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
WalkthroughAdds a Keycloak OAuth auth provider with OIDC discovery and Dynamic Client Registration proxying, plus accompanying docs, examples (server, client, env), local Keycloak Docker setup and scripts, and unit + integration tests for provider and end-to-end flows. Changes
Poem
Pre-merge checks and finishing touches✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (14)
docs/docs.json(1 hunks)docs/integrations/keycloak.mdx(1 hunks)examples/auth/keycloak_auth/.env.example(1 hunks)examples/auth/keycloak_auth/README.md(1 hunks)examples/auth/keycloak_auth/client.py(1 hunks)examples/auth/keycloak_auth/keycloak/README.md(1 hunks)examples/auth/keycloak_auth/keycloak/docker-compose.yml(1 hunks)examples/auth/keycloak_auth/keycloak/realm-fastmcp.json(1 hunks)examples/auth/keycloak_auth/keycloak/start-keycloak.sh(1 hunks)examples/auth/keycloak_auth/requirements.txt(1 hunks)examples/auth/keycloak_auth/server.py(1 hunks)src/fastmcp/server/auth/providers/keycloak.py(1 hunks)tests/integration_tests/auth/test_keycloak_provider_integration.py(1 hunks)tests/server/auth/providers/test_keycloak.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write instructions and procedures in second person ("you")
Use active voice over passive voice
Use present tense for current states and future tense for outcomes
Maintain consistent terminology across the documentation
Keep sentences concise while preserving necessary context
Use parallel structure in lists, headings, and procedures
Lead with the most important information (inverted pyramid)
Use progressive disclosure: basic concepts before advanced ones
Break complex procedures into numbered steps
Include prerequisites and context before instructions
Provide expected outcomes for each major step
End sections with next steps or related information
Use descriptive, keyword-rich headings for navigation and SEO
Focus on user goals and outcomes rather than system features
Anticipate common questions and address them proactively
Include troubleshooting for likely failure points
Offer multiple pathways when appropriate (beginner vs advanced) and provide an opinionated recommended path
Use for supplementary information that supports the main content
Use for expert advice, shortcuts, or best practices
Use for critical cautions, breaking changes, or destructive actions
Use for neutral background or contextual information
Use to confirm success or completion
Provide single code examples using fenced code blocks with language (and filename when relevant)
Use to present the same concept in multiple languages
For API docs, use to show requests
For API docs, use to show responses
Use and to document procedures and sequential instructions
Use and for platform-specific or alternative approaches
Use / for supplementary content that might interrupt flow
In API docs, use for parameters (path, body, query, header) with type and required/default as appropria...
Files:
docs/integrations/keycloak.mdx
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
docs/**/*.{md,mdx}: Documentation code examples should be explained before the code and be fully runnable, including imports
Use clear headers with logical H2/H3 hierarchy to form navigation
Write user-focused content that motivates the why before the how
Prefer prose for important information over code comments in docs
Files:
docs/integrations/keycloak.mdx
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{md,mdx}: Be brief and to the point in written materials; avoid regurgitating obvious code details
Avoid defensive constructions like "This isn't X" or "Not just X, but Y"; state what it is directly
Files:
docs/integrations/keycloak.mdxexamples/auth/keycloak_auth/keycloak/README.mdexamples/auth/keycloak_auth/README.md
{docs.json,docs/docs.json}
📄 CodeRabbit inference engine (AGENTS.md)
Documentation pages must be listed in docs.json to be included
Files:
docs/docs.json
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare except; always catch specific exception types
Files:
examples/auth/keycloak_auth/client.pytests/integration_tests/auth/test_keycloak_provider_integration.pytests/server/auth/providers/test_keycloak.pyexamples/auth/keycloak_auth/server.pysrc/fastmcp/server/auth/providers/keycloak.py
tests/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
tests/**/*.py: Tests must be atomic, self-contained, and cover a single functionality
Use pytest parameterization for multiple examples of the same functionality
Use separate tests for distinct pieces of functionality
Always put imports at the top of test files; do not import inside test bodies
Do not add @pytest.mark.asyncio; asyncio_mode = "auto" is set globally
Prefer in-memory transport for tests; use HTTP transport only when explicitly testing networking
For slow/long-running tests, mark them as integration or optimize (default timeout is 5s)
In tests, use # type: ignore[attr-defined] for MCP results instead of type assertions
Files:
tests/integration_tests/auth/test_keycloak_provider_integration.pytests/server/auth/providers/test_keycloak.py
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Use Python ≥ 3.10 and provide full type annotations for library code
Files:
src/fastmcp/server/auth/providers/keycloak.py
🧬 Code graph analysis (4)
tests/integration_tests/auth/test_keycloak_provider_integration.py (2)
src/fastmcp/server/server.py (2)
FastMCP(148-2693)http_app(2136-2183)src/fastmcp/server/auth/providers/keycloak.py (1)
KeycloakAuthProvider(46-396)
tests/server/auth/providers/test_keycloak.py (3)
src/fastmcp/server/server.py (1)
http_app(2136-2183)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)
examples/auth/keycloak_auth/server.py (3)
src/fastmcp/server/auth/providers/keycloak.py (1)
KeycloakAuthProvider(46-396)src/fastmcp/server/dependencies.py (1)
get_access_token(104-135)src/fastmcp/utilities/logging.py (1)
configure_logging(29-95)
src/fastmcp/server/auth/providers/keycloak.py (5)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)src/fastmcp/utilities/logging.py (1)
get_logger(14-26)
🪛 dotenv-linter (4.0.0)
examples/auth/keycloak_auth/.env.example
[warning] 3-3: [UnorderedKey] The FASTMCP_SERVER_AUTH_KEYCLOAK_BASE_URL key should go before the FASTMCP_SERVER_AUTH_KEYCLOAK_REALM_URL key
(UnorderedKey)
[warning] 6-6: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🪛 markdownlint-cli2 (0.18.1)
examples/auth/keycloak_auth/keycloak/README.md
28-28: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
32-32: Trailing spaces
Expected: 0 or 2; Actual: 1
(MD009, no-trailing-spaces)
129-129: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
examples/auth/keycloak_auth/README.md
20-20: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
24-24: Trailing spaces
Expected: 0 or 2; Actual: 1
(MD009, no-trailing-spaces)
26-26: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run tests with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.10 on windows-latest
| - **Dynamic Client Registration** enabled for `http://localhost:8000/*` | ||
| - **Registration Allowed**: Yes | ||
| - **Allowed Client Scopes**: `openid`, `profile`, `email`, `roles`, `offline_access`, `web-origins`, `basic` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the Docker Compose reference.
The link text/path points to examples/auth/keycloak_auth/docker-compose.yml, but the compose file actually lives under examples/auth/keycloak_auth/keycloak/docker-compose.yml. Following the current link gives a 404. Please update both the label (docker-compose.yml) and the href to include the keycloak/ subdirectory.
-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/docker-compose.yml) file included in the Keycloak auth example.
+If you prefer using Docker Compose instead, review [`docker-compose.yml`](https://github.com/jlowin/fastmcp/blob/main/examples/auth/keycloak_auth/keycloak/docker-compose.yml) included with the Keycloak auth example.Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/auth/keycloak_auth/keycloak/README.md around lines 38 to 40, the
Docker Compose link text and href wrongly point to
examples/auth/keycloak_auth/docker-compose.yml; update the link label to
"keycloak/docker-compose.yml" (or include the subdirectory in the label) and
change the href to examples/auth/keycloak_auth/keycloak/docker-compose.yml so
the link points to the actual file location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed as suggested as per eb73267
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the current content of the README to verify the Docker Compose link fix
cat examples/auth/keycloak_auth/keycloak/README.md | grep -A 2 -B 2 "docker-compose"
Length of output: 81
🏁 Script executed:
#!/bin/bash
# Check the specific commit mentioned by the user
git show eb732675686c59d86c97ab3d362dd7f3e28eecc3 --stat
Length of output: 1654
🏁 Script executed:
#!/bin/bash
# Show the actual changes in that commit for the README file
git show eb732675686c59d86c97ab3d362dd7f3e28eecc3 -- examples/auth/keycloak_auth/keycloak/README.md | head -50
Length of output: 1873
@stephaneberle9 Thank you for the quick fix! I can confirm the commit addresses the path issues in the keycloak/README.md file. The troubleshooting section paths have been corrected to reference files relative to the keycloak directory (e.g., realm-fastmcp.json instead of keycloak/realm-fastmcp.json), which makes sense since users would be running commands from within that directory. The changes look good! ✅
tests/integration_tests/auth/test_keycloak_provider_integration.py
Outdated
Show resolved
Hide resolved
tests/integration_tests/auth/test_keycloak_provider_integration.py
Outdated
Show resolved
Hide resolved
tests/integration_tests/auth/test_keycloak_provider_integration.py
Outdated
Show resolved
Hide resolved
Address 11 actionable code review comments from CodeRabbitAI: - Fix FileTokenStorage import and add CLEAR_TOKEN_CACHE feature flag in client example - Remove manual scope splitting in server.py, delegate to KeycloakProviderSettings parser - Merge client-requested scopes with required scopes in registration endpoint - Always append missing required scopes to authorization requests - Fix test mocking to only patch provider's httpx imports, not globally - Move asyncio import to module scope in integration tests - Fix markdown linting issues (convert bold to headings, remove trailing space) - Fix troubleshooting paths in keycloak/README.md - Pin dependency versions in requirements.txt for reproducibility These changes ensure proper scope handling that preserves client intent while enforcing server requirements, improve test isolation, and enhance code quality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/integration_tests/auth/test_keycloak_provider_integration.pyis excluded by none and included by none
📒 Files selected for processing (6)
examples/auth/keycloak_auth/README.md(1 hunks)examples/auth/keycloak_auth/client.py(1 hunks)examples/auth/keycloak_auth/keycloak/README.md(1 hunks)examples/auth/keycloak_auth/requirements.txt(1 hunks)examples/auth/keycloak_auth/server.py(1 hunks)src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{md,mdx}: Be brief and to the point in written materials; avoid regurgitating obvious code details
Avoid defensive constructions like "This isn't X" or "Not just X, but Y"; state what it is directly
Files:
examples/auth/keycloak_auth/keycloak/README.mdexamples/auth/keycloak_auth/README.md
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare except; always catch specific exception types
Files:
examples/auth/keycloak_auth/server.pyexamples/auth/keycloak_auth/client.pysrc/fastmcp/server/auth/providers/keycloak.py
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Use Python ≥ 3.10 and provide full type annotations for library code
Files:
src/fastmcp/server/auth/providers/keycloak.py
🧠 Learnings (10)
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Confirm proper heading hierarchy with H2 for main sections, H3 for subsections
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.613Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.613Z
Learning: Applies to docs/**/*.mdx : Use <Warning> for critical cautions, breaking changes, or destructive actions
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Add appropriate <Warning> callouts for destructive or security-sensitive actions
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Ensure proper heading hierarchy starting with H2 for main sections and H3 for subsections
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Check for consistency in terminology, formatting, and component usage
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Use <Tabs> for platform-specific content or alternative approaches
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:40:00.422Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-27T14:40:00.422Z
Learning: Applies to docs/**/*.{md,mdx} : Use clear headers with logical H2/H3 hierarchy to form navigation
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Include verification/testing steps with expected outcomes
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.614Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.614Z
Learning: Applies to docs/**/*.mdx : Explain prerequisites clearly before instructions
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.613Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.613Z
Learning: Applies to docs/**/*.mdx : Use parallel structure in lists, headings, and procedures
Applied to files:
examples/auth/keycloak_auth/README.md
🧬 Code graph analysis (3)
examples/auth/keycloak_auth/server.py (3)
src/fastmcp/server/auth/providers/keycloak.py (1)
KeycloakAuthProvider(46-407)src/fastmcp/server/dependencies.py (1)
get_access_token(104-135)src/fastmcp/utilities/logging.py (1)
configure_logging(29-95)
examples/auth/keycloak_auth/client.py (1)
src/fastmcp/client/client.py (1)
Client(97-941)
src/fastmcp/server/auth/providers/keycloak.py (5)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)src/fastmcp/utilities/logging.py (1)
get_logger(14-26)
🪛 markdownlint-cli2 (0.18.1)
examples/auth/keycloak_auth/keycloak/README.md
28-28: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
32-32: Trailing spaces
Expected: 0 or 2; Actual: 1
(MD009, no-trailing-spaces)
129-129: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run tests: Python 3.10 on windows-latest
- GitHub Check: Run tests with lowest-direct dependencies
Address 3 additional code review comments from CodeRabbitAI: - Fix markdownlint violations in keycloak/README.md: - Add bash language tag to docker-compose restart code block - Remove trailing whitespace - Add blank line after Python code fence for proper list continuation - Guard against missing access token in server.py get_access_token_claims tool: - Check if token or token.claims is None before accessing claims - Raise RuntimeError with clear message instead of AttributeError - Provides better error handling for unauthenticated requests - Preserve Authorization header when proxying client registration in keycloak.py: - Forward all incoming headers except Host to support authenticated DCR - Enables realms that require initial access tokens or bearer tokens - Avoids routing issues by excluding Host header - Ensures Content-Type is always application/json for modified body These changes improve error handling, support authenticated Dynamic Client Registration scenarios, and ensure documentation passes linting checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
examples/auth/keycloak_auth/keycloak/README.md(1 hunks)examples/auth/keycloak_auth/server.py(1 hunks)src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Never use bare except; always catch specific exception types
Files:
examples/auth/keycloak_auth/server.pysrc/fastmcp/server/auth/providers/keycloak.py
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{md,mdx}: Be brief and to the point in written materials; avoid regurgitating obvious code details
Avoid defensive constructions like "This isn't X" or "Not just X, but Y"; state what it is directly
Files:
examples/auth/keycloak_auth/keycloak/README.md
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Use Python ≥ 3.10 and provide full type annotations for library code
Files:
src/fastmcp/server/auth/providers/keycloak.py
🧬 Code graph analysis (2)
examples/auth/keycloak_auth/server.py (3)
src/fastmcp/server/auth/providers/keycloak.py (1)
KeycloakAuthProvider(46-415)src/fastmcp/server/dependencies.py (1)
get_access_token(104-135)src/fastmcp/utilities/logging.py (1)
configure_logging(29-95)
src/fastmcp/server/auth/providers/keycloak.py (5)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)src/fastmcp/utilities/logging.py (1)
get_logger(14-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run tests with lowest-direct dependencies
- GitHub Check: Run tests: Python 3.10 on windows-latest
| super().__init__( | ||
| token_verifier=token_verifier, | ||
| authorization_servers=[AnyHttpUrl(base_url)], | ||
| base_url=base_url, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the invalid AnyHttpUrl call
AnyHttpUrl is a type alias (typing.Annotated) in Pydantic v2, so calling AnyHttpUrl(base_url) raises TypeError: 'typing.Annotated' object is not callable. As soon as KeycloakAuthProvider is instantiated the app will crash, so no request ever succeeds. Pass the already validated realm/base URL as a plain string (or via a TypeAdapter) instead.
- super().__init__(
- token_verifier=token_verifier,
- authorization_servers=[AnyHttpUrl(base_url)],
- base_url=base_url,
- )
+ super().__init__(
+ token_verifier=token_verifier,
+ authorization_servers=[str(settings.realm_url)],
+ base_url=base_url,
+ )Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/fastmcp/server/auth/providers/keycloak.py around lines 152 to 156, the
code mistakenly calls AnyHttpUrl(base_url) (Pydantic v2 AnyHttpUrl is a
typing.Annotated and not callable) which raises a TypeError at instantiation;
fix by passing the already-validated realm/base_url as a plain string into
authorization_servers (or, if you need runtime validation, run the value through
a Pydantic TypeAdapter for AnyHttpUrl and pass the adapter-validated string),
ensuring authorization_servers receives a string URL rather than calling
AnyHttpUrl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The claim that AnyHttpUrl(base_url) is invalid is incorrect.
Evidence
-
Direct testing confirms it works:
from pydantic import AnyHttpUrl, TypeAdapter import sys print(f"Python version: {sys.version}") print(f"AnyHttpUrl type: {type(AnyHttpUrl)}") print(f"AnyHttpUrl: {AnyHttpUrl}") # Direct instantiation url1 = AnyHttpUrl("https://example.com") print(f"SUCCESS: Direct instantiation works: {url1}") # Output: # Python version: 3.12.10 (tags/v3.12.10:0cc8128, Apr 8 2025, 12:21:36) [MSC v.1943 64 bit (AMD64)] # AnyHttpUrl type: <class 'type'> # AnyHttpUrl: <class 'pydantic.networks.AnyHttpUrl'> # SUCCESS: Direct instantiation works: https://example.com/
-
The parent class
RemoteAuthProviderconstructor explicitly expects this pattern:def __init__( self, token_verifier: TokenVerifier, authorization_servers: list[AnyHttpUrl], # ← expects AnyHttpUrl instances base_url: AnyHttpUrl | str, ... ): -
This pattern is used throughout the FastMCP codebase without issues:
- supabase.py:124: authorization_servers=[AnyHttpUrl(f"{self.project_url}/auth/v1")]
- workos.py:379: authorization_servers=[AnyHttpUrl(self.authkit_domain)]
- scalekit.py:129: AnyHttpUrl(f"{self.environment_url}/resources/{self.resource_id}")
- descope.py:121: AnyHttpUrl(f"{self.descope_base_url}/v1/apps/{self.project_id}")
-
If this were truly invalid, most tests would fail. The test suite extensively uses
AnyHttpUrl()instantiation:- All tests in
test_remote_auth_provider.py - All tests in
test_oidc_proxy.py - All tests in
test_oauth_mounting.py - And many more...
- All tests in
Conclusion: The code is correct as-is. AnyHttpUrl is fully instantiable in Pydantic v2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check RemoteAuthProvider's __init__ signature
echo "=== RemoteAuthProvider signature ==="
ast-grep --pattern $'class RemoteAuthProvider {
$$$
def __init__($$$) {
$$$
}
$$$
}'
# Check the type annotations in RemoteAuthProvider
rg -n "class RemoteAuthProvider" -A 30 --type=py
# Check AnyHttpUrl usage in other provider files mentioned
echo -e "\n=== Supabase provider AnyHttpUrl usage ==="
rg -n "AnyHttpUrl" src/fastmcp/server/auth/providers/supabase.py -C 2
echo -e "\n=== WorkOS provider AnyHttpUrl usage ==="
rg -n "AnyHttpUrl" src/fastmcp/server/auth/providers/workos.py -C 2
echo -e "\n=== Scalekit provider AnyHttpUrl usage ==="
rg -n "AnyHttpUrl" src/fastmcp/server/auth/providers/scalekit.py -C 2
echo -e "\n=== Descope provider AnyHttpUrl usage ==="
rg -n "AnyHttpUrl" src/fastmcp/server/auth/providers/descope.py -C 2
Length of output: 6631
🏁 Script executed:
#!/bin/bash
# Check where AnyHttpUrl is imported from and its definition
echo "=== AnyHttpUrl imports in codebase ==="
rg -n "from pydantic.*import.*AnyHttpUrl" --type=py | head -20
# Check Pydantic version in requirements
echo -e "\n=== Pydantic version ==="
rg "pydantic" pyproject.toml setup.py requirements.txt 2>/dev/null || echo "No explicit pydantic version found in common files"
# Check if there's a networks module or AnyHttpUrl definition
echo -e "\n=== Pydantic networks module usage ==="
rg "from pydantic.networks import" --type=py | head -10
Length of output: 2537
🏁 Script executed:
#!/bin/bash
# Check tests that use AnyHttpUrl instantiation
echo "=== Tests using AnyHttpUrl() instantiation ==="
rg -n "AnyHttpUrl\(" tests/ --type=py -C 1 | head -50
Length of output: 3783
@stephaneberle9 You're absolutely right, and I apologize for the incorrect comment. Thank you for the detailed evidence.
After reviewing the codebase, I can confirm:
-
RemoteAuthProvidersignature explicitly expectslist[AnyHttpUrl]:def __init__( self, token_verifier: TokenVerifier, authorization_servers: list[AnyHttpUrl], # ← expects AnyHttpUrl instances base_url: AnyHttpUrl | str, ... ):
-
The pattern is used consistently across all provider implementations:
supabase.py:124:authorization_servers=[AnyHttpUrl(f"{self.project_url}/auth/v1")]workos.py:379:authorization_servers=[AnyHttpUrl(self.authkit_domain)]scalekit.py:129:AnyHttpUrl(f"{self.environment_url}/resources/{self.resource_id}")descope.py:121:AnyHttpUrl(f"{self.descope_base_url}/v1/apps/{self.project_id}")
-
Tests extensively use this pattern without issues:
test_oauth_mounting.pytest_remote_auth_provider.pytest_oidc_proxy.pytest_auth_integration.py
While AnyHttpUrl is indeed a type alias in Pydantic v2, it's callable and the codebase (using Pydantic >=2.11.7) relies on this behavior throughout. Your implementation at line 152-156 is correct and follows the established pattern.
My original comment was wrong. The code is fine as-is. 🙏
|
Waiting for this to be merged :) |
Convert AnyHttpUrl types to str for httpx and JWTVerifier compatibility: - Cast registration_endpoint to str in httpx.post() call - Cast jwks_uri and issuer to str in JWTVerifier initialization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/fastmcp/server/auth/providers/keycloak.py (5)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)src/fastmcp/utilities/logging.py (1)
get_logger(14-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/fastmcp/server/auth/providers/keycloak.py (4)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
Address CodeRabbit review feedback: - Add 10s timeout to OIDC discovery to prevent startup hangs - Add 10s timeout to httpx client for registration requests - Improve error handling: wrap response.json() in try/catch and preserve Keycloak error details in error_description field - Simplify URL conversions: remove redundant None checks after discovery applies defaults (keep str() conversions for type safety)
1c9afdb to
67af227
Compare
Address two proxy-related bugs identified in code review: 1. Content-Length header mismatch: Exclude hop-by-hop headers (content-length, transfer-encoding) when forwarding client registration requests, allowing httpx to recompute the correct Content-Length after body modifications. 2. Multi-valued query parameter loss: Use multi_items() instead of dict() when proxying authorization requests to preserve duplicate query parameters (e.g., multiple 'resource' params per RFC 8707). These fixes ensure proper HTTP proxying behavior and maintain full compatibility with OAuth 2.0 and RFC 8707 standards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/fastmcp/server/auth/providers/keycloak.py (5)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)src/fastmcp/utilities/logging.py (1)
get_logger(14-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
Fix a security issue where provider-configured required scopes were silently ignored when users supplied custom token verifiers. This allowed provider-level scope requirements to be bypassed. Now merges provider-level required scopes into custom verifiers before initialization, ensuring: - Provider-mandated scopes are always enforced - Existing custom verifier scopes are preserved - No duplicate scopes are added - Both proxy logic and token verification respect scope requirements This maintains the security model where provider-level scope policies cannot be circumvented by supplying a custom verifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/fastmcp/server/auth/providers/keycloak.py (4)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
Address three robustness issues identified in code review: 1. Graceful handling of immutable verifiers: Wrap scope mutation in try-except to handle verifiers with frozen/validated required_scopes attribute. Logs a warning instead of crashing when mutation fails. 2. Avoid double-decoding responses: Cache response.content once and reuse it to prevent stream rewinding errors that occur when response.json() is called multiple times on streaming responses. 3. Preserve multi-valued query params: Add doseq=True to urlencode() to properly preserve duplicate query parameters (e.g., multiple 'resource' params per RFC 8707) that were kept by multi_items(). These fixes ensure robust HTTP proxying with various verifier implementations, large payloads, and RFC 8707 compliance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/fastmcp/server/auth/providers/keycloak.py (4)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (2)
src/fastmcp/server/auth/providers/keycloak.py (2)
173-195: LGTM!The OIDC discovery implementation correctly fetches configuration from Keycloak, applies sensible defaults for missing fields, and includes appropriate timeout handling to prevent startup hangs.
246-445: Excellent proxy implementation!The three proxy endpoints (registration, authorization, and metadata) are well-implemented with proper:
- Scope injection and merging without duplication
- HTTP header handling (excluding hop-by-hop headers)
- Multi-valued query parameter preservation using
multi_items()anddoseq=True- Response body caching to avoid double-decode issues
- Comprehensive error handling that preserves Keycloak error details
- Appropriate timeouts to prevent hangs
All the issues raised in previous review rounds have been properly addressed.
Implement configurable JWT audience validation to address security concerns about accepting tokens intended for any resource server. Changes: - Add `audience` parameter to KeycloakProviderSettings and KeycloakAuthProvider constructor - Pass audience to JWTVerifier instead of hardcoded None - Add FASTMCP_SERVER_AUTH_KEYCLOAK_AUDIENCE environment variable - Update docstrings with security notes about audience validation - Update documentation (keycloak.mdx) with security warning and configuration examples - Update example code to demonstrate audience validation with base_url as default Security impact: By default, audience validation remains disabled for backward compatibility and DCR flexibility. However, documentation now prominently encourages production deployments to configure audience validation to prevent accepting tokens issued for other services. Recommended configuration: audience="https://your-server.com" # or base_url
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
docs/integrations/keycloak.mdx(1 hunks)examples/auth/keycloak_auth/.env.example(1 hunks)examples/auth/keycloak_auth/README.md(1 hunks)examples/auth/keycloak_auth/server.py(1 hunks)src/fastmcp/server/auth/providers/keycloak.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
docs/**/*.mdx
📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)
docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write instructions and procedures in second person ("you")
Use active voice over passive voice
Use present tense for current states and future tense for outcomes
Maintain consistent terminology across the documentation
Keep sentences concise while preserving necessary context
Use parallel structure in lists, headings, and procedures
Lead with the most important information (inverted pyramid)
Use progressive disclosure: basic concepts before advanced ones
Break complex procedures into numbered steps
Include prerequisites and context before instructions
Provide expected outcomes for each major step
End sections with next steps or related information
Use descriptive, keyword-rich headings for navigation and SEO
Focus on user goals and outcomes rather than system features
Anticipate common questions and address them proactively
Include troubleshooting for likely failure points
Offer multiple pathways when appropriate (beginner vs advanced) and provide an opinionated recommended path
Use for supplementary information that supports the main content
Use for expert advice, shortcuts, or best practices
Use for critical cautions, breaking changes, or destructive actions
Use for neutral background or contextual information
Use to confirm success or completion
Provide single code examples using fenced code blocks with language (and filename when relevant)
Use to present the same concept in multiple languages
For API docs, use to show requests
For API docs, use to show responses
Use and to document procedures and sequential instructions
Use and for platform-specific or alternative approaches
Use / for supplementary content that might interrupt flow
In API docs, use for parameters (path, body, query, header) with type and required/default as appropria...
Files:
docs/integrations/keycloak.mdx
🧠 Learnings (10)
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Confirm proper heading hierarchy with H2 for main sections, H3 for subsections
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Ensure proper heading hierarchy starting with H2 for main sections and H3 for subsections
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Add appropriate <Warning> callouts for destructive or security-sensitive actions
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Use <Warning> for critical cautions, breaking changes, or destructive actions
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Check for consistency in terminology, formatting, and component usage
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Use parallel structure in lists, headings, and procedures
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Include verification/testing steps with expected outcomes
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Maintain consistent terminology across the documentation
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Use <Tabs> for platform-specific content or alternative approaches
Applied to files:
examples/auth/keycloak_auth/README.md
📚 Learning: 2025-10-27T14:38:52.643Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: docs/.cursor/rules/mintlify.mdc:0-0
Timestamp: 2025-10-27T14:38:52.643Z
Learning: Applies to docs/**/*.mdx : Include troubleshooting for likely failure points
Applied to files:
examples/auth/keycloak_auth/README.md
🧬 Code graph analysis (2)
examples/auth/keycloak_auth/server.py (4)
src/fastmcp/server/server.py (1)
FastMCP(149-2699)src/fastmcp/server/auth/providers/keycloak.py (1)
KeycloakAuthProvider(47-470)src/fastmcp/server/dependencies.py (1)
get_access_token(104-135)src/fastmcp/utilities/logging.py (1)
configure_logging(29-95)
src/fastmcp/server/auth/providers/keycloak.py (4)
src/fastmcp/server/auth/auth.py (2)
RemoteAuthProvider(192-262)TokenVerifier(166-189)src/fastmcp/server/auth/oidc_proxy.py (1)
OIDCConfiguration(27-169)src/fastmcp/server/auth/providers/jwt.py (1)
JWTVerifier(165-498)src/fastmcp/utilities/auth.py (1)
parse_scopes(9-34)
🪛 dotenv-linter (4.0.0)
examples/auth/keycloak_auth/.env.example
[warning] 3-3: [UnorderedKey] The FASTMCP_SERVER_AUTH_KEYCLOAK_BASE_URL key should go before the FASTMCP_SERVER_AUTH_KEYCLOAK_REALM_URL key
(UnorderedKey)
[warning] 10-10: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
Address code review feedback: 1. Fix broken Docker Compose link: Update path to point to keycloak/docker-compose.yml subdirectory in documentation 2. Fix .env.example formatting: Alphabetize environment variable keys (BASE_URL before REALM_URL) and add trailing newline to satisfy dotenv-linter 3. Fix environment loading order: Call load_dotenv() before configure_logging() in example server to ensure logging-related environment variables from .env are properly applied These changes improve documentation accuracy and ensure the example follows best practices for environment variable handling.
|
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. |
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