I am making an IOS and Android app that will communicate with Django real time through websockets but I am at a complete loss on how to do user/password authentication when opening a websockets stream. I built the chat demo, but of course the problem is that there is no authentication in it at all. I noticed if I log into the admin panel with my user, then scope[‘user’] will get populated in my AuthMiddleware / Consumer, but since I will be creating a websocket connection from a native mobile app I need a way to pass the user/password without using a Django login form.
I suppose I could pass the username/password with a URL query but this would be highly insecure and the password may be logged in cleartext if done in this matter. I also tried the http basic auth method http://user:pass@127.0.0.1:8000/ws/chat/lobby but these creds don’t seem to make it into the session at all.
Surely someone has done this, can anyone provide example code? I see a lot of examples where the user is provided in query text and in the middleware there is a lookup for the token that the user owns but I don’t see how this could ever be secure without providing a password somewhere. I’m coming from crossbar WAMP server and there were several methods to authenticate with that.
What am I missing? Thanks for the help.
Here is my current code:
asgi.py:
routes = chat.routing.websocket_urlpatterns
application = ProtocolTypeRouter(
{
“http”: django_asgi_app,
“websocket”: AllowedHostsOriginValidator(
DualAuthMiddleware(URLRouter(routes))
),
}
)
DualAuthMiddleware:
class DualAuthMiddleware:
“”"
Custom middleware (insecure) that takes user IDs from the query string.
“”"def __init__(self, app): # Store the ASGI application we were passed self.app = app async def __call__(self, scope, receive, send): # Look up user from query string (you should also do things like # checking if it is a valid user ID, or if scope["user"] is already # populated). print(chat.routing.websocket_urlpatterns) if scope['path'].startswith("/ws/chat"): print("REGULAR AUTH") return await AuthMiddlewareStack(self.app)(scope, receive, send) else: print("NO AUTH") return await AuthMiddlewareStack(self.app)(scope, receive, send) return await self.app(scope, receive, send)
ChatConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
user = self.scope[“user”]
print(“HEREHEREHERE2”,user,user.is_authenticated,self.scope)
if user.is_authenticated:
#else:
# await self.close()
print(“HEREHEREHERE”,user,user.is_authenticated)
self.room_name = self.scope[“url_route”][“kwargs”][“room_name”]
self.room_group_name = “chat_%s” % self.room_name# Join room group await self.channel_layer.group_add(self.room_group_name, self.channel_name) await self.accept() else: await self.close() async def disconnect(self, close_code): # Leave room group try: await self.channel_layer.group_discard(self.room_group_name, self.channel_name) except AttributeError: pass # Receive message from WebSocket async def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json["message"] # Send message to room group await self.channel_layer.group_send( self.room_group_name, {"type": "chat_message", "message": message} ) # Receive message from room group async def chat_message(self, event): message = event["message"] # Send message to WebSocket await self.send(text_data=json.dumps({"message": message}))
routing.py:
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]
room.html:
Chat Room
{{ room_name|json_script:"room-name" }} const roomName = JSON.parse(document.getElementById('room-name').textContent);const chatSocket = new WebSocket( 'ws://' + 'testuser:testpass@127.0.0.1:9000' + '/ws/chat/' + roomName + '/' ); chatSocket.onmessage = function(e) { const data = JSON.parse(e.data); document.querySelector('#chat-log').value += (data.message + '\n'); }; chatSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); }; document.querySelector('#chat-message-input').focus(); document.querySelector('#chat-message-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#chat-message-submit').click(); } }; document.querySelector('#chat-message-submit').onclick = function(e) { const messageInputDom = document.querySelector('#chat-message-input'); const message = messageInputDom.value; chatSocket.send(JSON.stringify({ 'message': message })); messageInputDom.value = ''; /*const usernameInputDom = document.querySelector("#username-input"); const passwordInputDom = document.querySelector("#password-input"); document.querySelector('#chat-log').value = (usernameInputDom.value + '\n'); document.querySelector('#chat-log').value += (passwordInputDom.value + '\n');*/ }; </script>