AuthFailed with django-social-auth & Facebook authentication - django

I'm using django-social-auth to do Facebook server-side authentication, and I am getting this error that I've been unable to track down: AuthFailed('There was an error authenticating the app').
I do NOT get this error when I test locally and I have the Facebook app site URL pointing to localhost.
However, I do get this error every time I point the Facebook app site URL to my dev instance -- e.g., https://dev.mydomain.com.
I have not been able to find a list of things that might cause the AuthFailed('There was an error authenticating the app') error.
Also, I have not been able to find others that have used django-social-auth successfully and solved this problem already.
I've found some useful info at the following:
https://github.com/omab/django-social-auth/blob/master/doc/configuration.rst
http://django-social-auth.readthedocs.org/en/latest/
https://developers.facebook.com/docs/technical-guides/login/ https://developers.facebook.com/docs/howtos/login/server-side-login/
Lastly, I want to do server-side authentication because I want to retrieve users' name, email, and photo. However, if that can be done via client-side authentication, I guess I could try that.
Code below...
in settings.py:
LOGIN_URL = '/account/login/'
LOGIN_REDIRECT_URL = '/'
LOGIN_ERROR_URL = '/account/login/' # added when trying to debug Facebook error
AUTHENTICATION_BACKENDS = (
'social_auth.backends.facebook.FacebookBackend',
'django.contrib.auth.backends.ModelBackend',
)
FACEBOOK_APP_ID = 'xxx'
FACEBOOK_API_SECRET = 'xxxxxx'
FACEBOOK_EXTENDED_PERMISSIONS = ['email']
SOCIAL_AUTH_CREATE_USERS = True
SOCIAL_AUTH_FORCE_RANDOM_USERNAME = False
SOCIAL_AUTH_DEFAULT_USERNAME = 'socialauth_user'
SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete'
SOCIAL_AUTH_ERROR_KEY = 'socialauth_error'
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True # force https in dev and production
# is SOCIAL_AUTH_LOGIN_REDIRECT_URL or SOCIAL_AUTH_BACKEND_ERROR_URL needed???
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
'social_auth.backends.pipeline.associate.associate_by_email',
'social_auth.backends.pipeline.misc.save_status_to_session',
'apps.dsa_pipeline.redirect_to_form',
'apps.dsa_pipeline.username',
'apps.dsa_pipeline.phone',
'social_auth.backends.pipeline.user.create_user',
'social_auth.backends.pipeline.social.associate_user',
'social_auth.backends.pipeline.social.load_extra_data',
'social_auth.backends.pipeline.user.update_user_details',
'apps.dsa_pipeline.profile',
)
in dsa_pipeline.py:
from django.http import HttpResponseRedirect
from urllib2 import urlopen
def redirect_to_form(*args, **kwargs):
if not kwargs['request'].session.get('saved_username') and kwargs.get('user') is None:
return HttpResponseRedirect('/account/signup_dsa/')
def username(request, *args, **kwargs):
if kwargs.get('user'):
username = kwargs['user'].username
else:
username = request.session.get('saved_username')
return {'username': username}
def phone(request, *args, **kwargs):
if kwargs.get('phone'):
phone = kwargs['phone'].phone
else:
phone = request.session.get('saved_phone')
return {'phone': phone}
def profile(backend, details, response, social_user, uid, user, *args, **kwargs):
profile = user.profile
if not profile.phone:
ph = kwargs.get('phone')
if ph:
profile.phone = ph
profile.save()
in urls.py:
urlpatterns = patterns("",
url(r"^$", direct_to_template, {"template": "homepage.html"}, name="home"),
url(r"^admin/", include(admin.site.urls)),
# profiles
url(r'^', include("apps.profiles.urls")),
url(r"^account/signup/$", SignupView.as_view(), name="account_signup"),
url(r"^account/signup_dsa/$", SignupViewDSA.as_view(), name="account_signup_dsa"),
url(r"^account/", include("account.urls")),
# Django Social Auth
url(r'', include('social_auth.urls')),
Thanks for any help!

That AuthFailed error is raised here, I would check the values for client_id, redirect_uri and client_secret being used a few lines above and compare against your app configuration on Facebook.

Related

Auth0 save username instead of 'sub'

I finished implementing auth0 to my django and react app. But ever since I signup with a new user, the 'sub' is saved as the username instead of the real 'name'. Is there a way to fix this?
settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'django.contrib.auth.backends.RemoteUserBackend',
]
JWT_AUTH = {
'JWT_PAYLOAD_GET_USERNAME_HANDLER':
'posts.utils.jwt_get_username_from_payload_handler',
'JWT_DECODE_HANDLER':
'posts.utils.jwt_decode_token',
'JWT_ALGORITHM': 'RS256',
'JWT_AUDIENCE': '<API_IDENTIFIER>',
'JWT_ISSUER': 'https://<APP_DOMAIN>/',
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}
posts.utils.py
from django.contrib.auth import authenticate
import json
import jwt
import requests
def jwt_get_username_from_payload_handler(payload):
username = payload.get('sub').replace('|', '.')
authenticate(remote_user=username)
return username
def jwt_decode_token(token):
header = jwt.get_unverified_header(token)
jwks = requests.get(
'https://{}/.well-known/jwks.json'.format('<APP_DOMAIN>')).json()
public_key = None
for jwk in jwks['keys']:
if jwk['kid'] == header['kid']:
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk))
if public_key is None:
raise Exception('Public key not found.')
issuer = 'https://{}/'.format('<APP_DOMAIN>')
return jwt.decode(token, public_key, audience='<API_IDENTIFIER>', issuer=issuer, algorithms=['RS256'])
Here are some of my codes I used at the auth0 tutorial. I tried changing the username payload handler but it didn't work yet.

