linkedin email address not recieved using python socail auth - django

I have my linkedin scope set as follows:
SOCIAL_AUTH_LINKEDIN_SCOPE = ['r_basicprofile', 'r_emailaddress']
When I try logging in using python social auth then an exception is raised which is defined in my model:
if not email:
raise ValueError('Users must have an email address')
while logging in with facebook and google work fine but not linkedin
The error means it does not have email field in it. I am working on my localhost

It looks like for linkedin we need to specify field selectors in order to get email address and other information from it unlike facebook and google. This is what i was missing:
SOCIAL_AUTH_LINKEDIN_FIELD_SELECTORS = ['email-address']

Related

Email conflict while login by different socials

I have python-django backend, that allows u to sign in through fb, apple, email, google. My email field is unique, so I can't have more than one user with single email.
When user sign in with socials I take his email and create new user.
Problem is, if u have two socials with single email, u can't use both of them to sign in. It works like:
We have Facebook and appleId with same email
Sign in with apple -> I create user with appleId, name, email -> user press logout -> user press sign in with Facebook -> I can't create new user because I have that email in db already.
So the question is, what should I do and where I can find examples of it.
Details: I have custom Django User and I have to take email in any case. I can't use Django-social.
I think on the last step I should give user profile, that was made in second step, but I don't know how to google this problem and how its done common practice
when someone logs in through FB or Google and its email not present in the local account, It creates a new social account. If the email present in local accounts matches with google or Facebook accounts while logging in through this, it only authenticates it (no need to put local account password). I also saved few things into the User during #receiver(user_signed_up).
This code solved the conflict between same google and Facebook using the same email id
I did not use verification, you can use it if you want
class MyAppSocialAccountAdapter(DefaultSocialAccountAdapter):
# login(request, user) Before we did this
#transaction.atomic
def pre_social_login(self, request, sociallogin):
# social account already exists, so no need to do anything Auto login will happen
if sociallogin.is_existing:
return
# some social logins don't have an email address, e.g. facebook accounts
# with mobile numbers only, but allauth takes care of this case so just
# ignore it
if 'email' not in sociallogin.account.extra_data:
return
# find the first verified email that we get from this sociallogin
# verified_email = None
# for email in sociallogin.email_addresses:
# if email.verified:
# verified_email = email
# break
try:
user = User.objects.get(email=sociallogin.email_addresses[0])
# This user now can be authenticated without password through google or facebook
sociallogin.connect(request, user)
raise ImmediateHttpResponse(redirect('logout_process')) # send it back to login
except Exception as e:
print(e)
# if social account does not exist, it creates one by default

Django all-auth - Social login - Facebook - Pass without requesting email

So I must be missing something, I've looked for similiar questions and tried their solutions like overhere (Django allauth, require email verification for regular accounts but not for social accounts)
I would like to let users interact with my webapp without requesting their e-mailadres after logging in with their social account, in this case with their facebook account.
I have the following set up;
settings.py
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_VERIFICATION = 'optional'
SOCIALACCOUNT_EMAIL_REQUIRED = False
SOCIALACCOUNT_EMAIL_VERIFICATION = 'optional'
At the moment, whenever someone tries to login with their facebook account, they get redirected to the signup form requesting for their email address.
Even though I'm using email as a authentication method for regular signups, this should not be necessary for social signups am I right?
Edit:
So I tried implementing Google as a social login provider, since google has a flag for email_verified I thought this might solve the problem.
Even though you can nog login with your Google account, the social login redirects to
/accounts/social/signup/
Not really sure why but I would like to skip this step for social logins.
Kevin
Allright, found something over here Paypal redirects to /accounts/social/signup
When using a social login while the email address is already in use, the signup form kicks and you have to submit your email address.
After signup, a message will be displayed saying the email address is already in use.

is there a way to authenticate user and password using django?

Is there away to authenticate username and password in Django. all i can find is
https://simpleisbetterthancomplex.com/tutorial/2017/02/18/how-to-create-user-sign-up-view.html
this it is just uses username authentication
Django by default authenticating the user by using of username. If you want to authenticate the user with email instead of username you can check this tutorial. Authenticate user with email instead of username
It might be worth your while to look at allauth. https://django-allauth.readthedocs.io/en/latest/overview.html

