Code Monkey home page Code Monkey logo

Comments (15)

bencleary avatar bencleary commented on May 14, 2024 9

I have been able to integrate it easily in a few places, initially i followed the GlobalAuth example in the docs here by extending the HttpBearer class like so and using the JWTAuthentication class from the simplejwt library, like so:

from typing import Any, Optional
from django.http import HttpRequest
from ninja.security import HttpBearer
from rest_framework_simplejwt.authentication import JWTAuthentication


class JWTAuthRequired(HttpBearer):
    def authenticate(self, request: HttpRequest, token: str) -> Optional[Any]:
        jwt_authenticator = JWTAuthentication()
        try:
            response = jwt_authenticator.authenticate(request)
            if response is not None:
                return True # 200 OK
            return False # 401
        except Exception:
            # Any exception we want it to return False i.e 401
            return False

As for the creation of tokens, its a fairly simple approach also. I have 2 schemas one for the auth and one for the repsonse:

from ninja import Schema


class AuthSchema(Schema):
    username: str
    password: str


class JWTPairSchema(Schema):
    refresh: str
    access: str

These are then used in my authentication router like so:

from ninja import Router
from auth.api.schema import AuthSchema, JWTPairSchema
from django.contrib.auth import authenticate
from rest_framework_simplejwt.tokens import RefreshToken


router = Router()


@router.post('/login', response=JWTPairSchema, auth=None)
def login(request, auth: AuthSchema):
    user = authenticate(**auth.dict())
    if user is not None:
        refresh = RefreshToken.for_user(user)

    return {
        'refresh': str(refresh),
        'access': str(refresh.access_token),
    }

I have reused all my authentication logic from DRF in django-ninja and it works well. Hope that helps someone else.

from django-ninja.

bondarev avatar bondarev commented on May 14, 2024 7

Hi @vitalik!

Any news?

from django-ninja.

vitalik avatar vitalik commented on May 14, 2024 2

it is not out of the box, but (as it actually requested few more time) I will try to pull out some example into separate repository as a plugin

from django-ninja.

ognjenk avatar ognjenk commented on May 14, 2024 1

@VetalM84
Hey, I'm the author of that comment in the reddit thread. As far as I can tell you managed to get it to work now, but if you have any more problems let me know and I'll try to help.
I am building a pretty large enterprise app and this auth method has been happily working for me for a year now.

from django-ninja.

ognjenk avatar ognjenk commented on May 14, 2024 1

Hey,
if you have set up your sign in endpoint to send urlencoded data (and set up parameters as form data with something like username: str = Form(...), password: str = Form(...)
than you would have to set up your test something like this:

data = f"username={username}&password={password}"
response = client.post(f"{API_ROOT}/auth/sign_in", data=data, content_type="application/x-www-form-urlencoded")

I have since switched my sign in endpoint to expect the usual json post request where I'm sending username and password in json, so then the test would be like yours, only content_type would be = "application/json"

from django-ninja.

 avatar commented on May 14, 2024

I was having the same question... Since JWT is just another form of bearer authentication what about this? I'm using python-jose for the JWT.

from jose import jwt

[...]
class JWT(HttpBearer):
    def authenticate(self, request, token):
        try:
            jwt.decode(token, 'key')
            return True
        except:
            return False
[...]

@router.get('/', auth=JWT())

This works just fine.

EDIT: Somehow it only worked when using Django's development server and didn't translate to the productive environment. God knows why but it's already too late for me to think properly. I fiddled around with it even more, another solution at least within a development setting is:

def validate_jwt(request):
    token = request.META['HTTP_AUTHORIZATION'].split(" ")[1]
    try:
        jwt.decode(token, 'key')
        return True
    except:
        return False
[...]
@router.get('/', auth=validate_jwt)

But this also sucks. I will look into it tomorrow.

EDIT 2:
Seems like I don't get the standard AuthBearer way to work either (following the tutorial).

from django-ninja.

 avatar commented on May 14, 2024

I don't know if there is a bug or not, but I could make it work with the ApiKey(APIKeyHeader) method in my production environment. I contaminated every second line of my script with loggers, and for the AuthBearer method, it seems like the authentication function is never called. Has anyone experienced a similar thing?

from django-ninja.

vitalik avatar vitalik commented on May 14, 2024

@fantasticle
nope, there is a chance you have some typo :)
without a code could not help