Django Rest Auth - Custom registration logic for social views

I'm building a REST API with Django Rest Framework and Django Rest Auth.
My users have a consumer profile.
class UserConsumerProfile(
SoftDeletableModel,
TimeStampedModel,
UniversallyUniqueIdentifiable,
Userable,
models.Model
):
def __str__(self):
return f'{self.user.email} ({str(self.uuid)})'
As you can see it consists of some mixins that give it a UUID, a timestamp and an updated field and a OneToOne relationship to the user. I use this consumerprofile in relations to link data to the user.
This consumerprofile should get created as soon as a user signs up.
Here is the serializer that I wrote for the registration:
from profiles.models import UserConsumerProfile
from rest_auth.registration.serializers import RegisterSerializer
class CustomRegisterSerializer(RegisterSerializer):
def custom_signup(self, request, user):
profile = UserConsumerProfile.objects.create(user=user)
profile.save()
I connected this serializer in the settings:
REST_AUTH_REGISTER_SERIALIZERS = {
"REGISTER_SERIALIZER": "accounts.api.serializers.CustomRegisterSerializer"
}
It works flawlessly when the users signs up using his email. But when he signs up using facebook, no consumer profile gets created.
I thought the social view would also use the register serializer when creating users? How can I run custom logic after a social sign up?
EDIT for the bounty:
Here are the settings that I use for Django Rest Auth:
# django-allauth configuration
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_CONFIRM_EMAIL_ON_GET = True
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_ADAPTER = 'accounts.adapter.CustomAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'accounts.adapter.CustomSocialAccountAdapter'
SOCIALACCOUNT_PROVIDERS = {
'facebook': {
'METHOD': 'oauth2',
'SCOPE': ['email', 'public_profile', 'user_friends'],
'AUTH_PARAMS': {'auth_type': 'reauthenticate'},
'INIT_PARAMS': {'cookie': True},
'FIELDS': [
'id',
'email',
'name',
'first_name',
'last_name',
'verified',
'locale',
'timezone',
'link',
'gender',
'updated_time',
],
'EXCHANGE_TOKEN': True,
'LOCALE_FUNC': 'path.to.callable',
'VERIFIED_EMAIL': True,
'VERSION': 'v2.12',
}
}
# django-rest-auth configuration
REST_SESSION_LOGIN = False
OLD_PASSWORD_FIELD_ENABLED = True
REST_AUTH_SERIALIZERS = {
"TOKEN_SERIALIZER": "accounts.api.serializers.TokenSerializer",
"USER_DETAILS_SERIALIZER": "accounts.api.serializers.UserDetailSerializer",
}
REST_AUTH_REGISTER_SERIALIZERS = {
"REGISTER_SERIALIZER": "accounts.api.serializers.CustomRegisterSerializer"
}
And here are the custom adapters (in case they matter):
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.utils import build_absolute_uri
from django.http import HttpResponseRedirect
from django.urls import reverse
class CustomAccountAdapter(DefaultAccountAdapter):
def get_email_confirmation_url(self, request, emailconfirmation):
"""Constructs the email confirmation (activation) url."""
url = reverse(
"accounts:account_confirm_email",
args=[emailconfirmation.key]
)
ret = build_absolute_uri(
request,
url
)
return ret
def get_email_confirmation_redirect_url(self, request):
"""
The URL to return to after successful e-mail confirmation.
"""
url = reverse(
"accounts:email_activation_done"
)
ret = build_absolute_uri(
request,
url
)
return ret
def respond_email_verification_sent(self, request, user):
return HttpResponseRedirect(
reverse('accounts:account_email_verification_sent')
)
class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def get_connect_redirect_url(self, request, socialaccount):
"""
Returns the default URL to redirect to after successfully
connecting a social account.
"""
assert request.user.is_authenticated
url = reverse('accounts:socialaccount_connections')
return url
Lastly, here are the views:
from allauth.socialaccount.providers.facebook.views import \
FacebookOAuth2Adapter
from rest_auth.registration.views import SocialConnectView, SocialLoginView
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
class FacebookConnect(SocialConnectView):
adapter_class = FacebookOAuth2Adapter
I thought that if I connected the serializer like I did in the initial part of the question the register serializer logic would also get run when someone signs up using facebook.
What do I need to do to have that logic also run when someone signs up using facebook?
(If I can't fix it, I could make a second server request after each facebook sign up on the client side which creates the userconsumerprofile, but that would be kinda overkill and would introduce new code surface which leads to a higher likelihood of bugs.)
Looking briefly at the DefaultAccountAdapter and DefaultSocialAccountAdapter it may be an opportunity for you to override/implement the save_user(..) in your CustomAccountAdapter/CustomSocialAccountAdapter to setup the profile?
Looking just at code it seems that the DefaultSocialAccountAdapter.save_user will finally call the DefaultAccountAdapter.save_user.
Something like this maybe?
class CustomAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
user = super(CustomAccountAdapter, self).save_user(request, user, form,
commit)
UserConsumerProfile.objects.get_or_create(user=user)
return user
There are a few other "hooks"/functions in the adapters that may we worth to investigate if the save_user doesn't work for your scenario.
The REGISTER_SERIALIZER that you created is only used by the RegisterView.
The social login & connect views use different serializers: SocialLoginSerializer and SocialConnectSerializer, that cannot be overwritten per settings.
I can think of two ways to achieve your desired behavior:
create serializers for the social login & connect views (inherriting the default serializers) and set them as serializer_class for the view,
use Django signals, especially the post_save signal for the User model and when an instance is created, create your UserConsumerProfile.

Prevent user to go to login page after successful login in django

I am using Django-redux for authentication purpose. My question is how can I prevent to go to registration page or login page if the user is authenticated.
settings.py
REGISTRATION_OPEN = True
ACCOUNT_ACTIVATION_DAYS = 7
REGISTRATION_AUTO_LOGIN = True
REGISTRATION_FORM = 'signin.forms.MyRegForm'
LOGIN_REDIRECT_URL = '/signin/user_sign/'
urls.py
from django.conf.urls import url
from signin import views
app_name = 'signin'
urlpatterns = [
url(r'^user_sign/', views.user_sign, name='user_sign'),
]
views.py
def user_sign(request):
print('user_sign')
obj = UserSelection.objects.get(user=request.user)
if obj.user_type == 'candidate':
return redirect('candidate:cand_dash')
else:
return redirect('employer:employer_dash')
In login or registration views, just check that the user is authenticated or not
if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...

mongoengine and django auth, login

I am trying to implement login functionality using mongoengine and django.
I have included 'mongoengine.django.mongo_auth' in INSTALLED_APPS
Following are my settings.py from mongoengine site.
MONGOENGINE_USER_DOCUMENT = 'mongoengine.django.auth.User'
AUTH_USER_MODEL = 'mongo_auth.MongoUser'
SESSION_ENGINE = 'mongoengine.django.sessions'
AUTHENTICATION_BACKENDS = (
'mongoengine.django.auth.MongoEngineBackend',
)
This is from models.py
class UserInfo(User,DynamicDocument):
address = EmbeddedDocumentField(Address)
dob = DateTimeField(required = True)
sex = StringField(max_length = 1, choices = SEX)
primary_number = LongField(required = True)
And following is from views.py
def LoginOrCreateUser(request):
formAuth = AuthenticationForm(data=(request.POST or None))
if(request.method=='POST'):
if(formAuth.is_valid()):
if(formAuth.clean_email()):
if(formAuth.clean_password()):
formAuth.save(True)
user=authenticate(username=formAuth.cleaned_data['username'],password = formAuth.cleaned_data['password1'])
login(request,user)
return HttpResponse('New User Success')
This code gives me error <obj_id "user"> is NOT JSON serializable.
The error is raised for login, so I guess here login API is provided by django but the user we are providing to it is the value got from authenticate which is mongoengine's provided api.
I looked into the auth.py of django and mongoengine. So, we don't have login API in mongoengine. And the authenticate of django returns the user instance, while authenticate of mongoengine returns a string i.e. username.
Any suggestions here or mistakes I am making in the implementation here.
André I just tried you're example but it gives me
NameError at /game/
global name 'jsonResponse' is not defined
If I import jsonResponse like
from django.http import HttpResponse,JsonResponse
then gives me
ImportError at /game/ cannot import name JsonResponse
I just only want to manage simple user auth with my mongoengine backend.
Thanks.
1st question, are you trying to customize the User object? Or you want to use default Django User model? I ask because you should set MONGOENGINE_USER_DOCUMENT and AUTH_USER_MODEL if you do want to make a custom model.
I will show you how I'm authenticating using mongoengine x Django:
On settings.py
connect('localhost')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.dummy',
}
}
AUTHENTICATION_BACKENDS = (
'mongoengine.django.auth.MongoEngineBackend'
)
SESSION_ENGINE = 'mongoengine.django.sessions'
On views.py
from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponse
def jsonResponse(responseDict):
return HttpResponse(simplejson.dumps(responseDict), mimetype='application/json')
def createSession(request):
if not request.is_ajax():
return jsonResponse({'error':True})
if not request.session.exists(request.session.session_key):
request.session.create()
data = extractDataFromPost(request)
email = data["email"]
password = data["password"]
try:
user = User.objects.get(username=email)
if user.check_password(password):
user.backend = 'mongoengine.django.auth.MongoEngineBackend'
user = authenticate(username=email, password=password)
login(request, user)
request.session.set_expiry(3600000) # 1 hour timeout
return jsonResponse(serializeUser(user))
else:
result = {'error':True, 'message':'Invalid credentials'}
return jsonResponse(result)
except User.DoesNotExist:
result = {'error':True, 'message':'Invalid credentials'}
return jsonResponse(result)
def serializeUser(user):
return simplejson.dumps({'email': user.email, 'username': user.username, 'id': str(user.id), 'firstName': user.first_name, 'lastName': user.last_name})
After reading lots of stuff I could make it work this way following MongoEngine User authentication (django).
I'm not setting anything on models.py since I use the default Django user model.
Regards

