django-ninja icon indicating copy to clipboard operation
django-ninja copied to clipboard

Is the JWT tool or integrate in progress?

Open wu-clan opened this issue 3 years ago • 16 comments

This is very important for the separation of front-end and back-end, and it is also positive to provide everyone with a unified verification method

wu-clan avatar Dec 23 '21 08:12 wu-clan

Hi @wjhgg yes the planning is in progress...

could you list features that are important for you ?

  • simple tokens
  • refresh tokens
  • expirations
  • rotations
  • blacklisting

vitalik avatar Dec 23 '21 08:12 vitalik

It is quite easy to implement a custom JWT authentication using PyJWT, I personally prefer that Django-ninja is quite lightweight and any auth packages should be separated as third party packages (even if they have support by the community).

robinsandstrom avatar Jan 07 '22 10:01 robinsandstrom

It is quite easy to implement a custom JWT authentication using PyJWT, I personally prefer that Django-ninja is quite lightweight and any auth packages should be separated as third party packages (even if they have support by the community).

Integration is important and can be used more friendly

wu-clan avatar Jan 09 '22 17:01 wu-clan

@vitalik I found django-ninja-jwt but it's integrated in django-ninja-extra I don't think this integration is very comfortable,it is based on class decorators

wu-clan avatar Jan 09 '22 17:01 wu-clan

  • refresh tokens
  • expirations

refresh tokens expirations

TimurKady avatar Jan 11 '22 12:01 TimurKady

I don't know if it helps, but i have used the restframework-simplejwt library with ninja with no issues, see my reply here - https://github.com/vitalik/django-ninja/issues/45#issuecomment-1049829818

bencleary avatar Feb 25 '22 09:02 bencleary

I tried django-ninja-jwt but even the basic example is not working and raising issues, it's a pity because it looks quite rich in features. I would love to see that implemented to the core, anything we can do? I'm more than happy to chip in to help the project.

areski avatar Jun 08 '22 13:06 areski

I've just implemented my own auth package that plugs into Ninja, its pretty simple to use your chosen JWT library and wrap it up with Ninja. I have done it with the restframework-simplejwt as stated above and grown that into a viable package i use in project now. I can't really speak for @vitalik but personally as it depends on another third party package (ninja extra) i wouldn't bring it into core.

bencleary avatar Jun 08 '22 13:06 bencleary

Thanks I agree with you, as per @vitalik earlier comment, I thought there was a plan to build this in the core, certainly not porting something that depends of ninja extra but maybe an adapted version that would work without.

areski avatar Jun 08 '22 13:06 areski

@areski The issues you had was because of the recent release of django-ninja. And that has been fixed.

The reason why django-ninja-jwt depends on django-ninja-extra is not just about the class-based decorator. Porting a library designed after DRF is not easy. Django-ninja doesn't have some of core DRF classes that aids the process. Plus I still have to track changes coming from djangorestframework-simplejwt

Nevertheless, I want something that can be maintained by the community and reduce project dependencies. If @areski or anyone here wants to move django-ninja-jwt to core, I am willing to help.

eadwinCode avatar Jun 10 '22 05:06 eadwinCode

Thanks @eadwinCode your fix on django-ninja-extra just did the trick, thanks a lot for addressing this so quickly! Your project/port is very complete, and very well documented, Kudos!

It might also be interesting to have django-ninja-jwt as an addons without depending of django-ninja-extra. Not sure what would be the best direction to take. It might be easier if there is needs to keep it in sync with djangorestframework-simplejwt

areski avatar Jun 10 '22 14:06 areski

I tried django-ninja-jwt, but didn't work for me for same reasons mentioned above - django-ninja-extra dependency.

Below set up worked for me, uses djangorestframework-simplejwt

I ended up relying on djangorestframework-simplejwt dependency for some builtin functionality. could be swapped for pyjwt with some effort.


from typing import Optional, Any
from datetime import timedelta, datetime
from ninja.security import HttpBearer

from rest_framework_simplejwt.authentication import JWTAuthentication
from ninja.security import HttpBasicAuth
from django.http import HttpRequest, HttpResponse
from django.contrib.auth import get_user_model
from ninja.security.base import AuthBase
from abc import ABC, abstractmethod


# local
from core import settings


class JWTCookieAuth(JWTAuthentication):
    """
    An authentication plugin that authenticates requests through a JSON web
    token provided in a request header.
    """
    def authenticate(self, request):
 
        raw_token = self.get_raw_token(request)
        if raw_token is None:
            return None

        validated_token = self.get_validated_token(raw_token)

        return self.get_user(validated_token), validated_token

    def get_raw_token(self, request):
        """
        Extracts an unvalidated JSON web token from the given "Authorization"
        header value.
        """ 
        return request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE']) or None

def authenticate(request):
    authenticator = JWTCookieAuth()
    try:
        auth_data = authenticator.authenticate(request)
    except Exception as e:
        # raise e
        return None
    
    if auth_data is not None:
        user, token = auth_data
        return user
    return None

class OAuth2AuthBase(AuthBase, ABC):
    openapi_type: str = "oauth2"

class IsAuthenticated(OAuth2AuthBase, ABC):
    # openapi_scheme: str = "bearer"
    header: str = "Authorization"
    authorizationUrl: str = '/api/login'

    def __call__(self, request: HttpRequest) -> Optional[Any]:
        return authenticate(request)

add it to api:

api = NinjaAPI(csrf=False, auth=IsAuthenticated())

unfortunately, I couldn't get the authorization url to work on the swagger UI. So for testing, just have to use the login route to set the JWT cookie. A fix for this would be great!

mandarup avatar Jul 16 '22 16:07 mandarup

@vitalik hey! Is there any updates on this? thank you

magedhelmy1 avatar Mar 31 '23 14:03 magedhelmy1

from rest_framework_simplejwt.authentication import JWTAuthentication
from ninja.security import HttpBearer

class GlobalJWTAuth(HttpBearer):
    openapi_scheme: str = "bearer"
    header: str = "Authorization"
    def __call__(self, request):
        user_and_token = JWTAuthentication().authenticate(request)
        if user_and_token is not None:
            user = user_and_token[0]
            request.user = user
            return user
        else:
            return None
    def authenticate(self, request: HttpRequest, token: str) -> Optional[Any]:
        pass  


api = NinjaAPI(auth=GlobalJWTAuth())

This will show 'Authorize' in doc page. I guess for your code

class OAuth2AuthBase(AuthBase, ABC):
    openapi_type: str = "oauth2"

instead of inherit from AuthBase, inherit HttpBearer will work.

swuecho avatar Oct 05 '23 12:10 swuecho

class GlobalJWTAuth(HttpBearer):
    def authenticate(self, request: HttpRequest, token: str) -> Optional[Any]:
        user_and_token = JWTAuthentication().authenticate(request)
        if user_and_token is not None:
            user = user_and_token[0]
            request.user = user
            request.claims = user_and_token[1]
            return user
        else:
            return None

better version

swuecho avatar Oct 05 '23 12:10 swuecho

class GlobalJWTAuth(HttpBearer):
    def authenticate(self, request: HttpRequest, token: str) -> Optional[Any]:
        user_and_token = JWTAuthentication().authenticate(request)
        if user_and_token is not None:
            user = user_and_token[0]
            request.user = user
            request.claims = user_and_token[1]
            return user
        else:
            return None

better version

Worked perfectly for me :) thank you!

@vitalik if you are not currently working on this integration I am happy to put some work into it. This is a very important feature for a few projects of mine!

JPGarCar avatar Dec 07 '23 20:12 JPGarCar