from django-ninja.

VetalM84 avatar VetalM84 commented on May 14, 2024

Hi, found this post to implement auth https://www.reddit.com/r/django/comments/r2tti8/django_ninja_auth_example/
But that did not work for me.

from django-ninja.

epicserve avatar epicserve commented on May 14, 2024

@VetalM84,

@bencleary's comment above worked for me. However, it's less than ideal and isn't something I want to go to production with since it installs the Django Rest Framework because it is a dependency of djangorestframework-simplejwt.

I'm curious, what didn't work in the example you linked to? I think the solution you linked to since it uses PyJWT.

P.S. your link didn't work when I clicked on it. I had to copy and paste it.

For the project I'm working on, I'm also considering going back to just using django_auth and coming up with a system for the front-end to use csrf tokens.

from django-ninja.

VetalM84 avatar VetalM84 commented on May 14, 2024

I'm curious, what didn't work in the example you linked to?

I've got the working one based on that!

def create_token(username):
    jwt_signing_key = getattr(settings, "JWT_SIGNING_KEY", None)
    jwt_access_expire = getattr(settings, "JWT_ACCESS_EXPIRY", 60)
    payload = {"username": username}
    access_expire = datetime.datetime.now(tz=timezone.utc) + datetime.timedelta(
        minutes=jwt_access_expire
    )
    payload.update({"exp": access_expire})
    token = jwt.encode(payload=payload, key=jwt_signing_key, algorithm="HS256")
    return token


class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        jwt_signing_key = getattr(settings, "JWT_SIGNING_KEY", None)
        try:
            payload = jwt.decode(token, key=jwt_signing_key, algorithms=["HS256"])
        except Exception as e:
            return {"error": e}
        username: str = payload.get("username", None)
        return username


@api.post("/sign_in", auth=None)
def sign_in(request, username: str = Form(...), password: str = Form(...)):
    user_model = get_object_or_404(User, username=username)

    passwords_match = check_password(password, user_model.password)
    if not passwords_match:
        raise ValidationError([{"error": "Wrong password"}])

    token = create_token(user_model.username)
    return {"token": token}

from django-ninja.

VetalM84 avatar VetalM84 commented on May 14, 2024

@ognjenk
How I can unittest sign in endpoint?

    def test_sign_in(self):
        """Test Sing in."""
        data = {
            "username": "TestUserName",
            "password": "test"
        }
        response = self.client.post(
            path="/api/sign_in",
            data=data,
            content_type="application/x-www-form-urlencoded",
            follow=True
        )
        self.assertEqual(response.status_code, 200)

This code returns me AssertionError: 422 != 200

from django-ninja.

VetalM84 avatar VetalM84 commented on May 14, 2024

@ognjenk
Thank you. Although I've found one more easy solution:

...
username: str = Form(...), password: str = Form(...)
...
    def test_sign_in(self):
        """Test Sing in."""
        data = {"username": "TestUserName", "password": "test"}
        response = self.client.post(
            path="/api/sign_in",
            data=data,
            # or like this
            # data=f"username=TestUserName&password=test",
            # content_type="application/x-www-form-urlencoded",
        )
        self.assertEqual(response.status_code, 200)

I just had to remove this line content_type="application/x-www-form-urlencoded" and it worked like I did a json request.

I wonder do you user roles (Django user groups) to split access to different endpoints?

from django-ninja.

ognjenk avatar ognjenk commented on May 14, 2024

No, we have rolled out our own solution for endpoint authorization.
We cache it in Redis per user and have created a custom decorator which we use for endpoints that need the check.

from django-ninja.

bendowlingtech avatar bendowlingtech commented on May 14, 2024

Is it secure to have a static "JWT_SIGNING_KEY" like that?

from django-ninja.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.