Skip to content

Commit 393cba8

Browse files
authored
Merge pull request #1305 from DalgoT4D/enhancement/websockets-use-cookie-for-authentication
Enhancement/websockets use cookie for authentication
2 parents c078147 + 61a2c35 commit 393cba8

2 files changed

Lines changed: 34 additions & 6 deletions

File tree

ddpui/websockets/__init__.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import json
2+
from http.cookies import SimpleCookie
23
from channels.generic.websocket import WebsocketConsumer
34
from rest_framework_simplejwt.tokens import AccessToken
45
from urllib.parse import parse_qs
56
from django.contrib.auth.models import User
67

7-
from ddpui.websockets.schemas import WebsocketResponse
8+
from ddpui.websockets.schemas import WebsocketResponse, WebsocketCloseCodes
89
from ddpui.models.org_user import OrgUser
910
from ddpui.utils.custom_logger import CustomLogger
1011

@@ -56,14 +57,34 @@ def authenticate_user(self, token: str, orgslug: str):
5657
def respond(self, message: WebsocketResponse):
5758
self.send(text_data=json.dumps(message.model_dump()))
5859

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+
5969
def connect(self):
6070
query_string = parse_qs(self.scope["query_string"].decode())
61-
token = query_string.get("token", [None])[0]
6271
orgslug = query_string.get("orgslug", [None])[0]
6372

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")
6682
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)
6788
else:
68-
logger.info("Authentication failed, closing connection")
69-
self.close()
89+
logger.info("User authenticated via cookie, establishing connection")
90+
self.accept()

ddpui/websockets/schemas.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
from enum import Enum
44

55

6+
class WebsocketCloseCodes:
7+
"""Custom WebSocket close codes (4000-4999 range is for application use)"""
8+
9+
NO_TOKEN = 4001
10+
INVALID_TOKEN = 4003
11+
12+
613
class WebsocketResponseStatus(str, Enum):
714
SUCCESS = "success"
815
ERROR = "error"

0 commit comments

Comments
 (0)