User without username with allauth, rest_auth and facebook - django

I have a setup with django-rest-framework, rest-auth, allauth and facebook Oauth2. My problem here is that when I created an user using the facebook endpoint, the social user was created, the django user was also created but both have no username. Should I configure this somewhere?

The username field is no longer present, because it was deprecated with Graph API v2.0 one year ago...
See
https://developers.facebook.com/docs/apps/changelog#v2_0_graph_api
/me/username is no longer available.
Check if you're using the newest versions of your libraries.

you can set custom username by overriding DefaultSocialAccountAdapter
i just removed # from email and replaced by _ to generate username
#in setting added this
SOCIALACCOUNT_ADAPTER = 'trip2.users.adapter.SocialAdapter'
# and then override the Adapter
class SocialAdapter(DefaultSocialAccountAdapter):
def populate_user(self,
request,
sociallogin,
data):
"""
Hook that can be used to further populate the user instance.
For convenience, we populate several common fields.
Note that the user instance being populated represents a
suggested User instance that represents the social user that is
in the process of being logged in.
The User instance need not be completely valid and conflict
free. For example, verifying whether or not the username
already exists, is not a responsibility.
"""
username = data.get('username')
first_name = data.get('first_name')
last_name = data.get('last_name')
email = data.get('email')
name = data.get('name')
user = sociallogin.user
emailtouname = email.split('#')[0] + "_" + email.split('#')[1].split('.')[0]
user_email(user, valid_email_or_none(email) or '')
name_parts = (name or '').partition(' ')
user_field(user, 'first_name', first_name or name_parts[0])
user_field(user, 'last_name', last_name or name_parts[2])
user_username(user, username or emailtouname)
return user

You can pass a 'username' key along with other data retrieved via Facebook API. Or you can dig into allauth/socialaccount/adapter.py populate_user() method and customize the 'username' field (I simply make it equal to user email)

Related

does django encode passwords in the Database?

