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?
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With