Defaults to ``LOGIN_ERROR_URL``.
+ - The application catches any exception and logs errors to ``logger`` or
+ ``django.contrib.messagess`` application by default. But it's possible to
+ override the default behavior by defining a function to process the
+ exceptions using this setting::
+
+ SOCIAL_AUTH_PROCESS_EXCEPTIONS = 'social_auth.utils.process_exceptions'
+
+ The function parameters will ``request`` holding the current request object,
+ ``backend`` with the current backend and ``err`` which is the exception
+ instance.
+
+ Recently this set of exceptions were introduce to describe the situations
+ a bit more than the old ``ValueError`` usually raised::
+
+ AuthException - Base exception class
+ AuthFailed - Authentication failed for some reason
+ AuthCanceled - Authentication was canceled by the user
+ AuthUnknownError - An unknown error stoped the authentication
+ process
+ AuthTokenError - Unauthorized or access token error, it was
+ invalid, impossible to authenticate or user
+ removed permissions to it.
+ AuthMissingParameter - A needed parameter to continue the process was
+ missing, usually raised by the services that
+ need some POST data like myOpenID
+
+ These are a subclass of ``ValueError`` to keep backward compatibility.
+
+ Having tracebacks is really useful when debugging, for that purpose this
+ setting was defined::
+
+ SOCIAL_AUTH_RAISE_EXCEPTIONS = DEBUG
+
+ It's default value is ``DEBUG``, so you need to set it to ``False`` to avoid
+ tracebacks when ``DEBUG = True``.
+
+
+ Some settings can be tweak by backend by adding the backend name prefix (all
+ uppercase and replace ``-`` with ``_``), here's the supported settings so far::
+
+ LOGIN_ERROR_URL
+ SOCIAL_AUTH_BACKEND_ERROR_URL
+ SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL
+ SOCIAL_AUTH_DISCONNECT_REDIRECT_URL
+ SOCIAL_AUTH_NEW_USER_REDIRECT_URL
+ SOCIAL_AUTH_LOGIN_REDIRECT_URL
+ SOCIAL_AUTH_INACTIVE_USER_URL
+
+- The app catches any exception and logs errors to ``logger`` or
+ ``django.contrib.messagess`` app. Having tracebacks is really useful when
+ debugging, for that purpose this setting was defined::
+
+ SOCIAL_AUTH_RAISE_EXCEPTIONS = DEBUG
+
+ It's default value is ``DEBUG``, so you need to set it to ``False`` to avoid
+ tracebacks when ``DEBUG = True``.
.. _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
"""
import cgi
from urllib import urlencode
- from urllib2 import urlopen
+ from urllib2 import urlopen, HTTPError
+import base64
+import hmac
+import hashlib
+import time
from django.utils import simplejson
from django.contrib.auth import authenticate
def auth_complete(self, *args, **kwargs):
"""Completes loging process, must return user instance"""
- if 'code' not in self.data:
- if self.data.get('error') == 'access_denied':
- raise AuthCanceled(self)
- else:
- raise AuthException(self)
+ access_token = None
+ expires = None
+
+ if 'code' in self.data:
- url = 'https://graph.facebook.com/oauth/access_token?' + \
- urlencode({'client_id': setting('FACEBOOK_APP_ID'),
- 'redirect_uri': self.redirect_uri,
- 'client_secret': setting('FACEBOOK_API_SECRET'),
- 'code': self.data['code']})
- response = cgi.parse_qs(urlopen(url).read())
++ url = ACCESS_TOKEN + urlencode({
++ 'client_id': setting('FACEBOOK_APP_ID'),
++ 'redirect_uri': self.redirect_uri,
++ 'client_secret': setting('FACEBOOK_API_SECRET'),
++ 'code': self.data['code']
++ })
++ try:
++ response = cgi.parse_qs(urlopen(url).read())
++ except HTTPError:
++ raise AuthFailed(self, 'There was an error authenticating the app')
++
+ access_token = response['access_token'][0]
+ if 'expires' in response:
+ expires = response['expires'][0]
- url = ACCESS_TOKEN + urlencode({
- 'client_id': setting('FACEBOOK_APP_ID'),
- 'redirect_uri': self.redirect_uri,
- 'client_secret': setting('FACEBOOK_API_SECRET'),
- 'code': self.data['code']
- })
- try:
- response = cgi.parse_qs(urlopen(url).read())
- except HTTPError:
- raise AuthFailed(self, 'There was an error authenticating the app')
+ if 'signed_request' in self.data:
+ response = load_signed_request(self.data.get('signed_request'))
- access_token = response['access_token'][0]
- data = self.user_data(access_token)
+ if response is not None:
+ access_token = response.get('access_token') or response.get('oauth_token') \
+ or self.data.get('access_token')
- if data is not None:
- data['access_token'] = access_token
- # expires will not be part of response if offline access
- # premission was requested
- if 'expires' in response:
- data['expires'] = response['expires'][0]
+ if 'expires' in response:
+ expires = response['expires']
+
+ if access_token:
+ data = self.user_data(access_token)
+
- kwargs.update({'auth': self,
- 'response': data,
- self.AUTH_BACKEND.name: True})
- return authenticate(*args, **kwargs)
+ if data is not None:
- if 'error' in data:
- error = self.data.get('error') or 'unknown error'
- raise ValueError('Authentication error: %s' % error)
+ data['access_token'] = access_token
+ # expires will not be part of response if offline access
- # premission was requested
++ # premission was requested
+ if expires:
- data['expires'] = expires
- kwargs.update({'response': data, self.AUTH_BACKEND.name: True})
++ data['expires'] = response['expires'][0]
++
++ kwargs.update({'auth': self,
++ 'response': data,
++ self.AUTH_BACKEND.name: True})
++
+ return authenticate(*args, **kwargs)
+ else:
- error = self.data.get('error') or 'unknown error'
- raise ValueError('Authentication error: %s' % error)
++ if self.data.get('error') == 'access_denied':
++ raise AuthCanceled(self)
++ else:
++ raise AuthException(self)
@classmethod
def enabled(cls):