multiple USERNAME_FIELD in djangorestframework? - django

How can i allow users to authenticate via multiple username field e.g(email | phone_number as USERNAME_FIELD)??
I have a custom User model---> in which I have a USERNAME_FIELD set to 'email'.
settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
#THis is a default Backend which points to CustomUser's USERNAME_FIELD.
'account.backends.AuthenticateBackend',
##Custom Backend which tells to use phone number as USERNAME_FIELD.
]
Here's the issue!
It works fine if I use default obtain_auth_token view e.g.(allows me to login via multiple field)---> but whenever I write my own login logic it skips default model backend settings thus, it greets me with error.
ValueError: Field 'phone_number' expected a number but got 'someEmail#gmail.com'.

Related

Django DRF Djoser - change default User model to custom model which is not equal but inherited from AUTH_USER_MODEL

In django have the following custom user structure:
class CinemaUser(AbstractUser): ...
class CinemaAdmin(CinemaUser):....
class Cinemagoer(CinemaUser): ...
Logic is that I have
Cinemagoer - an ordinary user (can register themselves),
CinemaAdmin - kind of administrator (is created only by superuser) and
Superuser.
in settings: AUTH_USER_MODEL = CinemaUser
the problem is with Djoser,
a request http://localhost:8000/auth/users/ (with data in body) creates CinemaUser, because Djoser has User = get_user_model(), which means Djoser uses AUTH_USER_MODE, while I need Djoser to use Cinemagoer model to create Cinemagoer). How can I use Cinemagoer with Djoser in this case?
I tried to change in Djoser/serializers directly User = CinemaUser ... just for test, it works fine, but it is definitely the wrong way.

Custom authentication middleware not working when objects is not overridden in model definition

Following the question I asked here
I have a custom authentication middleware that does not work anymore if I do not override the objects.
The models.py looks like this:
class Person(AbstractUser):
company_id = models.IntegerField(null=True, blank=True, default=None)
role = models.CharField(max_length=255)
def __str__(self):
return "{last}, {first} ({id})".format(
last=self.last_name, first=self.first_name, id=self.id
)
If I leave the code like this, the authentication never takes place (and I end up in an infinite loop of redirections).
Now, if I update my model by adding:
objects = models.Manager()
All of the sudden, the authentication takes place and it is (at least this part) is working fine.
I know that the custom authentication middleware is updating the database when the user logs in. However, I can't figure out why I should override objects
So I'm surprised this middleware works, because it intercepts any page, including the authentication page and does authentication. If the setting TEQUILA_CLEAN_URL is set to a falsy value, then it will login the user and then let the login view run. If this is the standard login view and redirect_authenticated_user is set to the default of False, then the user will be logged out and logged in again.
My guess is (without putting it under the debugger) that this then fails in auth.autenticate:
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
Which means authentication fails and so your session isn't destroyed and you're actually logged in because the middleware already did it.
That get_by_natural_key() call is pretty much the only thing I can see in the auth and login flow that's different for the UserManager. But this theory hinges on the fact that TEQUILA_CLEAN_URL is set to False on your end. Otherwise, you'd have a redirect and not hit the login view.

Separate AUTH_USER_MODEL for Django Rest Framework

The Django-Project contains a REST-API, based on TokenAuthentication and a Web Login to the Backend with the Django Auth User.
The Problem is: I found no way to specify two different AUTH_USER_MODEL'S for each authentication individually.
For the REST-API users, I defined a Custom User Model:
class User(models.Model):
id = models.IntegerField(primary_key=True)
name = ....
....
USERNAME_FIELD = 'id'
REQUIRED_FIELDS = []
In the settings I added AUTH_USER_MODEL = 'backend.User', so that the rest_framework Token authentication references to the right user model.
But when enabling the django.contrib.auth.views.login for the Web-Backend, this also uses the backend.Usermodel for authentication. But it should use the django built in user model.
How can I specify a second user model specifically for either the rest framework api or for the django.contrib.auth.views?

Longer username in Django 1.7

I want to increase the length of the username in django from 30 to around 80, I know it may be duplicate question but the previous answers are not working, for example https://kfalck.net/2010/12/30/longer-usernames-for-django
this is for Django 1.2.
Did anyone try similar hack for Django>1.5
Thanks in advance
In Django 1.5 and above, the recommended approach would be to create a custom user model. Then you can make the username field exactly as you want.
I had the same problem few days ago. Finally, I ended just with cutting off first 30 characters of the (old) username (into the new database table), and adding a custom authentication backend that will check the email instead of user name. Terrible hack I know, and I'm planning to fix it as soon as I have some time. The idea is following:
I already have a model class that has one-to-one relation with djangos auth.User. I will add another field there called full_username.
class MyCustomUserModel(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL, related_name="custom_user")
full_username = models.CharField(max_length=80, ...)
...
Then, I'll add another custom authentication backend that will check this field as username. It would look something like this:
from django.contrib.auth.backends import ModelBackend
class FullUsernameAuthBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.filter(custom_user__full_username=username)
# If this doesn't work, will use (the second case):
# user = MyCustomUserModel.objects.filter(full_username=username).user
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Adding exception MyCustomUserModel.DoesNotExist in "(the second case)"
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
After this, you need to change settings.py:
AUTHENTICATION_BACKENDS = (
"....FullUsernameAuthBackend",
# I will have the email auth backend here also.
)
I hope that it will work.
Custom User Models are a huge change to make and aren't always compatible with apps. I solved it by running this very pragmatic migration. Note this only solves it at the database level.
migrations.RunSQL("alter table auth_user alter column username type varchar(254);")

Django Social Auth store username from Facebook in Custom User Model

I've just integrated Django-Social-Auth (DSA) v0.7.23 using django 1.5 with a custom User model and auth/login is working fine except for the username field which is not being stored in my custom User model.
Stepping through the DSA code it appears to explicitly remove the username which was passed back by facebook. Here is the function in question that pops the username off:
#classmethod
def username_field(cls, values):
user_model = cls.user_model()
if hasattr(user_model, 'USERNAME_FIELD'):
# Django 1.5 custom user model, 'username' is just for internal
# use, doesn't imply that the model should have an username field
values[user_model.USERNAME_FIELD] = values.pop('username') # Username removed
return values
How can I go about getting the username that was passed back from facebook and not having it be explicitly removed by DSA?
I believe a work around would be to create a custom pipeline that generates a username. However I was wondering if anyone else encountered this scenario before and leveraged anything that already exists within DSA (i.e. a particular settings.py configuration)
Thanks.
The original username is available in the details attribute passed to pipeline functions, take this for example:
def generated_username(user, details, *args, **kwargs):
username = details['username']
user.your_field = username
user.save()
It's worth noting that the username is popped from values if you have a USERNAME_FIELD defined in your custom model.