How to achieve authentication with django-auth-ldap?

I have an app running using django.
Now i want only users that are authenticated via an openldap server to see "their view" (therefore i only need their uid after successfull authentication)
How can i achieve that?
I guess django-auth-ldap is the way to go, so i tried the whole day to get to know where the authentication actually takes place and how i can get the uid of the user requesting a view.
I used the documentation for the settings.py but i could not find out how to "actually use" it. Maybe someone can point me in the right direction?
settings.py:
import ldap
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
AUTH_LDAP_SERVER_URI = "ldap://123.60.56.61"
AUTH_LDAP_BIND_DN = ""
AUTH_LDAP_BIND_PASSWORD = ""
AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,dc=rd,dc=corpintra,dc=net"
(By the way: i already can perform ldap-searche with python-ldap and get results like ldapsearch on the command line, so everything else works just fine...)
What do i need in my views?
Thanks for your help!
Here's a snippet from one of our sites.
# Django Auth Ldap
main_dn = 'dc=____,dc=organisation,dc=com'
groups_dn = 'ou=Groups,'+main_dn
users_dn = 'ou=Users,'+main_dn
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
AUTH_LDAP_SERVER_URI = "ldap://ldap.organisation.com"
AUTH_LDAP_BIND_DN = 'cn=___,'+main_dn
AUTH_LDAP_BIND_PASSWORD = "__________________"
AUTH_LDAP_USER_SEARCH = LDAPSearch(users_dn, 2, "(uid=%(user)s)")
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail"
}
AUTH_LDAP_MIRROR_GROUPS = True
AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_GROUP_TYPE = PosixGroupType()
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(groups_dn, ldap.SCOPE_SUBTREE, "(objectClass=posixGroup)")
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
"is_staff": "cn=admins,"+groups_dn,
"is_superuser": "cn=developers,"+groups_dn,
}
EDIT:
Since the question is "What do i need in my views?", The answer is that this config will save the user's uid as the username field on the User model, so in your views, you need
uid = request.user.username
Hopefully this gets you up and running.
Since django-auth-ldap is a normal Django authentication backend, request.user should be set to the authenticated user (assuming you have the standard middleware installed—see the Django docs). With a typical setup, request.user.username will be the uid of the user's DN. If you need more information, you can get it from request.user.ldap_user.
I'm not use django-auth-ldap, i write my own ldap authentification backend's.
#define your backend authentification
AUTHENTICATION_BACKENDS = (
'netipa.managment.ldapwm.netipaldapdjango.NetIpaLdap',
#'django.contrib.auth.backends.ModelBackend ',
)
For more information about extend the User model, see https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#specifying-a-custom-user-model
#!/usr/bin/env python
#coding:utf-8
# Author: peter --<pjl#hpc.com.py>
# Created: 22/04/12
from django.conf import settings
import ldap
#this is a abstrac class to add some custom fields to the default django User model
#see https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#specifying-a-custom-user-model, for more informacion
from netipa.contrib.accesos.models import LdapUsers as User
from django.contrib.auth.backends import ModelBackend
#import logging
class NetIpaLdap(object):
supports_inactive_user = False
def authenticate(self, username=None, password=None):
# logging.basicConfig(format='%(asctime)s %(message)s',filename="/tmp/auth.log",level=logging.DEBUG)
if username is None:
return None
try:
# a variable's define in settings
ip_server = settings.LDAP_BASES.get('ip')
userdn = settings.LDAP_BASES.get('users')
ldap.initialize('ldap://%s' % ip_server)
lop = ldap.simple_bind_s(
"uid=%s,%s" % (username, userdn),
password
)
except ldap.LDAPError, e:
print e
return None
except Exception,e:
print e
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
ldap_at = lop.search(settings.LDAP_BASES.get('users'),
fil='uid=%s' % username,
types=1,
attr=['uidnumber', 'mail'])
user = User(username=username, password=password, ldap_id=ldap_at[0][-1].get('uidnumber')[0],
ldap_mail=ldap_at[0][-1].get('mail')[0])
user.is_staff = True
user.is_superuser = True
user.save()
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Here is my extend User Class Model
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class LdapUsers(AbstractUser):
ldap_id = models.IntegerField()
ldap_mail = models.EmailField()