|
- import jwt
- from fastapi import WebSocket, WebSocketDisconnect, status
- from loguru import logger
-
- from .manager import AuthManager
- from .models import User
-
-
- class WebSocketAuthHandler:
- """
- Helper class for authenticating WebSocket connections.
- """
-
- def __init__(self, auth_manager: AuthManager):
- self.auth_manager = auth_manager
-
- async def authenticate(self, websocket: WebSocket) -> tuple[bool, User | None]:
- """
- Authenticate a WebSocket connection.
- Returns (success, user) tuple.
- """
- if self.auth_manager.config.type == "none":
- # No authentication required
- return True, User(id="guestuser@gmail.com", name="Default User", provider="none")
-
- try:
- # Extract token from query params or headers query_params)
- token = None
- if "token" in websocket.query_params:
- token = websocket.query_params["token"]
- elif "authorization" in websocket.headers:
- auth_header = websocket.headers["authorization"]
- if auth_header.startswith("Bearer "):
- token = auth_header.replace("Bearer ", "")
-
- if not token:
- logger.warning("No token found for WebSocket connection")
- return False, None
-
- # Validate token
- if not self.auth_manager.config.jwt_secret:
- # Development mode with no JWT secret
- return True, User(id="guestuser@gmail.com", name="Default User", provider="none")
-
- try:
- # Decode and validate JWT
- if not self.auth_manager.config.jwt_secret:
- logger.warning("Invalid token for WebSocket connection")
- return False, None
- payload = jwt.decode(token, self.auth_manager.config.jwt_secret, algorithms=["HS256"])
-
- # Create User object from token payload
- user = User(
- id=payload.get("sub"),
- name=payload.get("name", "Unknown User"),
- email=payload.get("email"),
- provider=payload.get("provider", "jwt"),
- roles=payload.get("roles", ["user"]),
- )
-
- return True, user
-
- except jwt.ExpiredSignatureError:
- logger.warning("Expired token for WebSocket connection")
- return False, None
- except jwt.InvalidTokenError:
- logger.warning("Invalid token for WebSocket connection")
- return False, None
-
- except Exception as e:
- logger.error(f"WebSocket auth error: {str(e)}")
- return False, None
-
- async def on_connect(self, websocket: WebSocket) -> User | None:
- """
- Handle WebSocket connection with authentication.
- Returns authenticated user if successful, otherwise closes the connection.
- """
- success, user = await self.authenticate(websocket)
-
- if not success:
- # Authentication failed, close the connection
- await websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason="Authentication failed")
- raise WebSocketDisconnect(code=status.WS_1008_POLICY_VIOLATION)
-
- # Authentication successful, return the user
- return user
|