-
Notifications
You must be signed in to change notification settings - Fork 4
Fix WebSocket authentication to use configurable authentication header #68
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?
Changes from 4 commits
97009a3
25efe46
2ac855e
f07940d
5a6c25a
adfa64d
a092617
75c5b93
db677e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| from core.security_headers_middleware import SecurityHeadersMiddleware | ||
| from core.otel_config import setup_opentelemetry | ||
| from core.utils import sanitize_for_logging | ||
| from core.auth import get_user_from_header | ||
|
|
||
| # Import from infrastructure | ||
| from infrastructure.app_factory import app_factory | ||
|
|
@@ -186,30 +187,48 @@ | |
| 2. Reverse proxy intercepts WebSocket handshake (HTTP Upgrade request) | ||
| 3. Reverse proxy delegates to authentication service | ||
| 4. Auth service validates JWT/session from cookies or headers | ||
| 5. If valid: Auth service returns X-Authenticated-User header | ||
| 6. Reverse proxy forwards connection to this app with X-Authenticated-User header | ||
| 5. If valid: Auth service returns X-User-Email header | ||
| 6. Reverse proxy forwards connection to this app with X-User-Email header | ||
| 7. This app trusts the header (already validated by auth service) | ||
| SECURITY REQUIREMENTS: | ||
| - This app MUST ONLY be accessible via reverse proxy | ||
| - Direct public access to this app bypasses authentication | ||
| - Use network isolation to prevent direct access | ||
| - The /login endpoint lives in the separate auth service | ||
| - Reverse proxy MUST strip client-provided X-User-Email headers before adding its own | ||
| (otherwise attackers can inject headers: X-User-Email: [email protected]) | ||
| DEVELOPMENT vs PRODUCTION: | ||
| - Production: Extracts user from X-Authenticated-User header (set by reverse proxy) | ||
| - Production: Extracts user from X-User-Email header (set by reverse proxy) | ||
| - Development: Falls back to 'user' query parameter (INSECURE, local only) | ||
| See docs/security_architecture.md for complete architecture details. | ||
| """ | ||
| await websocket.accept() | ||
|
|
||
| # Basic auth: derive user from query parameters or use test user | ||
| user_email = websocket.query_params.get('user') | ||
| # Extract user email using the same authentication flow as HTTP requests | ||
| # Priority: 1) X-User-Email header (production), 2) query param (dev), 3) test user (dev fallback) | ||
| config_manager = app_factory.get_config_manager() | ||
| user_email = None | ||
|
|
||
| # Check X-User-Email header first (consistent with AuthMiddleware) | ||
| x_email_header = websocket.headers.get('X-User-Email') | ||
| if x_email_header: | ||
|
|
||
| user_email = get_user_from_header(x_email_header) | ||
| logger.info(f"WebSocket authenticated via X-User-Email header: {sanitize_for_logging(user_email)}") | ||
|
|
||
| # Fallback to query parameter for backward compatibility (development/testing) | ||
| if not user_email: | ||
| user_email = websocket.query_params.get('user') | ||
| if user_email: | ||
| logger.info(f"WebSocket authenticated via query parameter: {sanitize_for_logging(user_email)}") | ||
|
||
|
|
||
| # Final fallback to test user (development mode only) | ||
| if not user_email: | ||
| # Fallback to test user or require auth | ||
| config_manager = app_factory.get_config_manager() | ||
| user_email = config_manager.app_settings.test_user or '[email protected]' | ||
| logger.info(f"WebSocket using fallback test user: {sanitize_for_logging(user_email)}") | ||
|
|
||
| session_id = uuid4() | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,136 @@ | ||||
| """ | ||||
| Integration test demonstrating the fix for the access denied issue. | ||||
| This test simulates the exact scenario from the issue: | ||||
| - A file belongs to user '[email protected]' | ||||
| - WebSocket connection is authenticated as '[email protected]' via X-User-Email header | ||||
| - Attaching the file should succeed (not fail with "Access denied") | ||||
| """ | ||||
|
|
||||
| import pytest | ||||
| import base64 | ||||
| import uuid | ||||
|
||||
| import uuid |
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.
Reordered imports in db677e0 - base64 is now imported first, removing the unused import ordering issue.
Fixed
Show fixed
Hide fixed
Outdated
Copilot
AI
Nov 10, 2025
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.
Variable file_key is not used.
| file_key = f"users/{actual_user}/generated/1234567890_mypdf.pdf" |
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.
Removed the unused file_key variable in db677e0.
Uh oh!
There was an error while loading. Please reload this page.