19

In django-social-auth, there are a few instances where a backend will raise a ValueError (such as when a user cancels a login request or if a user tries to associate with an account that's already been associated with another User). If a User runs into one of these scenarios, they'll be presented with a 500 error on your site.

So, what's the best way to catch these? I'd prefer to be able to display a useful message (via the messages framework) when this happens, but I'm at a loss as to the best way to do this.

I'm thinking about writing my own view (in a separate app) that just wraps social_auth's associate_complete view, but this seems clunky... any ideas?

I could fork django-social-auth and customize this behavior, but I'd prefer not to maintain a separate fork--especially since I can't assume everone would want to handle these Exceptions in the same manner.

neves
  • 20,547
  • 15
  • 108
  • 137
Brad Montgomery
  • 2,501
  • 1
  • 22
  • 24

4 Answers4

19

Rather old question but worth mention that recent version of DSA supports a custom exception processor where you can do whatever you want with the exception message. The default version stores them in the messages app.

Also the exceptions are differentiated now instead of the not-useful ValueError used. Check the docs http://django-social-auth.readthedocs.org/en/latest/configuration.html.

Update (13/08/2013):

Since I've posted the above things have changed, now DSA has an exception middleware that when enabled stores the exception message in the jango builtin messages app. It's preferable to subclass the middleware and add the custom behavior to it. Check the doc at http://django-social-auth.readthedocs.org/en/latest/configuration.html#exceptions-middleware

Sample middleware:

# -*- coding: utf-8 -*-
from social_auth.middleware import SocialAuthExceptionMiddleware
from social_auth.exceptions import AuthFailed
from django.contrib import messages

class CustomSocialAuthExceptionMiddleware( SocialAuthExceptionMiddleware):

    def get_message(self, request, exception):
        msg = None
        if (isinstance(exception, AuthFailed) and 
            exception.message == u"User not allowed"):
            msg =   u"Not in whitelist" 
        else:
            msg =   u"Some other problem"    
        messages.add_message(request, messages.ERROR, msg)     
dani herrera
  • 39,746
  • 4
  • 87
  • 153
omab
  • 3,471
  • 15
  • 23
  • That's a move in the right direction, but still the custom processing function [cannot return an URL used in the subsequent redirection](https://github.com/omab/django-social-auth/blob/1fdb665b37bfc0de9871c2ea6031ed0a54ee3e6f/social_auth/views.py#L67).. – Tomasz Zieliński Mar 13 '12 at 18:07
  • Hello @omab, I'm also interested in the 'official way' to handle exceptions in DSA v0.7.25. I don't understand how to use the custom exception processor, can you give us an example please? Thanks in advance. – Cartucho Jul 24 '13 at 20:05
  • An update to my answer, since I've posted it things have changed, now DSA has an exception middleware that when enabled stores the exception message in the jango builtin messages app. It's preferable to subclass the middleware and add the custom behavior to it. Check the doc at http://django-social-auth.readthedocs.org/en/latest/configuration.html#exceptions-middleware – omab Jul 25 '13 at 17:22
  • I've recently installed DSA in a new project, and I really like where things have gone. Great Work! As for this question, I'd like to update it so that people know about the new changes, but I'm hesitant to change the original question. Should I just link to your answer/updated comment? Thoughts? – Brad Montgomery Aug 28 '13 at 14:24
  • @omab, I have posted a sample custom middleware, thanks a lot to share your work! Please, remove if it is not a nice sample. – dani herrera Sep 07 '13 at 14:17
  • Caution, this will not work if INSTALLED_APPS includes 'debug_toolbar' as it wraps the middleware and prevents the exception from percolating through the process_exception functions – Catskul May 24 '14 at 06:10
13

I've ecountered the same problem and it seems, that creating wrapper view is the best way to handle this situation, at this point, atleast. Here is how I had mine done:

def social_auth_login(request, backend):
    """
        This view is a wrapper to social_auths auth
        It is required, because social_auth just throws ValueError and gets user to 500 error
        after every unexpected action. This view handles exceptions in human friendly way.
        See https://convore.com/django-social-auth/best-way-to-handle-exceptions/
    """
    from social_auth.views import auth

    try:
        # if everything is ok, then original view gets returned, no problem
        return auth(request, backend)
    except ValueError, error:
        # in case of errors, let's show a special page that will explain what happened
        return render_to_response('users/login_error.html',
                                  locals(),
                                  context_instance=RequestContext(request))

You will have to setup url for it:

urlpatterns = patterns('',
    # ...
    url(r'^social_auth_login/([a-z]+)$',  social_auth_login, name='users-social-auth-login'), 
)

And then use it as before in template:

<a href="{% url 'users-social-auth-login' "google" %}">Log in with Google</a>

Hope this helps, even aftern two months after question was asked :)

Silver Light
  • 37,827
  • 29
  • 116
  • 159
  • 1
    Thanks for the answer! This is essentially what I did. For comparison, I'll add an answer containing the code I wrote to wrap associate_complete. – Brad Montgomery Aug 26 '11 at 02:43
6

You need add social auth middleware:

MIDDLEWARE_CLASSES += ('social_auth.middleware.SocialAuthExceptionMiddleware',)

If any error occurs user will be redirected to erorr url(LOGIN_ERROR_URL from settings).

For detailed explanation please see documentation: http://django-social-auth.readthedocs.org/en/latest/configuration.html#exceptions-middleware

Yevgeniy Shchemelev
  • 3,341
  • 1
  • 26
  • 37
  • 1
    FYI: This solution works for me with the caveat of setting DEBUG to False, if it's True you will see the exception and no redirect will happen. Apparently django_social_auth (0.7.22) is not doing a good job reading that flag. Commenting here since it made me spend 1 hour to figure that out in my dev environment. – Arjuna Del Toso Jan 19 '14 at 23:30
  • Docs have moved to https://python-social-auth-docs.readthedocs.io/en/latest/configuration/django.html#exceptions-middleware – Tom Mar 30 '19 at 17:38
5

In my app's views.py:

from social_auth.views import associate_complete

def associate_complete_wrapper(request, backend):
    try:
        return associate_complete(request, backend)
    except ValueError, e:
        if e and len(e.args) > 0:
            messages.error(request, "Failed to Associate: %s" % e.args[0])
    return redirect(reverse('pieprofile-edit-account'))

Then in the Root URLconf (notice the order of these url patterns):

url(r'^associate/complete/(?P<backend>[^/]+)/$', 'myapp.views.associate_complete_wrapper'),
url(r'', include('social_auth.urls')),

My associate_complete_wrapper url essentially hijacks social_auth's socialauth_associate_complete url.

Brad Montgomery
  • 2,501
  • 1
  • 22
  • 24