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

Update request.auth instead of request.user

Open ianrodrigues opened this issue 4 years ago • 6 comments
trafficstars

I was wondering why did you decide to set request.auth instead of request.user during the authentication step here:

https://github.com/vitalik/django-ninja/blob/master/ninja/operation.py#L100

When I intuitively try to access request.user I always get AnonymousUser. I mean, I always saw that other libraries set request.user, and when necessary they set some other request attributes besides user.

Is that really intended? What is the reason for that?

Thanks for your work on this library, by the way. Nice job!

ianrodrigues avatar Feb 16 '21 01:02 ianrodrigues

Hi @ianrodrigues

Is that really intended? What is the reason for that? yes, at the moment it it intended

because for APIs your auth entity is not always a user, can be token, can be boolean, etc

if you use django-ninja as a backend for javascript client on same domain - you can use django_session auth , and then you will have request.user available as expected

but on the other hand, I will think maybe it makes sense to have request auth attribute configurable somehow...

vitalik avatar Feb 16 '21 08:02 vitalik

It makes sense. However, I do believe it should have that configurable somehow, too.

In my case, django-ninja is both a backend for a React SPA (authenticating using Bearer) and a CLI tool (authenticating using Personal Access Token).

Thanks for your fast response. You can mark this issue as a question and close it.

ianrodrigues avatar Feb 16 '21 12:02 ianrodrigues

probably implementation will be the following

class MyAuth(HttpBearer):
    request_attribute = 'user'   # !!!

    def authenticate(self, ...):
            return some_object

vitalik avatar Apr 02 '21 11:04 vitalik

I solved this issue of request.user not being available by adding a call to django's login method to my Ninja auth class:

from users.models import User
from ninja.security import APIKeyHeader, django_auth
from django.contrib.auth import login


class ApiKey(APIKeyHeader):
    param_name = 'X-API-Key'

    def authenticate(self, request, key):
        user = User.objects.filter(token__key=key).first()
        if user is not None:
            login(request, user, backend='django.contrib.auth.backends.ModelBackend')

        return user

The token stuff is my own implementation, but basically on the user = User.objects line, you want to find the user in the database given the key that was supplied.

I hope this helps anyone else facing this situation.

Fingel avatar Oct 29 '21 18:10 Fingel

I solved this issue of request.user not being available by adding a call to django's login method to my Ninja auth class:

from users.models import User
from ninja.security import APIKeyHeader, django_auth
from django.contrib.auth import login


class ApiKey(APIKeyHeader):
    param_name = 'X-API-Key'

    def authenticate(self, request, key):
        user = User.objects.filter(token__key=key).first()
        if user is not None:
            login(request, user, backend='django.contrib.auth.backends.ModelBackend')

        return user

The token stuff is my own implementation, but basically on the user = User.objects line, you want to find the user in the database given the key that was supplied.

I hope this helps anyone else facing this situation.

I don't think you need to put login(request, user, backend='django.contrib.auth.backends.ModelBackend') Simply we can access request.user instead request.auth add this request.user = user If you put login function there, each api call login again and again

quroom avatar Nov 09 '23 07:11 quroom

Like @quroom said, the solution is

Simply we can access request.user instead request.auth add this request.user = user

For others:

class ApiKey(APIKeyHeader):
    param_name = 'X-API-Key'

    def authenticate(self, request, key):
        user = User.objects.filter(token__key=key).first()
        if user is not None:
            request.user = user
        return user

But it is a bit weird, maybe it can be documented.

Or we use request_attribute = 'user' route like @vitalik said

Using login(request, user, backend='django.contrib.auth.backends.ModelBackend') creates cookies and csrf token which we could need if we are doing an MPA (see image below)

Screenshot 2024-01-18 at 14 08 34

TLDR; if you need csrf token or session use the login function from django. If you need just the user to be replaced by the .auth use request.user = user

eznix86 avatar Jan 18 '24 10:01 eznix86