django-ninja
django-ninja copied to clipboard
Is the JWT tool or integrate in progress?
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
Hi @wjhgg yes the planning is in progress...
could you list features that are important for you ?
- simple tokens
- refresh tokens
- expirations
- rotations
- blacklisting
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).
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
@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
- refresh tokens
- expirations
refresh tokens expirations
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
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.
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.
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 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.
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
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!
@vitalik hey! Is there any updates on this? thank you
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.
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
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!