2👍
Django Channels already supports session authentication:
# In consumers.py
from channels import Channel, Group
from channels.sessions import channel_session
from channels.auth import channel_session_user, channel_session_user_from_http
# Connected to websocket.connect
@channel_session_user_from_http
def ws_add(message):
# Accept connection
message.reply_channel.send({"accept": True})
# Add them to the right group
Group("chat-%s" % message.user.username[0]).add(message.reply_channel)
# Connected to websocket.receive
@channel_session_user
def ws_message(message):
Group("chat-%s" % message.user.username[0]).send({
"text": message['text'],
})
# Connected to websocket.disconnect
@channel_session_user
def ws_disconnect(message):
Group("chat-%s" % message.user.username[0]).discard(message.reply_channel)
http://channels.readthedocs.io/en/stable/getting-started.html#authentication
1👍
Your function worked “as-is” for me. Before I walk through the details, there was a bug (now resolved) that was preventing sessions from being closed which may explain your other issue.
I use scarce quotes around “as-is” because I was using a class-based consumer so I had to add self
to the whole stack of decorators to test it explicitly:
class MyRouter(WebsocketDemultiplexer):
# WebsocketDemultiplexer calls raw_connect for websocket.connect
@channel_session_user_from_http
@login_required_websocket
def raw_connect(self, message, **kwargs):
...
After adding some debug messages to verify the sequence of execution:
>>> ws = create_connection("ws://localhost:8085")
# server logging
channel_session_user_from_http.run
login_required_websocket.run
user: AnonymousUser
# client logging
websocket._exceptions.WebSocketBadStatusException: Handshake status 403
>>> ws = create_connection("ws://localhost:8085", cookie='sessionid=43jxki76cdjl97b8krco0ze2lsqp6pcg')
# server logging
channel_session_user_from_http.run
login_required_websocket.run
user: admin
As you can see from my snippet, you need to call @channel_session_user_from_http
first. For function-based consumers, you can simplify this by including it in your decorator:
def login_required_websocket(func):
@channel_session_user_from_http
@functools.wraps(func)
def inner(message, *args, **kwargs):
...
On class-based consumers, this is handled internally (and in the right order) by setting http_user_and_session
:
class MyRouter(WebsocketDemultiplexer):
http_user_and_session = True
Here’s the full code for a self
-respecting decorator that would be used with it:
def login_required_websocket(func):
"""
If user is not logged in, close connection immediately.
"""
@functools.wraps(func)
def inner(self, message, *args, **kwargs):
if not message.user.is_authenticated():
message.reply_channel.send({'close': True})
return func(self, message, *args, **kwargs)
return inner
- Bad Request (400) using Django, Heroku, and Name.com
- Django south: changing field type in data migration
0👍
My suggestion is that you can require a session key or even better take the username/password input within your consumer method. Then call the authenticate method to check if the user exists. On valid user object return, you can broadcast the message or return and invalid login details message.
from django.contrib.auth import authenticate
@channel_session_user
def ws_message(message):
user = authenticate(username=message.username, password=message.password')
if user is not None:
Group("chat-%s" % message.user.username[0]).send({
"text": message['text'],
})
else:
# User is not authenticated so return an error message.
- Django- session cookies and sites on multiple ports
- How to prevent auto escape in Django templates?
- Override Django allauth signup "next" redirect URL