Closed MrVhek closed 4 years ago
Strange. I wonder if it's an issue with the channels AuthMiddlewareStack. The user object should be provided in the asgi consumer scope
field. You can see the user value is extracted from the scope in django_eventstream/consumers.py.
Maybe you can log the scope value in your code or by modifying django_eventstream, to confirm whether it has a user field.
You seem to be right about that, here what I've done :
request = AsgiRequest(self.scope, body)
from logging import info
info("Asgi : ")
info(self.scope['user'])
gm = GripMiddleware()
gm.process_request(request)
from logging import info
info("Asgi + GM : ")
info(self.scope['user'])
if 'user' in self.scope:
request.user = self.scope['user']
And it logged this :
HTTP b'GET' request for ['127.0.0.1', 51315]
Asgi :
AnonymousUser
Asgi + GM :
AnonymousUser
HTTP 200 response started for ['127.0.0.1', 51315]
None
So yes, ASGI request doesn't seems to have anything... Here the self.scope values, I could maybe retrieve the user via the token but it is really ugly :
{'type': 'http', 'http_version': '1.1', 'method': 'GET', 'path': '/v1/gateways-events/gateway/1/', 'root_path': '', 'scheme': 'http', 'query_string': b'', 'headers': [(b'authorization', b'Bearer QmEvB9PpDnoXkXnrhN4SdxH5gOM66c'), (b'user-agent', b'PostmanRuntime/7.19.0'), (b'accept', b'*/*'), (b'cache-control', b'no-cache'), (b'postman-token', b'ce5e5e9f-24c5-463d-b2bc-8456da33fcd6'), (b'host', b'127.0.0.1:8000'), (b'content-type', b'multipart/form-data; boundary=--------------------------721020527187888896297434'), (b'accept-encoding', b'gzip, deflate'), (b'content-length', b'278'), (b'connection', b'keep-alive')], 'client': ['127.0.0.1', 51336], 'server': ['127.0.0.1', 8000], 'path_remaining': '', 'url_route': {'args': (), 'kwargs': {'obj_id': '1', 'format-channels': ['gateway-{obj_id}']}}, 'cookies': {}, 'session': <django.utils.functional.LazyObject object at 0x000002115BC5B668>, 'user': <channels.auth.UserLazyObject object at 0x000002115BC5B780>}
Here my middlewares, in case you think of a wrong order :
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware',
'django_grip.GripMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Ok, so I've found the solution, as you stated the AuthMiddlewareStack doesn't support Oauth2 nor token authentication, so I've wrapped the middleware in my own to have the user :
from channels.auth import AuthMiddlewareStack
from oauth2_provider.models import AccessToken
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
class TokenAuthMiddleware:
"""
Token OAuth2 authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
headers = dict(scope['headers'])
if b'authorization' in headers:
try:
token_name, token_key = headers[b'authorization'].decode().split()
if token_name == 'Bearer'or token_name == 'Token':
token = AccessToken.objects.get(token=token_key)
scope['user'] = token.user
close_old_connections()
except AccessToken.DoesNotExist:
scope['user'] = AnonymousUser()
return self.inner(scope)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
Maybe you should add a few line in documentation for people wondering about this problem.
Inspired by : https://gist.github.com/rluts/22e05ed8f53f97bdd02eafdf38f3d60a
Glad to hear you got it working! And thanks for the tip. I've added a note to the readme.
Hello, I've an issue making authentication work in the channels. I made a fairly simple channel manager :
The problem is, I'm using Oauth2 for authentication, and the user given in the can_read_channel is always None. In my views, I get the user with request.user without problems by passing the token in the headers but not in the channel manager.
Do I need some extra step to get the user ? I've followed the tutorial given by the readme so I have :
In a routing.py file
In the main routing.py file...
Thanks in advance for your help !