🐞 Bug Summary
The auth_middleware.py and rbac.py middleware components create their own database sessions using SessionLocal(), which leads to duplicate session creation when running alongside the observability middleware. This issue was identified during the code review of PR #3600 (which fixed duplicate sessions in observability middleware) and represents the same architectural problem in other middleware components.
Impact: Each request that passes through multiple middleware layers (observability + auth + RBAC) may create up to 4 separate database sessions instead of sharing a single request-scoped session, leading to:
🧩 Affected Component
Specific Files:
mcpgateway/middleware/auth_middleware.py - 3 instances of SessionLocal()
mcpgateway/middleware/rbac.py - 1 instance of SessionLocal()
🔁 Steps to Reproduce
-
Enable observability middleware with database tracing:
OBSERVABILITY_ENABLED=true
STRUCTURED_LOGGING_DATABASE_ENABLED=true
-
Configure authentication and RBAC:
AUTH_REQUIRED=true
RBAC_ENABLED=true
-
Make an authenticated API request that triggers all three middleware:
curl -H "Authorization: Bearer $TOKEN" http://localhost:4444/api/v1/servers
-
Observe database session creation in logs (with debug logging enabled):
-
Check for multiple session creation messages:
[OBSERVABILITY] DB session created: <id1>
[AUTH] DB session created: <id2> (if logging added)
[RBAC] DB session created: <id3> (if logging added)
[GET_DB] DB session created: <id4> (from route handler)
Expected: 1 session per request (shared via request.state.db)
Actual: Up to 4 sessions per request (one per middleware + route handler)
🤔 Expected Behavior
Following the pattern established in PR #3600 for observability middleware, all middleware should:
- Check for existing session in
request.state.db before creating a new one
- Reuse the shared session if available
- Create and store a session in
request.state.db if none exists
- Track ownership to ensure proper cleanup (only the creating middleware should close the session)
- Handle errors properly with rollback and connection invalidation
Example pattern (from observability middleware):
# Check if session already exists
db = getattr(request.state, "db", None)
if db is None:
# Create new session and mark ownership
db = SessionLocal()
request.state.db = db
session_owned_by_middleware = True
else:
# Reuse existing session
session_owned_by_middleware = False
📓 Logs / Error Output
Current behavior (inferred from code inspection):
# mcpgateway/middleware/auth_middleware.py
# Multiple locations where SessionLocal() is called directly:
# Location 1: Token validation
db = SessionLocal()
try:
# ... token validation logic ...
finally:
db.close()
# Location 2: User lookup
db = SessionLocal()
try:
# ... user lookup logic ...
finally:
db.close()
# Location 3: Permission checks
db = SessionLocal()
try:
# ... permission logic ...
finally:
db.close()
# mcpgateway/middleware/rbac.py
# Location: RBAC enforcement
db = SessionLocal()
try:
# ... RBAC logic ...
finally:
db.close()
No error logs currently, but this creates the same conditions that led to issue #3467:
- Connection pool exhaustion under load
- SQLite StaticPool segfaults with concurrent requests
- Suboptimal connection reuse in PostgreSQL/PgBouncer scenarios
🧠 Environment Info
| Key |
Value |
| Version or commit |
main (post PR #3600) |
| Runtime |
Python 3.11+ |
| Platform / OS |
All platforms |
| Container |
Docker, Podman, or native |
| Database |
SQLite (default) or PostgreSQL |
Affected Configurations:
- Any deployment with
AUTH_REQUIRED=true and OBSERVABILITY_ENABLED=true
- Any deployment with
RBAC_ENABLED=true and OBSERVABILITY_ENABLED=true
- High-concurrency deployments (>100 concurrent requests)
- PostgreSQL deployments with PgBouncer connection pooling
🧩 Additional Context
Relationship to PR 3600 and Issue #3467
This issue is a direct extension of the work done in PR #3600, which fixed duplicate session creation in observability middleware. The same architectural problem exists in auth and RBAC middleware but was marked as "Out of Scope (Future Work)" in the PR #3600 code review.
From PR #3600 Code Review (Section 6):
Locations:
mcpgateway/middleware/auth_middleware.py - 3 instances of SessionLocal()
mcpgateway/middleware/rbac.py - 1 instance of SessionLocal()
Issue: These middleware also create their own sessions, which could lead to additional duplicate sessions if they run alongside observability middleware.
Recommendation: Consider extending the session-sharing pattern to these middleware in a future PR.
Proposed Solution
Apply the same session-sharing pattern from PR #3600 to auth and RBAC middleware:
-
Update auth_middleware.py:
- Replace all 3
SessionLocal() calls with session reuse logic
- Add ownership tracking for proper cleanup
- Add connection invalidation in error paths
-
Update rbac.py:
- Replace the
SessionLocal() call with session reuse logic
- Add ownership tracking for proper cleanup
- Add connection invalidation in error paths
-
Ensure proper middleware ordering:
- Document the expected middleware execution order
- Ensure the first middleware in the chain creates the session
- Subsequent middleware reuse the shared session
-
Add comprehensive tests:
- Test session reuse across multiple middleware
- Test error handling in each middleware
- Integration test verifying single session per request with all middleware active
Performance Impact
Current state (with all middleware enabled):
Expected state (after fix):
- Total: 1 shared session per request
Improvement: 83% reduction in database sessions
Related Issues and PRs
Security Considerations
- Session sharing must maintain transaction isolation
- Each middleware must properly handle rollback on errors
- Connection invalidation is critical for broken connections (PostgreSQL/PgBouncer)
- No security implications from sharing read-only session state
Testing Strategy
-
Unit tests for each middleware:
- Test session creation when none exists
- Test session reuse when one exists
- Test ownership tracking
- Test error handling and cleanup
-
Integration tests:
- Test with all middleware enabled
- Verify single session per request
- Test concurrent requests (load testing)
- Test error scenarios across middleware boundaries
-
Performance tests:
- Measure session count reduction
- Measure connection pool pressure reduction
- Test under high concurrency (1000+ concurrent requests)
📋 Implementation Checklist
🔗 References
🐞 Bug Summary
The
auth_middleware.pyandrbac.pymiddleware components create their own database sessions usingSessionLocal(), which leads to duplicate session creation when running alongside the observability middleware. This issue was identified during the code review of PR #3600 (which fixed duplicate sessions in observability middleware) and represents the same architectural problem in other middleware components.Impact: Each request that passes through multiple middleware layers (observability + auth + RBAC) may create up to 4 separate database sessions instead of sharing a single request-scoped session, leading to:
🧩 Affected Component
mcpgateway- APImcpgateway- UI (admin panel)mcpgateway.wrapper- stdio wrapperSpecific Files:
mcpgateway/middleware/auth_middleware.py- 3 instances ofSessionLocal()mcpgateway/middleware/rbac.py- 1 instance ofSessionLocal()🔁 Steps to Reproduce
Enable observability middleware with database tracing:
Configure authentication and RBAC:
Make an authenticated API request that triggers all three middleware:
curl -H "Authorization: Bearer $TOKEN" http://localhost:4444/api/v1/serversObserve database session creation in logs (with debug logging enabled):
Check for multiple session creation messages:
[OBSERVABILITY] DB session created: <id1>[AUTH] DB session created: <id2>(if logging added)[RBAC] DB session created: <id3>(if logging added)[GET_DB] DB session created: <id4>(from route handler)Expected: 1 session per request (shared via
request.state.db)Actual: Up to 4 sessions per request (one per middleware + route handler)
🤔 Expected Behavior
Following the pattern established in PR #3600 for observability middleware, all middleware should:
request.state.dbbefore creating a new onerequest.state.dbif none existsExample pattern (from observability middleware):
📓 Logs / Error Output
Current behavior (inferred from code inspection):
No error logs currently, but this creates the same conditions that led to issue #3467:
🧠 Environment Info
main(post PR #3600)Python 3.11+All platformsDocker, Podman, or nativeSQLite (default) or PostgreSQLAffected Configurations:
AUTH_REQUIRED=trueandOBSERVABILITY_ENABLED=trueRBAC_ENABLED=trueandOBSERVABILITY_ENABLED=true🧩 Additional Context
Relationship to PR 3600 and Issue #3467
This issue is a direct extension of the work done in PR #3600, which fixed duplicate session creation in observability middleware. The same architectural problem exists in auth and RBAC middleware but was marked as "Out of Scope (Future Work)" in the PR #3600 code review.
From PR #3600 Code Review (Section 6):
Proposed Solution
Apply the same session-sharing pattern from PR #3600 to auth and RBAC middleware:
Update
auth_middleware.py:SessionLocal()calls with session reuse logicUpdate
rbac.py:SessionLocal()call with session reuse logicEnsure proper middleware ordering:
Add comprehensive tests:
Performance Impact
Current state (with all middleware enabled):
Expected state (after fix):
Improvement: 83% reduction in database sessions
Related Issues and PRs
Security Considerations
Testing Strategy
Unit tests for each middleware:
Integration tests:
Performance tests:
📋 Implementation Checklist
auth_middleware.pywith session sharing patternrbac.pywith session sharing pattern🔗 References
mcpgateway/middleware/observability_middleware.py(lines 100-120, 200-240)mcpgateway/main.py:get_db()(lines 2362-2442)