I have a problem with authenticate() method. It always returns None. I checked all my arguments, and they are not empty. In models, I have USERNAME_FIELD = 'username', in settings I have AUTH_USER_MODEL = 'account.User'. I really can't understand why it's not working.
models.py
class UserManager(BaseUserManager):
def _create(self, username, password):
if not username:
raise ValueError('Username cant be empty')
user = self.model(username=username)
user.set_password(raw_password=password)
user.save()
return user
def create_user(self, username, password):
return self._create(username=username, password=password)
class User(AbstractBaseUser):
id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='id')
email = models.EmailField()
username = models.CharField(max_length=25, unique=True)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
education = models.CharField(max_length=100, blank=True)
date_of_birth = models.DateField(blank=True, null=True)
name = models.CharField(max_length=25, blank=True)
last_name = models.CharField(max_length=25, blank=True)
objects = UserManager()
USERNAME_FIELD = 'username'
views.py
class UserLoginView(ObtainAuthToken):
serializer_class = UserLoginSerializer
serializers.py
class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(required=True)
password = serializers.CharField(required=True)
def validate_username(self, username):
if not User.objects.filter(username=username).exists():
raise serializers.ValidationError('User wasn\'t found')
print(User.objects.get(username=username).password)
return username
def validate(self, data):
# authentications
request = self.context.get('request')
username = data.get('username') # test_name
password = data.get('password') # newpassword11
if username and password:
user = authenticate(username=username, password=password, request=request)
print(user)
if not user:
raise serializers.ValidationError('Invalid password')
else:
raise serializers.ValidationError('You have to type you\'re mail and password')
data['user'] = user
return data
I fixed it by changing the model to this:
class User(AbstractBaseUser, PermissionsMixin):
.....
after send post request to url routing to UserLoginView view with username and password at the post body got Json something like this at the response:
{"token":"30e0215a5f4cc521203a633452d63995d8f1fa77"}
When used AbstractBaseUser instead of AbstractUser be careful to implement all requirements of Django user model.
Tips: Do not forget to make the DRF Authentication settings correctly.
The problem was in users field, called is_active. Django allows to login only when user is active.
P.S. Thank to everyone, who tried to help :)
Related
I am unable to authenticate custom user in django==3.1.3
Here is Custom User Manager:
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if username is None:
raise TypeError(_('Users should have a username.'))
if email is None:
raise TypeError(_('Users should have a Email.'))
user = self.model(username=username, email=self.normalize_email(email))
user.set_password(password)
user.save()
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError(_('Password should not be empty.'))
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
Here's the CustomUser model:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255, unique=True, db_index=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
def __str__(self):
return self.email
I have added AUTH_USER_MODEL = 'authentication.CustomUser' in settings.py
(authentication is the app name)
In the shell, if I run these, I get authenticated_user as None:
user = CustomUser.objects.create(email='tejas#gmail.com', username='tejas12', password='tejas12')
authenticated_user = authenticate(email='tejas#gmail.com', username='tejas12', password='tejas12')
However, the User gets created successfully with given details.
Also, check_password returns False:
from django.contrib.auth.hashers import check_password
user.check_password('tejas12') # returns False
On creating a user using python manage.py createsuperuser,
authenticate returns the required user, and
check_password returns True
How should I authenticate the custom users?
In your example, you create the user as a regular Django model which doesn't hash password properly
user = CustomUser.objects.create(email='tejas#gmail.com', username='tejas12', password='tejas12')
create_user method should be used instead
>>> second_user = CustomUser.objects.create_user(email='test#example.com', username='test', password='test_passwd')
>>> second_user.check_password('test_passwd')
True
Authentication uses default backend unless you specify your own and will rely on the USERNAME_FIELD and password
>>> from django.contrib.auth import authenticate
>>> authenticate(email='test#example.com', username='test', password='test_passwd')
>>> authenticate(email='test#example.com', password='test_passwd')
<CustomUser: test#example.com>
When trying to authenticate a user created through a view in DRF Browsable API, I get
No active account found with the given credentials
The view:
class MyUserCreate(APIView):
def post(self, request, format='json'):
serializer = MyUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The serializer:
class MyUserSerializer(serializers.ModelSerializer):
username = serializers.CharField(
required=True,
validators=[UniqueValidator(queryset=MyUser.objects.all())],
min_length=5,
max_length=20
),
password = serializers.CharField(
required=True,
max_length=256
)
class Meta:
model = MyUser
fields = ('username', 'password')
def create(self, validated_data):
password = make_password(validated_data['password'])
user = MyUser.objects.create_user(validated_data['username'], password)
return user
The password is being hashed. At this stage, went on to the admin page and tested to login there too with the created account and got
Made sure the custom user model had is_active, is_superuser and is_staff and checked if that would fix the issue but it didn't.
class MyUserManager(BaseUserManager):
def create_user(self, username, password, **extra_fields):
user = self.model(
username=username
)
user.is_staff=True
user.is_active=True
user.is_superuser=True
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self.create_user(username, password, **extra_fields)
class MyUser(AbstractBaseUser):
objects = MyUserManager()
class Meta:
# managed = False
db_table = 'user_entity'
user_id = models.AutoField(primary_key=True, db_column='userId')
username = models.CharField(db_column='username', unique=True, max_length=20)
password = models.CharField(db_column='userPassword', max_length=256)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=True)
USERNAME_FIELD = 'username'
def __str__(self):
return str(self.user_id) + " (%s)" % str(self.username)
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
Then, tried to create the user using python manage.py createsuperuser... doing so then I'm able to both login in the admin pannel and authenticate that user with the Token Obtain Pair view shown initially.
First of all. Thank you for providing all the details. It is always easier to debug. Coming to the issue, the problem is that you are using make_password explicitly.
If you would look through the Django's set_password documentation, you'd find that it takes care of hashing.
What you are doing is, first you are hashing your password, and providing it as a raw value to set_password method via create_user. The set_password would assume that as a raw value and would hash it again. Hence, your original password is not used at all.
You can just remove the use of make_password and instead change your serializer's create method to
class MyUserSerializer(serializers.ModelSerializer):
...
def create(self, validated_data):
user = MyUser.objects.create_user(validated_data['username'], validated_data["password"])
return user
This should resolve your query. I hope it helps! :)
I'm following a tutorial book for Django (Django Unleashed) and I think there is a problem with the code when using newer versions of Django (i've read about problems with this chaper on amazon as well). So for future students of this book I ask this openly and in relation to the book.
For the creation of a custom user model the book suggests a user model that does not have a 'username' field. This Error occursd during makemigrations:
django.core.exceptions.FieldError: Unknown field(s) (username) specified for User
There are other topics about this error on StackOverflow and they suggest that django.contrib.auth.admin.UserAdminneeds the 'username' specified and therefore suggest adding an attribute called add_fieldsets to a class that inherits form UserAdmin. My question (so I dont have to get ahead of the code in the book in run into other problems) ... is it possible to create a custom user profile without specifiying a username and without the admin class? Here is my code so far:
User model
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField('email adress', max_length=254, unique=True)
is_staff = models.BooleanField('staff status', default=False)
is_active = models.BooleanField('active', default=False)
name = models.CharField(max_length=255)
joined = models.DateTimeField("Date Joined", auto_now_add=True)
USERNAME_FIELD = 'email'
objects = UserManager()
def __str__(self):
return self.email
def get_absolute_url(self):
return self.profile.get_absolute_url()
def get_full_name(self):
return self.name
def get_short_name(self):
return self.profile.name
Usermanager
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(
self, email, password, **kwargs):
email = self.normalize_email(email)
is_staff = kwargs.pop('is_staff', False)
is_superuser = kwargs.pop(
'is_superuser', False)
user = self.model(
email=email,
is_active=True,
is_staff=is_staff,
is_superuser=is_superuser,
**kwargs)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(
self, email, password=None,
**extra_fields):
return self._create_user(
email, password, **extra_fields)
def create_superuser(
self, email, password,
**extra_fields):
return self._create_user(
email, password,
is_staff=True, is_superuser=True,
**extra_fields)
And I want to perform makemigrations without an error
i think you need to use AbstractUser instead of the AbstractBaseUser
class User(AbstractUser, PermissionsMixin):
or need declare username for your model, for example:
username = models.CharField(_('username'), max_length=30, unique=True)
I am tying to build a custom user model in a Django app, instead of using the built-in one.
models.py
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from django.contrib.auth.models import BaseUserManager
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
if not kwargs.get('username'):
raise ValueError('Users must have a valid username.')
account = self.model(
email=self.normalize_email(email),
username=kwargs.get('username')
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
tagline = models.CharField(max_length=140, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
When I run the python manage.py makemigrations commands, I get the following error:
You are trying to add a non-nullable field 'password' to account
without a default; we can't do that (the database needs something to
populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
Note, I have added this in settings.py
AUTH_USER_MODEL = 'authentication.Account'
The app is called authentication btw.
How do I fix this? Thanks
The error you're getting is from the database. You can't create a non-nullable column without a default value when there are already rows for that column.
You would either need to set a default for the password field or delete all the users you already have in that table before running this migration.
That's how I"ve extended User model:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='email',max_length=255,unique=True, db_index=True,)
username = models.CharField(verbose_name='username', max_length=255, unique=True)
first_name = models.CharField(verbose_name='first_name', max_length=255, blank=True)
last_name = models.CharField(verbose_name='last_name', max_length=255, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
avatar = models.ImageField(upload_to='profile_images', blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['username']
def get_full_name(self):
return '%s %s' % (self.first_name, self.last_name,)
def get_short_name(self):
return self.username
def __unicode__(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.is_admin
It works good , but after new user registration I can't log in with his username and password
and in my database I have now 2 tables with users
auth_user - old users
blog_user - new extended users
And I can log in only with users from auth_user.
That's what I have for login in views.py:
#csrf_protect
def loginn(request):
c = {}
c.update(csrf(request))
return render_to_response("login/login.html", c)
#csrf_protect
def auth_view(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return render_to_response('login/loggedin.html',RequestContext(request))
else:
return HttpResponseRedirect('/posts/invalid')
So , how can I log in with user from my new , extended table ?
In order to allow a custom User model to be used within the authenticate() method, Django requires setting the AUTH_USER_MODEL setting.
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model
For your example code it would be AUTH_USER_MODEL = 'blog.User' within settings.py
The authenticate() method makes a call to get_user_model() which goes and finds if the AUTH_USER_MODEL setting is set, and then uses that as it's base for the model which to authenticate against.
Please be sure to read the documentation on substituting a custom user model within Django. There are a handful of caveats that you should be aware of before making a decision to switch the auth user model.
If it's possible, I would recommend just extending the base auth user model to include any application specific fields you would need because it appears you are still logging users in via Username.
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model