final user name will have an integer suffix in case it's already taken.
-- OAuth authentication will store access_token by default, set this value to False to avoid such behavior::
+- OAuth authentication will store access_token by default, set this value
+ to False to avoid such behavior::
SOCIAL_AUTH_EXTRA_DATA = False
+- It's possible to override the used User class if needed::
+
+ SOCIAL_AUTH_USER_MODEL = 'myapp.CustomUser'
+
+this class must define the following fields::
+
+ username = CharField(...)
+ email = EmailField(...)
+ password = CharField(...)
+ last_login = DateTimeField(blank=True)
+ is_active = BooleanField(...)
+
+and the methods::
+
+ is_authenticated()
+
+AttributeError will be raised in case of any of these is
+missing, also the following are recommended but notenforced::
+
+ first_name = CharField(...)
+ last_name = CharField(...)
+
+by default `auth.User`_ is used. Check example application for
+implementation details, but first, please take a look to `User Profiles`_,
+it might solve your case.
+
------
OpenId
Several, maybe, please report :-)
+------------
+Contributors
+------------
+
+Attributions to whom deserves:
+
+- caioariede_ (Caio Ariede)
+
+
----------
Copyrights
----------
.. _Facebook development resources: http://developers.facebook.com/docs/authentication/
.. _Facebook App Creation: http://developers.facebook.com/setup/
.. _AUTHENTICATION_BACKENDS: http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#authentication-backends
+.. _auth.User: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py#L186
+.. _User Profiles: http://www.djangobook.com/en/1.0/chapter12/#cn222
+.. _caioariede: https://github.com/caioariede
--- /dev/null
+# Define a custom User class to work with django-social-auth
+#
+# from django.db import models
+#
+# class CustomUser(models.Model):
+# username = models.CharField(max_length=128)
+# email = models.EmailField()
+# password = models.CharField(max_length=128)
+# last_login = models.DateTimeField(blank=True, null=True)
+# first_name = models.CharField(max_length=128, blank=True)
+# last_name = models.CharField(max_length=128, blank=True)
+# is_active = models.BooleanField(default=True)
+#
+# def is_authenticated(self):
+# return True
SOCIAL_AUTH_DEFAULT_USERNAME = 'socialauth_user'
SOCIAL_AUTH_COMPLETE_URL_NAME = 'social:complete'
LOGIN_ERROR_URL = '/login/error/'
+#SOCIAL_AUTH_USER_MODEL = 'app.CustomUser'
'django.contrib.messages',
'django.contrib.admin',
'social_auth',
+ 'app',
)
AUTHENTICATION_BACKENDS = (
return {'email': '', # not supplied
'username': response['screen_name'],
'fullname': response['name'],
- 'firstname': response['name'],
- 'lastname': ''}
+ 'first_name': response['name'],
+ 'last_name': ''}
class FacebookBackend(OAuthBackend):
return {'email': response.get('email', ''),
'username': response['name'],
'fullname': response['name'],
- 'firstname': response.get('first_name', ''),
- 'lastname': response.get('last_name', '')}
+ 'first_name': response.get('first_name', ''),
+ 'last_name': response.get('last_name', '')}
class OpenIDBackend(SocialAuthBackend):
values = {'email': '',
'username': '',
'fullname': '',
- 'firstname': '',
- 'lastname': ''}
+ 'first_name': '',
+ 'last_name': ''}
resp = sreg.SRegResponse.fromSuccessResponse(response)
if resp:
for src, alias in OLD_AX_ATTRS + AX_SCHEMA_ATTRS)
fullname = values.get('fullname') or ''
- firstname = values.get('firstname') or ''
- lastname = values.get('lastname') or ''
+ first_name = values.get('first_name') or ''
+ last_name = values.get('last_name') or ''
- if not fullname and firstname and lastname:
- fullname = firstname + ' ' + lastname
+ if not fullname and first_name and last_name:
+ fullname = first_name + ' ' + last_name
elif fullname:
try: # Try to split name for django user storage
- firstname, lastname = fullname.rsplit(' ', 1)
+ first_name, last_name = fullname.rsplit(' ', 1)
except ValueError:
- lastname = fullname
+ last_name = fullname
values.update({'fullname': fullname,
- 'firstname': firstname,
- 'lastname': lastname,
+ 'first_name': first_name,
+ 'last_name': last_name,
'username': values.get('username') or \
- (firstname.title() + lastname.title())})
+ (first_name.title() + last_name.title())})
return values
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
+from django.contrib.auth.models import UNUSABLE_PASSWORD
-from .models import UserSocialAuth, get_user_model
+from .models import UserSocialAuth
+
+# get User class, could not be auth.User
+User = UserSocialAuth._meta.get_field('user').rel.to
-User = get_user_model()
class BaseAuth(object):
"""Base authentication class, new authenticators should subclass
def create_user(self, response, details):
"""Create user with unique username"""
username = self.get_username(details)
- user = User.objects.create_user(username, details.get('email', ''))
+ email = details.get('email', '')
+
+ if hasattr(User.objects, 'create_user'): # auth.User
+ user = User.objects.create_user(username, email)
+ else: # create user setting password to an unusable value
+ user = User.objects.create(username=username, email=email,
+ password=UNUSABLE_PASSWORD)
+
self.update_user_details(user, details) # load details
self.associate_auth(user, response, details) # save account association
return user
def update_user_details(self, user, details):
"""Update user details with new (maybe) data"""
- first_name = details.get('firstname') or user.first_name
- last_name = details.get('lastname') or user.last_name
- email = details.get('email') or user.email
- if (user.first_name, user.last_name, user.email) != (first_name, last_name, email):
- user.first_name = first_name
- user.last_name = last_name
- user.email = email
+ fields = user._meta.get_all_field_names()
+ changed = False
+
+ for name in ('first_name', 'last_name', 'email'):
+ value = details.get(name)
+ if name in fields and value != getattr(user, name, value):
+ setattr(user, name, value)
+ changed = True
+
+ if changed:
user.save()
def get_user_id(self, details, response):
{'email': <user email if any>,
'username': <username if any>,
'fullname': <user full name if any>,
- 'firstname': <user first name if any>,
- 'lastname': <user last name if any>}
+ 'first_name': <user first name if any>,
+ 'last_name': <user last name if any>}
"""
raise NotImplementedError, 'Implement in subclass'
+
+ def get_user(self, user_id):
+ """Return user instance for @user_id"""
+ try:
+ return User.objects.get(pk=user_id)
+ except User.DoesNotExist:
+ return None
# providers offer one but not the other.
('http://axschema.org/contact/email', 'email'),
('http://axschema.org/namePerson', 'fullname'),
- ('http://axschema.org/namePerson/first', 'firstname'),
- ('http://axschema.org/namePerson/last', 'lastname'),
+ ('http://axschema.org/namePerson/first', 'first_name'),
+ ('http://axschema.org/namePerson/last', 'last_name'),
('http://axschema.org/namePerson/friendly', 'nickname'),
]
AX_ATTRS = AX_SCHEMA_ATTRS + OLD_AX_ATTRS
from django.db import models
from django.conf import settings
-def get_user_model():
- """Allow setting a custom (extended) user model"""
- return models.get_model(*getattr(settings, 'SOCIAL_AUTH_USER_MODEL', 'auth.User').split('.'))
+# If User class is overrided, it must provide the following fields:
+# username = CharField()
+# email = EmailField()
+# password = CharField()
+MANDATORY_FIELDS = ('username', 'email', 'password', 'last_login')
+
+try: # try to import User model override and validate needed fields
+ User = models.get_model(*settings.SOCIAL_AUTH_USER_MODEL.split('.'))
+ if not all(User._meta.get_field(name) for name in MANDATORY_FIELDS):
+ raise AttributeError, 'Some mandatory field missing'
+except: # fail silently and fallback to auth.User on any error
+ from django.contrib.auth.models import User
-User = get_user_model()
class UserSocialAuth(models.Model):
"""Social Auth association model"""