Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User model other than AUTH_USER_MODEL in Django REST Framework

I have an architectural problem. I'm using Django (with admin panel) and DRF (api with stateless authentication using JWT).

Django has Admin users represented by model which is more or less the same as default Django User Model. Admins work with Django Admin only and can't use DRF api.

DRF has API users that are allowed to use only api through DRF and can't interact with Django Admin or Django Session etc.

I know that the best approach is to use multi model inheritance, like:

class User(DjangoUserModel):
    pass

 class Admin(User):
      pass

 class API(User):
      pass

AUTH_USER_MODEL = "User"

but the problem is that, those users are completly different. Eg: API user has complex composite key as an username field which is impossible in combination to simple Admin username field. And many other differences. ..

The question is: may I use a user object that is not an AUTH_USER_MODEL instance in DRF? So self.request.user will store a model instance that isn't connect in any way with an AUTH_USER_MODEL. Has any of you done something similar?

like image 735
User Avatar asked Mar 16 '18 22:03

User


3 Answers

Well, yes sir. You can do that. Look at the following example:

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

class AuthenticatedServiceClient:
    def is_authenticated(self):
        return True

class JwtServiceOnlyAuthentication(JSONWebTokenAuthentication):
    def authenticate_credentials(self, payload):
        # Assign properties from payload to the AuthenticatedServiceClient object if necessary
        return AuthenticatedServiceClient()

And in settings.py:

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'myapp.authentication.JwtServiceOnlyAuthentication',
    ),
}

If you want, you can define additional DEFAULT_AUTHENTICATION_CLASSES for your DRF. The authentication classes are just like middleware, just a queue that is populating request.user.

Adding your own authentication classes that are using diffrent user model from AUTH_USER_MODEL will work exactly as you would except.

like image 175
Gal Silberman Avatar answered Sep 23 '22 20:09

Gal Silberman


Since you are using Django and DRF, perhaps you can write an APIUser model which extends from AbstractBaseUser with your customizations, write a custom authentication class and plug that into the REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES setting. Leave AUTH_USER_MODEL alone for the django admin.

Your custom authentication may just need to override authenticate_credentials (i've referenced the TokenAuthentication class in DRF github) and return an APIUser rather than the default defined in settings.AUTH_USER_MODEL. This will be a bit different because you're decoding a JWT, so you'll likely be extract some info from your JWT and looking up your APIUser by whatever means you need, such as your composite field. This should result in self.request.user being an APIUser for your DRF API views.

You're API views should be using the rest framework's settings, and your Django admin should be using the regular django settings. There may be some other caveats, but generally you'll be ok with just this I think.

like image 45
A. J. Parr Avatar answered Sep 22 '22 20:09

A. J. Parr


One thing that I immediately recall is how Mongoengine used to hack the whole django authentication system. Mongoengine < 0.10 has a django compatibility app that implements a crutch to store users in MongoDB and make them accessible via self.request.user.

It has to use a crutch, because Django Session API is opinionated and assumes that you're using AUTH_USER_MODEL, backed by SQL database for storing your users.

So, I think you should disable SessionMiddleware and CSRF token handling and use 2 distinct custom authentication systems for Admin and API purposes. With TokenAuthentication or JWTAuthentication this should be doable.

Here's another example of a project with DRF and Mongoengine, with a custom implementation of TokenAuthentication, backed by MongoDB.

like image 22
Boris Burkov Avatar answered Sep 21 '22 20:09

Boris Burkov