jpadilla / django-rest-framework-jwt

JSON Web Token Authentication support for Django REST Framework
http://jpadilla.github.io/django-rest-framework-jwt/
MIT License
3.19k stars 649 forks source link

Usage with Django Channels (Feature?) #279

Open ArthurianX opened 7 years ago

ArthurianX commented 7 years ago

Hello,

I was wondering how I could use rest-framework-jwt with channels.

Is there such an implementation in place, or do I somehow need to send the token on the .connect and check to see if the token is valid in a custom way ?

How would that work?

This might be a stupid question, if that's the case, sorry :), I started dabbling in Pyhon / Django et al only a few weeks back.

NadavK commented 7 years ago

Any info how to use use rest-framework-jwt with channels?

Azelphur commented 6 years ago

Hi folks Just for future googlers, I've had a go at implementing this myself, and so far it seems to work, but I haven't extensively tested it, so caveat emptor.

from channels import route, Group
from channels.handler import AsgiRequest
from channels.sessions import channel_session
from rest_framework_jwt.settings import api_settings
from django.contrib.auth import get_user_model
from django.conf import settings
from jwt.exceptions import ExpiredSignatureError
from django.contrib.auth.models import AnonymousUser
import functools

jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER

def user_from_token(token):
    try:
        payload = jwt_decode_handler(token)
    except ExpiredSignatureError:
        return AnonymousUser()
    username = jwt_get_username_from_payload(payload)
    User = get_user_model()
    return User.objects.get(**{User.USERNAME_FIELD: username})

def channel_session_jwt(func):
    @channel_session
    @functools.wraps(func)
    def inner(message, *args, **kwargs):
        if message.channel_session["token"] is None:
            message.user = AnonymousUser()
        else:
            message.user = user_from_token(message.channel_session["token"])
        return func(message, *args, **kwargs)
    return inner

def channel_session_user_from_jwt(func):
    @channel_session
    @functools.wraps(func)
    def inner(message, *args, **kwargs):
        if "method" not in message:
            message['method'] = "FAKE"
        request = AsgiRequest(message)
        token = request.GET.get("token", None)
        message.channel_session['token'] = token
        message.user = user_from_token(message.channel_session["token"])
        return func(message, *args, **kwargs)
    return inner

@channel_session_jwt
def message_handler(message):
    print('Message', message.user)

@channel_session_user_from_jwt
def connect_handler(message):
    message.reply_channel.send({"accept": True})
    print('Connect', message.user)

channel_routing = [
    route("websocket.connect", connect_handler),
    route("websocket.receive", message_handler)
]