django python social auth get facebook email

I am using python social auth to connect with facebook but I am not getting email of the user
In my settings I have added :
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email',]
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id, name, email, age_range'
}
and I have written custom pipeline to get users email:
def create_user_profile(strategy, details, response, user=None, *args, **kwargs):
print(kwargs['response'].get('name'))
Here I can not get user email
How can i get user's email. Need advice
As far as I know, email is not included in the basic scope. That's why you need to explicitly show that you want email as well.
Do this in your settings file:
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
and try to register a user through Facebook. He will be asked whether he agrees to grant those permissions, including email. If he agrees, you will get his email in response['email']

Django allauth social login: automatically linking social site profiles using the registered email

I aim to create the easiest login experience possible for the users of my Django site. I imagine something like:
Login screen is presented to user
User selects to login with Facebook or Google
User enter password in external site
User can interact with my site as an authenticated user
Ok, this part is easy, just have to install django-allauth and configure it.
But I also want to give the option to use the site with a local user. It would have another step:
Login screen is presented to user
User selects to register
User enter credentials
Site sends a verification email
User clicks in email link and can interact with my site as an authenticated user
Ok, both the default authentication and allauth can do it. But now is the million dollars question.
If they change how they do the login, how do I automatically associate their Google, FB and local accounts?
See that any way they login, I have their email address. Is it possible to do it using django-allauth? I know I can do it with user intervention. Today the default behavior is to refuse the login saying that the email is already registered.
If it isn't possible to do just with configuration, I'll accept the answer that gives me some orientation about which modifications should I make in allauth code to support this workflow.
There are a lot of reasons to do this. The users will forget which method they used to authenticate, and will sometimes use Google, sometimes FB and sometimes the local user account. We already have a lot of local user accounts and social accounts will be a new feature. I want the users to maintain their identity. I envision the possibility to ask for the user friends list, so if they logged using Google, I'd like to also have their FB account.
It is a hobby site, there isn't great security requirements, so please don't answer that this isn't a wise security implementation.
Later, I'd create a custom user model to have just the email as the login id. But I'll be happy with an answer that just let me automatically associate a accounts of the default user model that has a required username.
I'm using Django==1.5.4 and django-allauth==0.13.0
Note (2018-10-23): I'm not using this anymore. Too much magic happening. Instead I enabled SOCIALACCOUNT_EMAIL_REQUIRED and 'facebook': { 'VERIFIED_EMAIL': False, ... }. So allauth will redirect social logins on a social signup form to enter a valid email address. If it's already registered an error shows up to login first and then connect the account. Fair enough for me atm.
I'm trying to improve this kind of use case and came up with the following solution:
from allauth.account.models import EmailAddress
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class SocialAccountAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
"""
Invoked just after a user successfully authenticates via a
social provider, but before the login is actually processed
(and before the pre_social_login signal is emitted).
We're trying to solve different use cases:
- social account already exists, just go on
- social account has no email or email is unknown, just go on
- social account's email exists, link social account to existing user
"""
# Ignore existing social accounts, just do this stuff for new ones
if sociallogin.is_existing:
return
# some social logins don't have an email address, e.g. facebook accounts
# with mobile numbers only, but allauth takes care of this case so just
# ignore it
if 'email' not in sociallogin.account.extra_data:
return
# check if given email address already exists.
# Note: __iexact is used to ignore cases
try:
email = sociallogin.account.extra_data['email'].lower()
email_address = EmailAddress.objects.get(email__iexact=email)
# if it does not, let allauth take care of this new social account
except EmailAddress.DoesNotExist:
return
# if it does, connect this new social login to the existing user
user = email_address.user
sociallogin.connect(request, user)
As far as I can test it, it seems to work well. But inputs and suggestions are very welcome!
You will need to override the sociallogin adapter, specifically, the pre_social_login method, which is called after authentication with the social provider, but before this login is processed by allauth.
In my_adapter.py, do something like this
from django.contrib.auth.models import User
from allauth.account.models import EmailAccount
from allauth.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class MyAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
# This isn't tested, but should work
try:
user = User.objects.get(email=sociallogin.email)
sociallogin.connect(request, user)
# Create a response object
raise ImmediateHttpResponse(response)
except User.DoesNotExist:
pass
And in your settings, change the social adapter to your adapter
SOCIALACCOUNT_ADAPTER = 'myapp.my_adapter.MyAdapter`
And you should be able to connect multiple social accounts to one user this way.
As per babus comment on this related thread, the proposed answers posted before this one (1, 2) introduce a big security hole, documented in allauth docs:
"It is not clear from the Facebook documentation whether or not the fact that the account is verified implies that the e-mail address is verified as well. For example, verification could also be done by phone or credit card. To be on the safe side, the default is to treat e-mail addresses from Facebook as unverified."
Saying so, I can signup in facebook with your email ID or change my email to yours in facebook and login to the website to get access to your account.
So taking this into consideration, and building on #sspross answer, my approach is to redirect the user to the login page, and notify her/him of the duplicate, and inviting him to log in with her/his other account, and link them once they are logged in. I acknowledge that differs from the original question, but in doing so, no security hole is introduced.
Thus, my adapter looks like:
from django.contrib.auth.models import User
from allauth.account.models import EmailAddress
from allauth.exceptions import ImmediateHttpResponse
from django.shortcuts import redirect
from django.contrib import messages
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class MyAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
"""
Invoked just after a user successfully authenticates via a
social provider, but before the login is actually processed
(and before the pre_social_login signal is emitted).
We're trying to solve different use cases:
- social account already exists, just go on
- social account has no email or email is unknown, just go on
- social account's email exists, link social account to existing user
"""
# Ignore existing social accounts, just do this stuff for new ones
if sociallogin.is_existing:
return
# some social logins don't have an email address, e.g. facebook accounts
# with mobile numbers only, but allauth takes care of this case so just
# ignore it
if 'email' not in sociallogin.account.extra_data:
return
# check if given email address already exists.
# Note: __iexact is used to ignore cases
try:
email = sociallogin.account.extra_data['email'].lower()
email_address = EmailAddress.objects.get(email__iexact=email)
# if it does not, let allauth take care of this new social account
except EmailAddress.DoesNotExist:
return
# if it does, bounce back to the login page
account = User.objects.get(email=email).socialaccount_set.first()
messages.error(request, "A "+account.provider.capitalize()+" account already exists associated to "+email_address.email+". Log in with that instead, and connect your "+sociallogin.account.provider.capitalize()+" account through your profile page to link them together.")
raise ImmediateHttpResponse(redirect('/accounts/login'))
I've just found this comment in the source code:
if account_settings.UNIQUE_EMAIL:
if email_address_exists(email):
# Oops, another user already has this address. We
# cannot simply connect this social account to the
# existing user. Reason is that the email adress may
# not be verified, meaning, the user may be a hacker
# that has added your email address to his account in
# the hope that you fall in his trap. We cannot check
# on 'email_address.verified' either, because
# 'email_address' is not guaranteed to be verified.
so, it is impossible to do by design.
If they change how they do the login, how do I automatically associate their Google, FB and local accounts?
It is possible, but you have to be careful about security issues. Check scenario:
User create account via email and password on your site. User does not have Facebook.
Attacker creates account on Facebook with user email. (Hypothetic scenario, but you do not control if social network verify email).
Attacker login to your site with Facebook and automatically get access to user original account.
But you can fix it. I describe solution to ticket https://github.com/pennersr/django-allauth/issues/1149
Happy scenario should be:
User create account via email and password on your site. User logged out.
User forget about his account and try to login via his Facebook.
System authenticate user via Facebook and find out, he already created account via other method (emails are same). System redirect user to normal login page with message "You already create your account using the email and password. Please log in this way. After you log in, you will be able to use and login using Facebook."
User login via email and password.
System automatically connect his Facebook login with his account. Next time user can use Facebook login or email and password.