Comments (11)
@ivellios, @omab, there are a few issues with using just email address as the main account:
- if a user already exists with that email address and database has emails as unique, then social-auth will generate an invalid email address for the user.
- If user does not provide email over social networks (permissions, etc.), it will use FirstNameLastName (username) which is NOT correct if you depend on username being the email address.
- If user creates social email account and/or regular email account, email sensitivity matters and user is forced to login using [email protected] (which is normal for email standards but not for users to remember which letters were upper/lower case).
- Custom case if email is username: If user is already logged in as [email protected], and logs in as [email protected], then accounts should NOT be linked (validate both emails).
To solve these problems, I created a custom get_username pipeline:
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
import urllib
# This is initially from https://github.com/python-social-auth/social-core/blob/master/social_core/pipeline/user.py
def get_username(strategy, details, backend, user=None, *args, **kwargs):
# Get the logged in user (if any)
logged_in_user = strategy.storage.user.get_username(user)
# Custom: check for email being provided
if not details.get('email'):
error = "Sorry, but your social network (Facebook or Google) needs to provide us your email address."
return HttpResponseRedirect(reverse('repairs-social-network-error') + "?error=" + urllib.quote_plus(error))
# Custom: if user is already logged in, double check his email matches the social network email
if logged_in_user:
if logged_in_user.lower() != details.get('email').lower():
error = "Sorry, but you are already logged in with another account, and the email addresses do not match. Try logging out first, please."
return HttpResponseRedirect(reverse('repairs-social-network-error') + "?error=" + urllib.quote_plus(error))
return {
'username': details.get('email').lower(),
}
In settings:
INSTALLED_APPS += [
'social_django',
]
# User models
AUTH_USER_MODEL = 'repairs_accounts.MyUser'
SOCIAL_AUTH_USER_MODEL = 'repairs_accounts.MyUser'
# Allowed authentication backends (social + django)
AUTHENTICATION_BACKENDS = [
# Social logins
'social_core.backends.facebook.FacebookOAuth2',
'social_core.backends.google.GoogleOAuth2',
# Default django login
'django.contrib.auth.backends.ModelBackend',
]
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
# Make up a username for this person, appends a random string at the end if
# there's any collision.
# 'social_core.pipeline.user.get_username',
# CUSTOM: this gets email address as the username and validates it matches
# the logged in user's email address.
'repairs_accounts.pipeline.get_username',
# 'social_core.pipeline.mail.mail_validation',
'social_core.pipeline.social_auth.associate_by_email',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details'
)
# Facebook settings
SOCIAL_AUTH_FACEBOOK_KEY = '..'
SOCIAL_AUTH_FACEBOOK_SECRET = '..'
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id, name, email, age_range',
}
SOCIAL_AUTH_FACEBOOK_AUTH_EXTRA_ARGUMENTS = {
# 'auth_type': 'reauthenticate',
}
# Google settings
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '..'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '..'
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {
# 'access_type': 'offline',
# 'approval_prompt': 'force',
}
If you have a custom User (BaseUserManager) that only takes email address, you need to let social-auth pass the field 'username' or else it will crash:
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None, username=""):
"""
Creates and saves a User with the given email and password.
NOTE: Argument 'username' is needed for social-auth. It is not actually used.
"""
if not email:
raise ValueError('Users must have an email address.')
# Validate email is unique in database
if MyUser.objects.filter(email = self.normalize_email(email).lower()).exists():
raise ValueError('This email has already been registered.')
user = self.model(
email=self.normalize_email(email).lower(),
)
user.set_password(password)
# Save and catch IntegrityError (due to email being unique)
try:
user.save(using=self._db)
except IntegrityError:
raise ValueError('This email has already been registered.')
return user
class MyUser(AbstractBaseUser):
objects = MyUserManager()
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
error_messages={
'unique':"This email has already been registered.",
}
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
# Custom: was this email validated, at some point, by sending an email?
is_email_validated = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __str__(self):
return self.email
def __unicode__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
It takes a while to solve this (plus handling email/password registrations without social networks) but it ends up working great.
from social-app-django.
I got the same problem but, just adding
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = { 'fields': 'id, name, email, age_range', }
solved my problem ✌️
from social-app-django.
@aminehaddad but what happens for the people register to facebook with their phone number only ? Those people also need to login to the website right.
from social-app-django.
Is your facebook app allowed to fetch emails and retrieving them?
from social-app-django.
@omab - yes, I get public information + e-mail address. E-mail address is saved in my db, but it does not set it as username.
from social-app-django.
@ivellios do you have SOCIAL_AUTH_USER_FIELDS
defined in your settings? If that's the case, what's the value for it? If not, then we need to debug the get_username
pipeline to check what's going on with the username
value since I can't reproduce the issue locally. This is the particular block of code that handles the case https://github.com/python-social-auth/social-core/blob/4167972181054d94c5091250b6545fa2fa6840a8/social_core/pipeline/user.py#L39-L44, the first if
clause is the one that handles the "email as username" option.
from social-app-django.
I have SOCIAL_AUTH_USER_FIELDS = ['email', 'username']
Also I do have: SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
I tried to set it to only ['email']
but then I get error. Maybe I set SOCIAL_AUTH_USER_FIELDS wrong? Also, I didn't find anything like that in docs when I was going through the configuration steps.
from social-app-django.
@ivellios are you able to conduct a debug of that particular function pointed in my previous comment?
from social-app-django.
I had to leave this issue for a few weeks, but coming back to it I find this answer :-)
@aminehaddad - that works great! Thanks for your help! 👍
from social-app-django.
@aminehaddad Just one question, though: log-in method tries to first associate user, and if it's not there, it goes to create_user, by default. Now, I know that you can override said behavior with SOCIAL_AUTH_PIPELINE, and I have by removing #'social_core.pipeline.user.create_user'. The problem is that, in that case, if user has no account associated with their email on facebook, nothing will happen. Is there a way to simply redirect user to an URL if that happens - everything is ok except that user with said email is not registered already?
from social-app-django.
@aminehaddad you saved my day!
@omab i think this issue should be re-opened and fixed somehow with some kind of settings in the documentation for this use case (facebook login with email as username)
from social-app-django.
Related Issues (20)
- Fix missing 'jose' module HOT 3
- Missing migrations in v5.1.0 ? HOT 10
- Only perform verification or update existing user
- Missing migrations on 5.2.0? HOT 2
- Django 4.2.2 breaks the way JSONField behaves HOT 1
- Question. Request additional Google Permissions outside of login flow
- SAML redirect loses session data storing "next" url and RelayState cannot be used instead
- Requiring POST method by default HOT 1
- Social Auth with Gmail SSO Causing Error --> 'str' object has no attribute 'get' HOT 2
- 5.2.0 not compatible with Django >= 4.2.2
- How to override a field of `AbstractUserSocialAuth` HOT 1
- IrreversibleError when migrating app zero for a new DB HOT 1
- Subclassing AbstractUserSocialAuth creates migration in social_django app. HOT 1
- 5.3.0 migrations feedback HOT 9
- Deprecated Sign In with LinkedIn HOT 2
- Enhance Django OAuth Library for Clean JSON Responses in API Development
- Linkedin complete login step is stuck
- AWS Cognito - Invalid scope error on authentication request HOT 1
- Update to social-core 4.5.2 HOT 1
- OPEN FOR MAINTAINERS
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from social-app-django.