]> git.parisson.com Git - django-social-auth.git/commitdiff
Improved user creation handling, closes gh-3
authorMatías Aguirre <matiasaguirre@gmail.com>
Mon, 27 Dec 2010 19:48:50 +0000 (17:48 -0200)
committerMatías Aguirre <matiasaguirre@gmail.com>
Mon, 27 Dec 2010 19:48:50 +0000 (17:48 -0200)
README.rst
example/app/models.py
example/app/views.py
example/settings.py
social_auth/auth.py
social_auth/backends.py
social_auth/models.py
social_auth/views.py

index 873b03c9c669152cb31cfb92f6eea66fa981795d..80c08e0baa0c637a5b2e276c0ce1f15ee5302118 100644 (file)
@@ -13,8 +13,8 @@ third parties.
 --------
 Features
 --------
-This app provides user registration and login using social sites credentials,
-some features are:
+This application provides user registration and login using social sites
+credentials, some features are:
 
 - Registration and Login using social sites using the following providers
   at the moment:
@@ -50,7 +50,7 @@ Dependencies that must be meet to use the app:
 ------------
 Installation
 ------------
-- Add social_auth app to PYTHONPATH and installed apps::
+- Add social_auth to PYTHONPATH and installed applications::
 
     INSTALLED_APPS = (
         ...
@@ -63,6 +63,8 @@ Installation
         'social_auth.backends.TwitterOAuthBackend',
         'social_auth.backends.FacebookOAuthBackend',
         'social_auth.backends.OrkutOAuthBackend',
+        'social_auth.backends.GoogleOpenIDBackend',
+        'social_auth.backends.YahooOpenIDBackend',
         'social_auth.backends.OpenIDBackend',
         'django.contrib.auth.backends.ModelBackend',
     )
@@ -76,13 +78,13 @@ Installation
     ORKUT_CONSUMER_KEY      = ''
     ORKUT_CONSUMER_SECRET   = ''
 
-- Setup login urls::
+- Setup login URLs::
 
     LOGIN_URL          = '/login-form/'
     LOGIN_REDIRECT_URL = '/logged-in/'
     LOGIN_ERROR_URL    = '/login-error/'
 
-  Check Django documentation at `Login url`_ and `Login redirect url`_
+  Check Django documentation at `Login URL`_ and `Login redirect URL`_
 
 - Configure authentication and association complete URL names to avoid
   possible clashes::
@@ -90,7 +92,7 @@ Installation
     SOCIAL_AUTH_COMPLETE_URL_NAME  = 'namespace:complete'
     SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'namespace:association_complete'
 
