-
Notifications
You must be signed in to change notification settings - Fork 381
feat: add http hooks #1394
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?
feat: add http hooks #1394
Conversation
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
…nto refactor/plugins
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
…nto refactor/plugins
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Frederico Araujo <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
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.
Needs a few changes to fix how the Http payload is created to avoid passing the headers positionally. Need to fix pylint issues. Otherwise, looks good.
Closes: #1019
| payload=HttpPreRequestPayload( | ||
| path=str(request.url.path), | ||
| method=request.method, | ||
| headers=HttpHeaderPayload(dict(request.headers)), |
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.
Pydantic model should not use positional args.
headers=HttpHeaderPayload(root=dict(request.headers)),
mcpgateway/auth.py
Outdated
| HttpHookType.HTTP_AUTH_RESOLVE_USER, | ||
| payload=HttpAuthResolveUserPayload( | ||
| credentials=credentials_dict, | ||
| headers=HttpHeaderPayload(headers), |
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.
Pydantic model should not use positional args. Use root kwarg.
| # POST-REQUEST HOOK: Allow plugins to inspect and modify response | ||
| try: | ||
| # Extract response headers | ||
| response_headers = HttpHeaderPayload(dict(response.headers)) |
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.
Pydantic model should not use positional args. Use root kwarg.
| payload=HttpPostRequestPayload( | ||
| path=str(request.url.path), | ||
| method=request.method, | ||
| headers=HttpHeaderPayload(dict(request.headers)), |
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.
Pydantic model should not use positional args. Use root kwarg.
mcpgateway/auth.py
Outdated
| Returns: | ||
| PluginManager instance if plugins are enabled, None otherwise. | ||
| """ | ||
| global _plugin_manager |
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.
Need a pylint exception here, or change the code not to use global.
Signed-off-by: Frederico Araujo <[email protected]>
|
Pushed some changes to fix the HTTP payload object initialization and pylint issues. @crivetimihai This PR should be merged after #1372 Also, this PR is a dependency for other teams integrating ContextForge. I tagged it for 0.9.0, but as discussed, we could slate it to a 0.9.1 targeted before Nov 14. |
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
Signed-off-by: Teryl Taylor <[email protected]>
HTTP Authentication Hooks
Overview
HTTP authentication hooks enable plugins to customize how MCP Gateway authenticates incoming requests. These hooks support custom authentication mechanisms like API keys, LDAP, mTLS certificates, and external authentication services without modifying core gateway code.
Why HTTP Authentication Hooks?
Traditional authentication in MCP Gateway supports:
However, enterprises often need:
HTTP authentication hooks solve these problems by allowing plugins to participate in the authentication flow without modifying core code.
Architecture: Three-Layer Design
The authentication hook system has three layers that work together:
Layer 1: Middleware (Header Transformation)
Hook:
HTTP_PRE_REQUESTRuns before authentication logic in middleware. Transforms custom headers to standard formats.
Use Cases:
X-API-Key→Authorization: Bearer <token>Layer 2: Auth Resolution (User Authentication)
Hook:
HTTP_AUTH_RESOLVE_USERRuns inside
get_current_user()before standard JWT validation. Implements custom authentication.Use Cases:
Layer 3: Permission Checking (RBAC Override)
Hook:
HTTP_AUTH_CHECK_PERMISSIONRuns before RBAC permission checks in route decorators. Allows plugins to grant/deny permissions based on custom logic.
Use Cases:
Hook Types
HTTP_PRE_REQUEST
Location: Middleware layer
Timing: Before any authentication
Payload:
HttpPreRequestPayloadReturns:
PluginResult[HttpHeaderPayload]- Modified headers onlyExample:
Important: Modified headers are applied to the request by updating
request.scope["headers"](the ASGI scope), making them immediately visible to all downstream code including FastAPI'sbearer_schemedependency, route handlers, and other middleware.HTTP_POST_REQUEST
Location: Middleware layer
Timing: After request completion
Payload:
HttpPostRequestPayloadReturns:
PluginResult[HttpHeaderPayload]- Modified response headersUse Cases:
Example (Adding correlation ID to response):
HTTP_AUTH_RESOLVE_USER
Location: Auth layer (inside
get_current_user())Timing: Before standard JWT validation
Payload:
HttpAuthResolveUserPayloadReturns:
PluginResult[dict]- Authenticated user dictionaryUser Dictionary Format:
{ "email": "[email protected]", # Required: User email "full_name": "User Name", # Optional: Display name "is_admin": False, # Optional: Admin flag "is_active": True, # Optional: Active status "password_hash": "", # Optional: Not used for custom auth "email_verified_at": datetime(...), # Optional: Verification timestamp "created_at": datetime(...), # Optional: Creation timestamp "updated_at": datetime(...), # Optional: Update timestamp }Example:
Important: Set
continue_processing=True(notFalse) to allow the auth middleware to use your user data. The plugin manager interpretscontinue_processing=Truewith amodified_payloadas "I'm providing data, use it, but don't block other plugins."HTTP_AUTH_CHECK_PERMISSION
Location: RBAC layer (inside
require_permissiondecorator)Timing: Before RBAC permission checks, after authentication
Payload:
HttpAuthCheckPermissionPayloadReturns:
PluginResult[HttpAuthCheckPermissionResultPayload]- Permission decisionPermission Result Payload:
Example (Grant full permissions to token-authenticated users):
Use Cases:
Complete Example: Custom API Key Authentication
This example shows both layers working together.
Plugin Implementation
Plugin Configuration
Usage
Hook Result Handling
HTTP_AUTH_RESOLVE_USER Results
Plugins can return three types of results from this hook:
1. Successful Authentication
Return:
PluginResultwithmodified_payload(user dict) andcontinue_processing=TrueResult: User is authenticated using plugin's user data. The
auth_methodfrom metadata is stored inrequest.statefor use by permission hooks.Important: Use
continue_processing=True(notFalse). The plugin manager interpretsTruewithmodified_payloadas "I'm providing data, use it."2. Explicit Authentication Denial
Raise:
PluginViolationErrorwith custom error messageResult: HTTP 401 Unauthorized with the custom error message in the response body.
3. Fallback to Standard Authentication
Return:
PluginResultwithcontinue_processing=Trueand no payloadResult: Gateway falls back to standard JWT/API token validation.
HTTP_AUTH_CHECK_PERMISSION Results
Plugins can return three types of results from this hook:
1. Grant Permission
Return:
PluginResultwithmodified_payloadcontaininggranted=TrueResult: Permission is granted, user can access the resource.
2. Deny Permission
Return:
PluginResultwithmodified_payloadcontaininggranted=FalseResult: HTTP 403 Forbidden, user cannot access the resource.
3. Fallback to RBAC
Return:
PluginResultwithcontinue_processing=Trueand no payloadResult: Gateway falls back to standard RBAC permission checks.
When to Use Each Result Type
For HTTP_AUTH_RESOLVE_USER
For HTTP_AUTH_CHECK_PERMISSION
Request Flow
Key Data Flow:
GlobalContextandrequest.statemetadata, stored inrequest.state, read by permission pluginHook Invocation Order: PRE_REQUEST → AUTH_RESOLVE_USER → AUTH_CHECK_PERMISSION → POST_REQUEST
Advanced Use Cases
mTLS Certificate Authentication
LDAP/Active Directory
Audit Logging (POST_REQUEST)
Security Considerations
Fallback Behavior: If custom auth fails or returns
continue_processing=True, the gateway falls back to standard JWT/API token validation. This ensures robustness.Error Handling: Plugin errors are logged but don't fail requests. Standard authentication continues if plugin fails.
Priority: Auth plugins should run early (low priority numbers, e.g., 10-20) to ensure they execute before other plugins.
Credential Storage: Never log or expose credentials. Use secure storage for API key mappings.
Rate Limiting: Combine with rate_limiter plugin to prevent brute force attacks on custom auth endpoints.
Audit Logging: Use HTTP_POST_REQUEST for comprehensive audit logging of authentication attempts.
Testing
Example test for custom auth plugin:
References