I created a user with a password password123 but in the database the password field look like this pbkdf2_sha256$260000$rJZWVrYXlokRG8fGMS1fek$S7Dm9soflUsy0Q74CJP8sB60tgfRWuRPdqj5XL0DBV0=
the problem: is when I create new user via rest framework i got the poassword feidl look like passsword123
so how should i created new user in order to keep the django password encoding functionality
also how to deactivate this password encoding functionality
Django uses encryption middlewares to encrypt passwords (since the database sees passwords as VarChar fields, so Django's model sees them as plain text unless it is told otherwise). If you want the Django User model to use encryption, you must call
user_obj.set_password(passwd_text)
With this line of code, you tell Django to run encryption algorithms. For example, in your case, you can first use the serializer's extra_kwargs to exclude passwords from database-readable data, then create the user.
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop("password")
user = User(**validated_data)
user.set_password(password)
user.save()
return user
if you want to read more on Django users and passwords read these docs
user model doc and
encryption types and password management doc
you need to override create method in User Create Serializer:
class UserCreateSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
class Meta:
model = User
fields = "__all__" # or your specific fields
extra_kwargs = {
"password": {"write_only": True},
}
Now your user password will be saved as hashed password in database.
re. question 2.
Django does not store the password, only hashed value of the password, which it uses for comparison when the user logs in.
It is not possible to reverse engineer the password from this hash (well, mathematically it is possible, but you don't have the resources to run that process). Nor should it be.
If you wanted a password field that wasn't hashed, you would use just a string field. But... DON'T DO THIS! It is massively insecure.
There is no need for you to know the user's password.
As for question 1, I'm not sure why you're not seeing it hashed - you will need to post some code.

Set optional username django User

I have to store Customer information in a Customer table. We don't need to store their username and password. But to create customer groups and users, I'm using django's User and Group Models. Here is the customer Model which I use to store it's basic information.
class Customer(models.Model):
"""
The Customer
"""
UID = models.TextField(blank=True, null=True, db_index=True)
fk_user = models.ForeignKey(User, primary_key=False)
fk_details = models.ForeignKey(UserDetails, primary_key=False)
fk_contact_details = models.ForeignKey(ContactDetails, primary_key=False)
...
This is a class method which creates user object for which later I'm using it to store customer information :
from django.contrib.auth.models import User
def create_user(self, req_dict):
'''
Create a new User
'''
try:
user=User.objects.create_user(**req_dict)
user.save()
except:
return None
return user
But this code is throwing an IntegrityError which is because we are not passing username in the req_dict
req_dict = {'first_name': u'John', 'last_name': u'Smith', 'email': u'john.smith#xyz.com'}
What's the way to store username optional while creating a new user?
If you want to use django.contrib.auth you can't make the username field optional. You always have to put a value in the database.
In order to bypass that, I suggest you generate a value in create_user() for username. You could either use a random value, or create a username from email. Just make sure that it's unique.
You could make a pre_save signal to just provide default values
pre_save.connect(give_default_username, sender=User)
def give_default_username(sender, instance, *args, **kwargs):
instance.username = 'default'
Any time a user object's save method is invoked, it will go through this method first to append your required values before continuing with the save method.
I agree with #zanderle that it would be better to generate some username for your users. Maybe you can user their UID, or email, or something like this.
pre_save signal could solve your problem, but, as for me, it makes you system more implicit, because your models will be not consistent with your database.
Another way - to create some Default user for anonymous users. So, every db record will have user assignment and you will not force your customers to register.

Email field required django.contrib.auth

I want to register a user in django-rest-auth, if all username, password and email fields are provided. (I want to implement token authentication too to obtain a JSON response from the server.)
The default email field in django.contrib.auth.User is optional. But I want to set the email filed as required to register in the database so that the user gets an HTTP error response when a POST request has been made without an email.
In the project I am registering the new user through the following code.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', User.USERNAME_FIELD, "password", 'full_name',
'is_active', 'links', )
write_only_fields = ('password',)
read_only_fields = ('id',)
def create(self, validated_data):
print("Validated Data",validated_data)
if 'email' in validated_data:
user = get_user_model().objects.create(
username=validated_data['username']
)
user.set_email(validated_data['email'])
user.set_password(validated_data['password'])
user.save()
email = EmailMessage('Hello', 'World',
to=[validated_data['email']])
email.send()
user.is_active=False
return user
else:
return None
However, the above gives:
create() did not return an object instance
How do I set the email field as a required field?
I want to register an user in django-rest-auth, if all username, password and email fields are provided.
The correct way to require a field on a serializer in Django REST framework is to set required=True when initializing the field, or using the extra_kwargs parameter to set it on automatically generated fields.
In default email field in django.contrib.auth.User is optional. But I want to set the email filed as required to register in the database so that the user gets HTTP error response when POST request has been made without a email.
This means that the automatically generated field will not be required by default, but you can still override it with required=True.
However, the above gives:
create() did not return an object instance
This is because you are not returning a User instance from the create method, just like it says on the tin. Specifically, you are returning None if the email field is not included. None is not a User instance, and DRF is warning you that you're doing something wrong.

How to customize the default CREATE_USER functionality in django auth

I am using django-social-auth for facebook login which creates a new user if it doesn't exist in the db, else updates the existing db entry. I think it checks for fb_username OR fb_uid in the database to check if the user exists or not.
Requirements:
In my application, i need to be able to create a user by manually inserting his info in db. And this row should be updated when the user logs_in with facebook.
So I manually created an entry with all the information of the user i.e. fb_username, fb_uid, email, first_name, last_name, hometown... etc.
But what happens when the user logs_in is, a new entry is created instead of updating the manually created entry. So i really don't understand what fields does it use to check if the user exists or not. What would be the best way to go from here?
Extra Info: I am extending the user model of django to UserProfile using a one-to-one relationship and AUTH_PROFILE_MODULE = 'accounts.UserProfile'
The only way to do it was editing this default pipeline
'social_auth.backends.pipeline.user.create_user',
by changing the
def create_user(backend,details,response,username,user=None,*args,**kwargs):
if user:
return {'user': user}
if not username:
return None
to
def create_user(backend,details,response,username,user=None,*args,**kwargs):
username = User.objects.get(u_name=username)
if username:
return {'user': username}
if not username:
return None

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.