|
1 | 1 | import json |
| 2 | +from http.cookies import SimpleCookie |
2 | 3 | from channels.generic.websocket import WebsocketConsumer |
3 | 4 | from rest_framework_simplejwt.tokens import AccessToken |
4 | 5 | from urllib.parse import parse_qs |
5 | 6 | from django.contrib.auth.models import User |
6 | 7 |
|
7 | | -from ddpui.websockets.schemas import WebsocketResponse |
| 8 | +from ddpui.websockets.schemas import WebsocketResponse, WebsocketCloseCodes |
8 | 9 | from ddpui.models.org_user import OrgUser |
9 | 10 | from ddpui.utils.custom_logger import CustomLogger |
10 | 11 |
|
@@ -56,14 +57,34 @@ def authenticate_user(self, token: str, orgslug: str): |
56 | 57 | def respond(self, message: WebsocketResponse): |
57 | 58 | self.send(text_data=json.dumps(message.model_dump())) |
58 | 59 |
|
| 60 | + def _get_cookie(self, name: str) -> str | None: |
| 61 | + """Extract a cookie value from the WebSocket scope headers.""" |
| 62 | + for header_name, header_value in self.scope.get("headers", []): |
| 63 | + if header_name == b"cookie": |
| 64 | + cookie = SimpleCookie(header_value.decode()) |
| 65 | + if name in cookie: |
| 66 | + return cookie[name].value |
| 67 | + return None |
| 68 | + |
59 | 69 | def connect(self): |
60 | 70 | query_string = parse_qs(self.scope["query_string"].decode()) |
61 | | - token = query_string.get("token", [None])[0] |
62 | 71 | orgslug = query_string.get("orgslug", [None])[0] |
63 | 72 |
|
64 | | - if self.authenticate_user(token, orgslug): |
65 | | - logger.info("User authenticated, establishing connection") |
| 73 | + # Read JWT from the access_token httpOnly cookie (webapp_v2) |
| 74 | + token = self._get_cookie("access_token") |
| 75 | + |
| 76 | + # TODO: remove this fallback once webapp_v1 is fully deprecated |
| 77 | + if not token: |
| 78 | + token = query_string.get("token", [None])[0] |
| 79 | + |
| 80 | + if not token: |
| 81 | + logger.info("No access_token cookie found, closing connection") |
66 | 82 | self.accept() |
| 83 | + self.close(code=WebsocketCloseCodes.NO_TOKEN) |
| 84 | + elif not self.authenticate_user(token, orgslug): |
| 85 | + logger.info("Authentication failed (invalid/expired token), closing connection") |
| 86 | + self.accept() |
| 87 | + self.close(code=WebsocketCloseCodes.INVALID_TOKEN) |
67 | 88 | else: |
68 | | - logger.info("Authentication failed, closing connection") |
69 | | - self.close() |
| 89 | + logger.info("User authenticated via cookie, establishing connection") |
| 90 | + self.accept() |
0 commit comments