-- Add urls entries::
+- Add URLs entries::
 
     urlpatterns = patterns('',
         ...
@@ -118,40 +120,37 @@ Installation
 
     SOCIAL_AUTH_EXTRA_DATA = False
 
-- It's possible to override the used User class if needed::
+- It's possible to override the used User model if needed::
 
     SOCIAL_AUTH_USER_MODEL = 'myapp.CustomUser'
 
-  this class must define the following fields::
+  This class *must* have a custom `Model Manager`_ with a create_user method
+  that resembles the one on `auth.UserManager`_.
+
+  Also, it's highly recommended that this class define the following fields::
 
     username   = CharField(...)
-    email      = EmailField(...)
-    password   = CharField(...)
     last_login = DateTimeField(blank=True)
     is_active  = BooleanField(...)
 
-  and the methods::
-
-    is_authenticated()
-
-  These are needed to ensure django-auth integration. AttributeError is raised
-  if any of these are missing.
+  and the method::
 
-  Also the following are recommended but not enforced::
+    is_authenticated():
+        ...
 
-    first_name = CharField(...)
-    last_name  = CharField(...)
+  These are needed to ensure a better django-auth integration, in other case
+  `login_required`_ won't be usable. A warning is displayed if any of these are
+  missing. By default `auth.User`_ is used.
 
-  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.
+  Check example application for implementation details, but first, please take
+  a look to `User Profiles`_, it might be what you were looking for.
 
 
 -------
 Signals
 -------
 A pre_update signal is sent when user data is about to be updated with new
-values from auth service provider, this apply to new users and already
+values from authorization service provider, this apply to new users and already
 existent ones. This is useful to update custom user fields or `User Profiles`_,
 for example, to store user gender, location, etc. Example::
 
@@ -190,7 +189,7 @@ Twitter offers per application keys named "Consumer Key" and
 "Consumer Secret". To enable Twitter these two keys are needed.
 Further documentation at `Twitter development resources`_:
 
-- Register a new app at `Twitter App Creation`_,
+- Register a new application at `Twitter App Creation`_,
 
 - mark the "Yes, use Twitter for login" checkbox, and
 
@@ -199,7 +198,7 @@ Further documentation at `Twitter development resources`_:
       TWITTER_CONSUMER_KEY
       TWITTER_CONSUMER_SECRET
 
-- You don't need to specify the url callback
+- You don't need to specify the URL callback
 
 
 --------
@@ -209,7 +208,7 @@ Facebook works similar to Twitter but it's simpler to setup and
 redirect URL is passed as a parameter when issuing an authorization.
 Further documentation at `Facebook development resources`_:
 
-- Register a new app at `Facebook App Creation`_, and
+- Register a new application at `Facebook App Creation`_, and
 
 - fill "App Id" and "App Secret" values in values::
 
@@ -262,22 +261,25 @@ Base work is copyrighted by:
     Copyright (C) 2007 Simon Willison
     Copyright (C) 2008-2010 Canonical Ltd.
 
+.. _Model Manager: http://docs.djangoproject.com/en/dev/topics/db/managers/#managers
+.. _Login URL: http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#login-url
+.. _Login redirect URL: http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#login-redirect-url
+.. _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
+.. _auth.UserManager: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py#L114
+.. _login_required: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/decorators.py#L39
+.. _User Profiles: http://www.djangobook.com/en/1.0/chapter12/#cn222
 .. _OpenId: http://openid.net/
 .. _OAuth: http://oauth.net/
 .. _django-twitter-oauth: https://github.com/henriklied/django-twitter-oauth
 .. _django-openid-auth: https://launchpad.net/django-openid-auth
 .. _python-openid: http://pypi.python.org/pypi/python-openid/
 .. _python-oauth: https://github.com/leah/python-oauth
-.. _Login url: http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#login-url
-.. _Login redirect url: http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#login-redirect-url
 .. _Twitter development resources: http://dev.twitter.com/pages/auth
 .. _Twitter App Creation: http://twitter.com/apps/new
 .. _dnsmasq: http://www.thekelleys.org.uk/dnsmasq/doc.html
 .. _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
 .. _Google support: http://www.google.com/support/a/bin/answer.py?hl=en&answer=162105
 .. _Orkut API:  http://code.google.com/apis/orkut/docs/rest/developers_guide_protocol.html#Authenticating
index 45ecf243595fe8b18fa42dbb6785ddfb8b15e115..8c28f48f6f58114dfbd0343ad6a69480852ee7f7 100644 (file)
@@ -1,15 +1,17 @@
 # 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
+from django.db import models
+
+
+class CustomUserManager(models.Manager):
+    def create_user(self, username, email):
+        return self.model._default_manager.create(username=username)
+
+
+class CustomUser(models.Model):
+    username = models.CharField(max_length=128)
+    last_login = models.DateTimeField(blank=True, null=True)
+
+    objects = CustomUserManager()
+
+    def is_authenticated(self):
+        return True
index 57cf0517b9467991d09e39ce4750ac20b65c944a..690c0695440b7d898b2277f1399e305eb7f2b5de 100644 (file)
@@ -5,6 +5,8 @@ from django.template import Template, Context, RequestContext
 
 
 def home(request):
+    if request.user.is_authenticated():
+        return HttpResponseRedirect('done')
     return HttpResponse(Template(
     """
     <html>
@@ -44,6 +46,7 @@ def home(request):
 
 @login_required
 def done(request):
+    names = request.user.social_auth.values_list('provider', flat=True)
     return HttpResponse(Template(
     """
     <html>
@@ -65,11 +68,11 @@ def done(request):
         <h2>Associate new credentials:</h2>
         <div>
           <ul>
-            <li><a href="/associate/twitter/">Twitter</a></li>
-            <li><a href="/associate/facebook/">Facebook</a></li>
-            <li><a href="/associate/orkut/">Orkut</a></li>
-            <li><a href="/associate/google/">Google</a></li>
-            <li><a href="/associate/yahoo/">Yahoo</a></li>
+            <li><a href="/associate/twitter/">Twitter</a> {% if twitter %}(associated){% endif %}</li>
+            <li><a href="/associate/facebook/">Facebook</a> {% if facebook %}(associated){% endif %}</li>
+            <li><a href="/associate/orkut/">Orkut</a> {% if orkut %}(associated){% endif %}</li>
+            <li><a href="/associate/google/">Google</a> {% if google %}(associated){% endif %}</li>
+            <li><a href="/associate/yahoo/">Yahoo</a> {% if yahoo %}(associated){% endif %}</li>
             <li>
               <form action="/associate/openid/" method="post">{% csrf_token %}
                 <label for="openid_identifier">Other provider:</label>
@@ -81,7 +84,8 @@ def done(request):
         </div>
       </body>
     </html>
-    """).render(RequestContext(request)),
+    """).render(RequestContext(request, dict((name.lower(), True)
+                                                for name in names))),
     content_type='text/html;charset=UTF-8')
 
 
index c99965eb0f5a3684e4c049c194fab5e2986a8137..efb2b2cdb3006b3837ec5a276b95742ba7ef43c6 100644 (file)
@@ -95,6 +95,8 @@ INSTALLED_APPS = (
 AUTHENTICATION_BACKENDS = (
     'social_auth.backends.TwitterBackend',
     'social_auth.backends.FacebookBackend',
+    'social_auth.backends.GoogleBackend',
+    'social_auth.backends.YahooBackend',
     'social_auth.backends.OpenIDBackend',
     'django.contrib.auth.backends.ModelBackend',
 )
index c3ea7a9770d937184f4cea75183647d01673c057..f91d55d192a9c7755c087148297ead57acfdcba5 100644 (file)
@@ -15,7 +15,7 @@ from django.contrib.auth import authenticate
 
 from .store import DjangoOpenIDStore
 from .backends import TwitterBackend, OrkutBackend, FacebookBackend, \
-                      OpenIDBackend
+                      OpenIDBackend, GoogleBackend, YahooBackend
 from .conf import AX_ATTRS, SREG_ATTR, OPENID_ID_FIELD, SESSION_NAME, \
                   OPENID_GOOGLE_URL, OPENID_YAHOO_URL, TWITTER_SERVER, \
                   TWITTER_REQUEST_TOKEN_URL, TWITTER_ACCESS_TOKEN_URL, \
@@ -54,7 +54,12 @@ class BaseAuth(object):
 
 
 class OpenIdAuth(BaseAuth):
-    """OpenId process handling"""
+    """
+    OpenId process handling
+        @AUTH_BACKEND   Authorization backend related with this service
+    """
+    AUTH_BACKEND = OpenIDBackend
+
     def auth_url(self):
         openid_request = self.setup_request()
         # Construct completion URL, including page we should redirect to
@@ -78,7 +83,7 @@ class OpenIdAuth(BaseAuth):
         if not response:
             raise ValueError, 'This is an OpenID relying party endpoint'
         elif response.status == SUCCESS:
-            kwargs.update({'response': response, OpenIDBackend.name: True})
+            kwargs.update({'response': response, self.AUTH_BACKEND.name: True})
             return authenticate(*args, **kwargs)
         elif response.status == FAILURE:
             raise ValueError, 'OpenID authentication failed: %s' % response.message
@@ -140,6 +145,8 @@ class OpenIdAuth(BaseAuth):
 
 class GoogleAuth(OpenIdAuth):
     """Google OpenID authentication"""
+    AUTH_BACKEND = GoogleBackend
+
     def openid_url(self):
         """Return Google OpenID service url"""
         return OPENID_GOOGLE_URL
@@ -147,6 +154,8 @@ class GoogleAuth(OpenIdAuth):
 
 class YahooAuth(OpenIdAuth):
     """Yahoo OpenID authentication"""
+    AUTH_BACKEND = YahooBackend
+
     def openid_url(self):
         """Return Yahoo OpenID service url"""
         return OPENID_YAHOO_URL
index c921aaf15e9c6ee08795d9491c0030632a69f88a..f4ae7dde699079c00f3630656ba270071ee4b253 100644 (file)
@@ -7,12 +7,13 @@ from openid.extensions import ax, sreg
 
 from django.conf import settings
 from django.contrib.auth.backends import ModelBackend
-from django.contrib.auth.models import UNUSABLE_PASSWORD
 
 from .models import UserSocialAuth
 from .conf import OLD_AX_ATTRS, AX_SCHEMA_ATTRS
 from .signals import pre_update
 
+USERNAME = 'username'
+
 # get User class, could not be auth.User
 User = UserSocialAuth._meta.get_field('user').rel.to
 
@@ -38,20 +39,28 @@ class SocialAuthBackend(ModelBackend):
         response = kwargs.get('response')
         details = self.get_user_details(response)
         uid = self.get_user_id(details, response)
+        new_user = False
         try:
-            auth_user = UserSocialAuth.objects.select_related('user')\
-                                              .get(provider=self.name,
-                                                   uid=uid)
+            social_user = UserSocialAuth.objects.select_related('user')\
+                                                .get(provider=self.name,
+                                                     uid=uid)
         except UserSocialAuth.DoesNotExist:
-            if not getattr(settings, 'SOCIAL_AUTH_CREATE_USERS', False):
-                return None
-            user = self.create_user(details=details, *args, **kwargs)
+            user = kwargs.get('user')
+            if user is None: # new user
+                if not getattr(settings, 'SOCIAL_AUTH_CREATE_USERS', True):
+                    return None
+                username = self.username(details)
+                email = details.get('email')
+                user = User.objects.create_user(username=username, email=email)
+                new_user = True
+            social_user = self.associate_auth(user, uid, response, details)
         else:
-            user = auth_user.user
-            self.update_user_details(user, response, details)
+            user = social_user.user
+
+        self.update_user_details(user, response, details, new_user=new_user)
         return user
 
-    def get_username(self, details):
+    def username(self, details):
         """Return an unique username, if SOCIAL_AUTH_FORCE_RANDOM_USERNAME
         setting is True, then username will be a random 30 chars md5 hash
         """
@@ -61,8 +70,8 @@ class SocialAuthBackend(ModelBackend):
 
         if getattr(settings, 'SOCIAL_AUTH_FORCE_RANDOM_USERNAME', False):
             username = get_random_username()
-        elif 'username' in details:
-            username = details['username']
+        elif USERNAME in details:
+            username = details[USERNAME]
         elif hasattr(settings, 'SOCIAL_AUTH_DEFAULT_USERNAME'):
             username = settings.SOCIAL_AUTH_DEFAULT_USERNAME
             if callable(username):
@@ -81,54 +90,27 @@ class SocialAuthBackend(ModelBackend):
                 break
         return username
 
-    def create_user(self, response, details, *args, **kwargs):
-        """Create user with unique username. New social credentials are
-        associated with @user if this parameter is not None."""
-        user = kwargs.get('user')
-        if user is None: # create user, otherwise associate the new credential
-            username = self.get_username(details)
-            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)
-
-        # update details and associate account with social credentials
-        self.update_user_details(user, response, details)
-        self.associate_auth(user, response, details)
-        return user
-
-    def associate_auth(self, user, response, details):
-        """Associate an OAuth with a user account."""
-        # Check to see if this OAuth has already been claimed.
-        uid = self.get_user_id(details, response)
-        try:
-            user_social = UserSocialAuth.objects.select_related('user')\
-                                                .get(provider=self.name,
-                                                     uid=uid)
-        except UserSocialAuth.DoesNotExist:
-            if getattr(settings, 'SOCIAL_AUTH_EXTRA_DATA', True):
-                extra_data = self.extra_data(user, uid, response, details)
-            else:
-                extra_data = ''
-            user_social = UserSocialAuth.objects.create(user=user, uid=uid,
-                                                        provider=self.name,
-                                                        extra_data=extra_data)
-        else:
-            if user_social.user != user:
-                raise ValueError, 'Identity already claimed'
-        return user_social
+    def associate_auth(self, user, uid, response, details):
+        """Associate a Social Auth with an user account."""
+        extra_data = '' if not getattr(settings, 'SOCIAL_AUTH_EXTRA_DATA',
+                                       False) \
+                        else self.extra_data(user, uid, response, details)
+        return UserSocialAuth.objects.create(user=user, uid=uid,
+                                             provider=self.name,
+                                             extra_data=extra_data)
 
     def extra_data(self, user, uid, response, details):
         """Return default blank user extra data"""
         return ''
 
-    def update_user_details(self, user, response, details):
-        """Update user details with new (maybe) data"""
+    def update_user_details(self, user, response, details, new_user=False):
+        """Update user details with (maybe) new data. Username is not
+        changed if associating a new credential."""
         changed = False
         for name, value in details.iteritems():
+            # not update username if user already exists
+            if not new_user and name == USERNAME:
+                continue
             if value and value != getattr(user, name, value):
                 setattr(user, name, value)
                 changed = True
@@ -137,7 +119,7 @@ class SocialAuthBackend(ModelBackend):
         # user instance (created or retrieved from database), service
         # response and processed details, signal handlers must return
         # True or False to signal that something has changed
-        updated = filter(bool, pre_update.send(sender=self, user=user,
+        updated = filter(None, pre_update.send(sender=self, user=user,
                                                response=response,
                                                details=details))
         if changed or len(updated) > 0:
@@ -149,8 +131,8 @@ class SocialAuthBackend(ModelBackend):
 
     def get_user_details(self, response):
         """Must return user details in a know internal struct:
-            {'email': <user email if any>,
-             'username': <username if any>,
+            {USERNAME: <username if any>,
+             'email': <user email if any>,
              'fullname': <user full name if any>,
              'first_name': <user first name if any>,
              'last_name': <user last name if any>}
@@ -182,8 +164,8 @@ class TwitterBackend(OAuthBackend):
 
     def get_user_details(self, response):
         """Return user details from Twitter account"""
-        return {'email': '', # not supplied
-                'username': response['screen_name'],
+        return {USERNAME: response['screen_name'],
+                'email': '', # not supplied
                 'fullname': response['name'],
                 'first_name': response['name'],
                 'last_name': ''}
@@ -195,8 +177,8 @@ class OrkutBackend(OAuthBackend):
 
     def get_user_details(self, response):
         """Return user details from Orkut account"""
-        return {'email': response['emails'][0]['value'],
-                'username': response['displayName'],
+        return {USERNAME: response['displayName'],
+                'email': response['emails'][0]['value'],
                 'fullname': response['displayName'],
                 'firstname': response['name']['givenName'],
                 'lastname': response['name']['familyName']}
@@ -208,8 +190,8 @@ class FacebookBackend(OAuthBackend):
 
     def get_user_details(self, response):
         """Return user details from Facebook account"""
-        return {'email': response.get('email', ''),
-                'username': response['name'],
+        return {USERNAME: response['name'],
+                'email': response.get('email', ''),
                 'fullname': response['name'],
                 'first_name': response.get('first_name', ''),
                 'last_name': response.get('last_name', '')}
@@ -225,11 +207,8 @@ class OpenIDBackend(SocialAuthBackend):
 
     def get_user_details(self, response):
         """Return user details from an OpenID request"""
-        values = {'email': '',
-                  'username': '',
-                  'fullname': '',
-                  'first_name': '',
-                  'last_name': ''}
+        values = {USERNAME: '', 'email': '', 'fullname': '',
+                  'first_name': '', 'last_name': ''}
 
         resp = sreg.SRegResponse.fromSuccessResponse(response)
         if resp:
@@ -254,9 +233,17 @@ class OpenIDBackend(SocialAuthBackend):
             except ValueError:
                 last_name = fullname
 
-        values.update({'fullname': fullname,
-                       'first_name': first_name,
+        values.update({'fullname': fullname, 'first_name': first_name,
                        'last_name': last_name,
-                       'username': values.get('username') or \
+                       USERNAME: values.get(USERNAME) or \
                                    (first_name.title() + last_name.title())})
         return values
+
+
+class GoogleBackend(OpenIDBackend):
+    """Google OpenID authentication backend"""
+    name = 'google'
+
+class YahooBackend(OpenIDBackend):
+    """Yahoo OpenID authentication backend"""
+    name = 'yahoo'
index 8ef3a96f58e8cc2c8596b5e51c20119bc9cb6c07..2008dafbfb88c28abbb816ae0dc6415bd9c2b875 100644 (file)
@@ -1,30 +1,33 @@
 """Social auth models"""
+import warnings
+
 from django.db import models
 from django.conf import settings
 
 # If User class is overrided, it must provide the following fields,
 # or it won't be playing nicely with auth module:
 #
-#   username = CharField()
-#   email = EmailField()
-#   password = CharField()
+#   username   = CharField()
+#   last_login = DateTimeField()
 #   is_active  = BooleanField()
 #
 # and methods:
 #
 #   def is_authenticated():
 #       ...
-MANDATORY_FIELDS = ('username', 'email', 'password', 'last_login')
-MANDATORY_METHODS = ('is_authenticated',)
+RECOMMENDED_FIELDS = ('username', 'last_login', 'is_active')
+RECOMMENDED_METHODS = ('is_authenticated',)
 
-try: # try to import User model override and validate needed fields
+if getattr(settings, 'SOCIAL_AUTH_USER_MODEL', None):
     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'
-    if not all(callable(getattr(User, name, None))
-                    for name in MANDATORY_METHODS):
-        raise AttributeError, 'Some mandatory methods missing'
-except AttributeError: # fail silently on missing setting
+    missing = list(set(RECOMMENDED_FIELDS) - 
+                   set(User._meta.get_all_field_names())) + \
+              [name for name in RECOMMENDED_METHODS
+                      if not callable(getattr(User, name, None))]
+    if missing:
+        warnings.warn('Missing recommended attributes or methods '\
+                      'in custom User model: "%s"' % ', '.join(missing))
+else:
     from django.contrib.auth.models import User
 
 
@@ -35,6 +38,10 @@ class UserSocialAuth(models.Model):
     uid = models.CharField(max_length=255)
     extra_data = models.TextField(default='', blank=True)
 
+    def __unicode__(self):
+        """Return associated user unicode representation"""
+        return unicode(self.user)
+
     class Meta:
         """Meta data"""
         unique_together = ('provider', 'uid')
index bb476891bdf5430b3a18f0a5f31ac4b05cedc2a0..c0e8500d13ff48ab128b77a793a85960481d9dd2 100644 (file)
@@ -34,7 +34,7 @@ def complete(request, backend):
         return HttpResponseServerError('Incorrect authentication service')
     backend = BACKENDS[backend](request, request.path)
     user = backend.auth_complete()
-    if user and user.is_active:
+    if user and getattr(user, 'is_active', True):
         login(request, user)
         url = request.session.pop(REDIRECT_FIELD_NAME, '') or \
               getattr(settings, 'LOGIN_REDIRECT_URL', '')
@@ -58,7 +58,7 @@ def associate_complete(request, backend):
     if backend not in BACKENDS:
         return HttpResponseServerError('Incorrect authentication service')
     backend = BACKENDS[backend](request, request.path)
-    user = backend.auth_complete(user=request.user)
+    backend.auth_complete(user=request.user)
     url = request.session.pop(REDIRECT_FIELD_NAME, '') or \
           getattr(settings, 'LOGIN_REDIRECT_URL', '')
     return HttpResponseRedirect(url)