From 350f98ebdec44ca54513afc010e50eb6b3ebda37 Mon Sep 17 00:00:00 2001
From: Braden MacDonald <braden@opencraft.com>
Date: Wed, 6 May 2015 11:16:36 -0700
Subject: [PATCH] Changes for compatibility with latest python-social-auth (0.2.7)

---
 common/djangoapps/auth_exchange/forms.py                           |  5 +++--
 common/djangoapps/auth_exchange/tests/test_forms.py                |  5 ++++-
 common/djangoapps/student/views.py                                 | 11 +++++++----
 common/djangoapps/third_party_auth/pipeline.py                     |  9 +++++----
 common/djangoapps/third_party_auth/provider.py                     | 36 ++++++------------------------------
 common/djangoapps/third_party_auth/tests/specs/base.py             | 93 +++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------
 common/djangoapps/third_party_auth/tests/test_change_enrollment.py |  6 +++---
 common/djangoapps/third_party_auth/tests/utils.py                  |  4 ++--
 common/test/db_cache/bok_choy_schema.sql                           | 53 -----------------------------------------------------
 common/test/db_cache/lettuce.db                                    | Bin 653312 -> 0 bytes
 lms/envs/test.py                                                   |  4 ++--
 requirements/edx/base.txt                                          |  2 +-
 12 files changed, 82 insertions(+), 146 deletions(-)

diff --git a/common/djangoapps/auth_exchange/forms.py b/common/djangoapps/auth_exchange/forms.py
index 2391486..7311948 100644
--- a/common/djangoapps/auth_exchange/forms.py
+++ b/common/djangoapps/auth_exchange/forms.py
@@ -10,6 +10,7 @@ from provider.oauth2.forms import ScopeChoiceField, ScopeMixin
 from provider.oauth2.models import Client
 from requests import HTTPError
 from social.backends import oauth as social_oauth
+from social.exceptions import AuthException
 
 from third_party_auth import pipeline
 
@@ -54,7 +55,7 @@ class AccessTokenExchangeForm(ScopeMixin, OAuthForm):
         if self._errors:
             return {}
 
-        backend = self.request.social_strategy.backend
+        backend = self.request.backend
         if not isinstance(backend, social_oauth.BaseOAuth2):
             raise OAuthValidationError(
                 {
@@ -89,7 +90,7 @@ class AccessTokenExchangeForm(ScopeMixin, OAuthForm):
         user = None
         try:
             user = backend.do_auth(self.cleaned_data.get("access_token"))
-        except HTTPError:
+        except (HTTPError, AuthException):
             pass
         if user and isinstance(user, User):
             self.cleaned_data["user"] = user
diff --git a/common/djangoapps/auth_exchange/tests/test_forms.py b/common/djangoapps/auth_exchange/tests/test_forms.py
index c89fec9..ffeffb4 100644
--- a/common/djangoapps/auth_exchange/tests/test_forms.py
+++ b/common/djangoapps/auth_exchange/tests/test_forms.py
@@ -24,8 +24,11 @@ class AccessTokenExchangeFormTest(AccessTokenExchangeTestMixin):
     def setUp(self):
         super(AccessTokenExchangeFormTest, self).setUp()
         self.request = RequestFactory().post("dummy_url")
+        redirect_uri = 'dummy_redirect_url'
         SessionMiddleware().process_request(self.request)
-        self.request.social_strategy = social_utils.load_strategy(self.request, self.BACKEND)
+        self.request.social_strategy = social_utils.load_strategy(self.request)
+        # pylint: disable=no-member
+        self.request.backend = social_utils.load_backend(self.request.social_strategy, self.BACKEND, redirect_uri)
 
     def _assert_error(self, data, expected_error, expected_error_description):
         form = AccessTokenExchangeForm(request=self.request, data=data)
diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index 2e863bc..14528e5 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -1129,7 +1129,7 @@ def login_oauth_token(request, backend):
     """
     warnings.warn("Please use AccessTokenExchangeView instead.", DeprecationWarning)
 
-    backend = request.social_strategy.backend
+    backend = request.backend
     if isinstance(backend, social_oauth.BaseOAuth1) or isinstance(backend, social_oauth.BaseOAuth2):
         if "access_token" in request.POST:
             # Tell third party auth pipeline that this is an API call
@@ -1137,7 +1137,7 @@ def login_oauth_token(request, backend):
             user = None
             try:
                 user = backend.do_auth(request.POST["access_token"])
-            except HTTPError:
+            except (HTTPError, AuthException):
                 pass
             # do_auth can return a non-User object if it fails
             if user and isinstance(user, User):
@@ -1447,7 +1447,10 @@ def create_account_with_params(request, params):
 
         # next, link the account with social auth, if provided
         if should_link_with_social_auth:
-            request.social_strategy = social_utils.load_strategy(backend=params['provider'], request=request)
+            backend_name = params['provider']
+            request.social_strategy = social_utils.load_strategy(request)
+            redirect_uri = reverse('social:complete', args=(backend_name, ))
+            request.backend = social_utils.load_backend(request.social_strategy, backend_name, redirect_uri)
             social_access_token = params.get('access_token')
             if not social_access_token:
                 raise ValidationError({
@@ -1461,7 +1464,7 @@ def create_account_with_params(request, params):
             pipeline_user = None
             error_message = ""
             try:
-                pipeline_user = request.social_strategy.backend.do_auth(social_access_token, user=user)
+                pipeline_user = request.backend.do_auth(social_access_token, user=user)
             except AuthAlreadyAssociated:
                 error_message = _("The provided access_token is already associated with another user.")
             except (HTTPError, AuthException):
diff --git a/common/djangoapps/third_party_auth/pipeline.py b/common/djangoapps/third_party_auth/pipeline.py
index 2ff7ea6..d13d52a 100644
--- a/common/djangoapps/third_party_auth/pipeline.py
+++ b/common/djangoapps/third_party_auth/pipeline.py
@@ -445,7 +445,7 @@ def parse_query_params(strategy, response, *args, **kwargs):
     """Reads whitelisted query params, transforms them into pipeline args."""
     auth_entry = strategy.session.get(AUTH_ENTRY_KEY)
     if not (auth_entry and auth_entry in _AUTH_ENTRY_CHOICES):
-        raise AuthEntryError(strategy.backend, 'auth_entry missing or invalid')
+        raise AuthEntryError(strategy.request.backend, 'auth_entry missing or invalid')
 
     return {'auth_entry': auth_entry}
 
@@ -526,7 +526,7 @@ def _create_redirect_url(url, strategy):
 
 
 @partial.partial
-def set_logged_in_cookie(backend=None, user=None, request=None, auth_entry=None, *args, **kwargs):
+def set_logged_in_cookie(backend=None, user=None, strategy=None, auth_entry=None, *args, **kwargs):
     """This pipeline step sets the "logged in" cookie for authenticated users.
 
     Some installations have a marketing site front-end separate from
@@ -552,6 +552,7 @@ def set_logged_in_cookie(backend=None, user=None, request=None, auth_entry=None,
 
     """
     if not is_api(auth_entry) and user is not None and user.is_authenticated():
+        request = strategy.request if strategy else None
         if request is not None:
             # Check that the cookie isn't already set.
             # This ensures that we allow the user to continue to the next
@@ -692,7 +693,7 @@ def change_enrollment(strategy, auth_entry=None, user=None, *args, **kwargs):
 
 
 @partial.partial
-def associate_by_email_if_login_api(auth_entry, strategy, details, user, *args, **kwargs):
+def associate_by_email_if_login_api(auth_entry, backend, details, user, *args, **kwargs):
     """
     This pipeline step associates the current social auth with the user with the
     same email address in the database.  It defers to the social library's associate_by_email
@@ -701,7 +702,7 @@ def associate_by_email_if_login_api(auth_entry, strategy, details, user, *args, 
     This association is done ONLY if the user entered the pipeline through a LOGIN API.
     """
     if auth_entry == AUTH_ENTRY_LOGIN_API:
-        association_response = associate_by_email(strategy, details, user, *args, **kwargs)
+        association_response = associate_by_email(backend, details, user, *args, **kwargs)
         if (
             association_response and
             association_response.get('user') and
diff --git a/common/djangoapps/third_party_auth/provider.py b/common/djangoapps/third_party_auth/provider.py
index 4fc1de6..9f0809d 100644
--- a/common/djangoapps/third_party_auth/provider.py
+++ b/common/djangoapps/third_party_auth/provider.py
@@ -36,7 +36,7 @@ class BaseProvider(object):
         return '%s.%s' % (cls.BACKEND_CLASS.__module__, cls.BACKEND_CLASS.__name__)
 
     @classmethod
-    def get_email(cls, unused_provider_details):
+    def get_email(cls, provider_details):
         """Gets user's email address.
 
         Provider responses can contain arbitrary data. This method can be
@@ -44,16 +44,16 @@ class BaseProvider(object):
         extracted by the social_details pipeline step.
 
         Args:
-            unused_provider_details: dict of string -> string. Data about the
+            provider_details: dict of string -> string. Data about the
                 user passed back by the provider.
 
         Returns:
             String or None. The user's email address, if any.
         """
-        return None
+        return provider_details.get('email')
 
     @classmethod
-    def get_name(cls, unused_provider_details):
+    def get_name(cls, provider_details):
         """Gets user's name.
 
         Provider responses can contain arbitrary data. This method can be
@@ -61,13 +61,13 @@ class BaseProvider(object):
         extracted by the social_details pipeline step.
 
         Args:
-            unused_provider_details: dict of string -> string. Data about the
+            provider_details: dict of string -> string. Data about the
                 user passed back by the provider.
 
         Returns:
             String or None. The user's full name, if any.
         """
-        return None
+        return provider_details.get('fullname')
 
     @classmethod
     def get_register_form_data(cls, pipeline_kwargs):
@@ -121,14 +121,6 @@ class GoogleOauth2(BaseProvider):
         'SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET': None,
     }
 
-    @classmethod
-    def get_email(cls, provider_details):
-        return provider_details.get('email')
-
-    @classmethod
-    def get_name(cls, provider_details):
-        return provider_details.get('fullname')
-
 
 class LinkedInOauth2(BaseProvider):
     """Provider for LinkedIn's Oauth2 auth system."""
@@ -141,14 +133,6 @@ class LinkedInOauth2(BaseProvider):
         'SOCIAL_AUTH_LINKEDIN_OAUTH2_SECRET': None,
     }
 
-    @classmethod
-    def get_email(cls, provider_details):
-        return provider_details.get('email')
-
-    @classmethod
-    def get_name(cls, provider_details):
-        return provider_details.get('fullname')
-
 
 class FacebookOauth2(BaseProvider):
     """Provider for LinkedIn's Oauth2 auth system."""
@@ -161,14 +145,6 @@ class FacebookOauth2(BaseProvider):
         'SOCIAL_AUTH_FACEBOOK_SECRET': None,
     }
 
-    @classmethod
-    def get_email(cls, provider_details):
-        return provider_details.get('email')
-
-    @classmethod
-    def get_name(cls, provider_details):
-        return provider_details.get('fullname')
-
 
 class Registry(object):
     """Singleton registry of third-party auth providers.
diff --git a/common/djangoapps/third_party_auth/tests/specs/base.py b/common/djangoapps/third_party_auth/tests/specs/base.py
index 902a1be..b188e8f 100644
--- a/common/djangoapps/third_party_auth/tests/specs/base.py
+++ b/common/djangoapps/third_party_auth/tests/specs/base.py
@@ -140,7 +140,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         exception_middleware = middleware.ExceptionMiddleware()
         request, _ = self.get_request_and_strategy(auth_entry=auth_entry)
         response = exception_middleware.process_exception(
-            request, exceptions.AuthCanceled(request.social_strategy.backend))
+            request, exceptions.AuthCanceled(request.backend))
         location = response.get('Location')
 
         self.assertEqual(302, response.status_code)
@@ -161,7 +161,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         """
         _, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         self.create_user_models_for_existing_account(
             strategy, email, password, self.get_username(), skip_social_auth=True)
 
@@ -239,7 +239,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
     def assert_redirect_to_dashboard_looks_correct(self, response):
         """Asserts a response would redirect to /dashboard."""
         self.assertEqual(302, response.status_code)
-        # pylint: disable-msg=protected-access
+        # pylint: disable=protected-access
         self.assertEqual(auth_settings._SOCIAL_AUTH_LOGIN_REDIRECT_URL, response.get('Location'))
 
     def assert_redirect_to_login_looks_correct(self, response):
@@ -287,7 +287,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         See student.views.register and student.views._do_create_account.
         """
         response_data = self.get_response_data()
-        uid = strategy.backend.get_user_id(response_data, response_data)
+        uid = strategy.request.backend.get_user_id(response_data, response_data)
         user = social_utils.Storage.user.create_user(email=email, password=password, username=username)
         profile = student_models.UserProfile(user=user)
         profile.save()
@@ -310,7 +310,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         args = ()
         kwargs = {
             'request': strategy.request,
-            'backend': strategy.backend,
+            'backend': strategy.request.backend,
             'user': None,
             'response': self.get_response_data(),
         }
@@ -355,8 +355,9 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         if auth_entry:
             request.session[pipeline.AUTH_ENTRY_KEY] = auth_entry
 
-        strategy = social_utils.load_strategy(backend=self.backend_name, redirect_uri=redirect_uri, request=request)
+        strategy = social_utils.load_strategy(request=request)
         request.social_strategy = strategy
+        request.backend = social_utils.load_backend(strategy, self.backend_name, redirect_uri)
 
         return request, strategy
 
@@ -404,7 +405,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # configure the backend, and mock out wire traffic.
         request, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         pipeline.analytics.track = mock.MagicMock()
         request.user = self.create_user_models_for_existing_account(
             strategy, 'user@example.com', 'password', self.get_username(), skip_social_auth=True)
@@ -413,12 +414,12 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # expected state.
         self.client.get(
             pipeline.get_login_url(self.PROVIDER_CLASS.NAME, pipeline.AUTH_ENTRY_LOGIN))
-        actions.do_complete(strategy, social_views._do_login)  # pylint: disable-msg=protected-access
+        actions.do_complete(request.backend, social_views._do_login)  # pylint: disable=protected-access
 
         mako_middleware_process_request(strategy.request)
         student_views.signin_user(strategy.request)
         student_views.login_user(strategy.request)
-        actions.do_complete(strategy, social_views._do_login)  # pylint: disable-msg=protected-access
+        actions.do_complete(request.backend, social_views._do_login)  # pylint: disable=protected-access
 
         # First we expect that we're in the unlinked state, and that there
         # really is no association in the backend.
@@ -428,7 +429,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # We should be redirected back to the complete page, setting
         # the "logged in" cookie for the marketing site.
         self.assert_logged_in_cookie_redirect(actions.do_complete(
-            request.social_strategy, social_views._do_login, request.user, None,  # pylint: disable-msg=protected-access
+            request.backend, social_views._do_login, request.user, None,  # pylint: disable=protected-access
             redirect_field_name=auth.REDIRECT_FIELD_NAME
         ))
 
@@ -437,7 +438,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
 
         # Fire off the auth pipeline to link.
         self.assert_redirect_to_dashboard_looks_correct(actions.do_complete(
-            request.social_strategy, social_views._do_login, request.user, None,  # pylint: disable-msg=protected-access
+            request.backend, social_views._do_login, request.user, None,  # pylint: disable=protected-access
             redirect_field_name=auth.REDIRECT_FIELD_NAME))
 
         # Now we expect to be in the linked state, with a backend entry.
@@ -449,7 +450,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # configure the backend, and mock out wire traffic.
         request, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         user = self.create_user_models_for_existing_account(
             strategy, 'user@example.com', 'password', self.get_username())
         self.assert_social_auth_exists_for_user(user, strategy)
@@ -461,12 +462,12 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # expected state.
         self.client.get(
             pipeline.get_login_url(self.PROVIDER_CLASS.NAME, pipeline.AUTH_ENTRY_LOGIN))
-        actions.do_complete(strategy, social_views._do_login)  # pylint: disable-msg=protected-access
+        actions.do_complete(request.backend, social_views._do_login)  # pylint: disable=protected-access
 
         mako_middleware_process_request(strategy.request)
         student_views.signin_user(strategy.request)
         student_views.login_user(strategy.request)
-        actions.do_complete(strategy, social_views._do_login, user=user)  # pylint: disable-msg=protected-access
+        actions.do_complete(request.backend, social_views._do_login, user=user)  # pylint: disable=protected-access
 
         # First we expect that we're in the linked state, with a backend entry.
         self.assert_account_settings_context_looks_correct(account_settings_context(request), user, linked=True)
@@ -474,7 +475,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
 
         # Fire off the disconnect pipeline to unlink.
         self.assert_redirect_to_dashboard_looks_correct(actions.do_disconnect(
-            request.social_strategy, request.user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME))
+            request.backend, request.user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME))
 
         # Now we expect to be in the unlinked state, with no backend entry.
         self.assert_account_settings_context_looks_correct(account_settings_context(request), user, linked=False)
@@ -490,7 +491,8 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         username = self.get_username()
         _, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        backend = strategy.request.backend
+        backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         linked_user = self.create_user_models_for_existing_account(strategy, email, password, username)
         unlinked_user = social_utils.Storage.user.create_user(
             email='other_' + email, password=password, username='other_' + username)
@@ -499,7 +501,8 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         self.assert_social_auth_does_not_exist_for_user(unlinked_user, strategy)
 
         with self.assertRaises(exceptions.AuthAlreadyAssociated):
-            actions.do_complete(strategy, social_views._do_login, user=unlinked_user)  # pylint: disable-msg=protected-access
+            # pylint: disable=protected-access
+            actions.do_complete(backend, social_views._do_login, user=unlinked_user)
 
     def test_already_associated_exception_populates_dashboard_with_error(self):
         # Instrument the pipeline with an exception. We test that the
@@ -511,21 +514,21 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # that the duplicate error has no effect on the state of the controls.
         request, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         user = self.create_user_models_for_existing_account(
             strategy, 'user@example.com', 'password', self.get_username())
         self.assert_social_auth_exists_for_user(user, strategy)
 
         self.client.get('/login')
         self.client.get(pipeline.get_login_url(self.PROVIDER_CLASS.NAME, pipeline.AUTH_ENTRY_LOGIN))
-        actions.do_complete(strategy, social_views._do_login)  # pylint: disable-msg=protected-access
+        actions.do_complete(request.backend, social_views._do_login)  # pylint: disable=protected-access
 
         mako_middleware_process_request(strategy.request)
         student_views.signin_user(strategy.request)
         student_views.login_user(strategy.request)
-        actions.do_complete(strategy, social_views._do_login, user=user)  # pylint: disable-msg=protected-access
+        actions.do_complete(request.backend, social_views._do_login, user=user)  # pylint: disable=protected-access
 
-        # Monkey-patch storage for messaging; pylint: disable-msg=protected-access
+        # Monkey-patch storage for messaging; pylint: disable=protected-access
         request._messages = fallback.FallbackStorage(request)
         middleware.ExceptionMiddleware().process_exception(
             request,
@@ -539,7 +542,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # configure the backend, and mock out wire traffic.
         request, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         pipeline.analytics.track = mock.MagicMock()
         user = self.create_user_models_for_existing_account(
             strategy, 'user@example.com', 'password', self.get_username())
@@ -558,8 +561,8 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
 
         # Next, the provider makes a request against /auth/complete/<provider>
         # to resume the pipeline.
-        # pylint: disable-msg=protected-access
-        self.assert_redirect_to_login_looks_correct(actions.do_complete(strategy, social_views._do_login))
+        # pylint: disable=protected-access
+        self.assert_redirect_to_login_looks_correct(actions.do_complete(request.backend, social_views._do_login))
 
         mako_middleware_process_request(strategy.request)
         # At this point we know the pipeline has resumed correctly. Next we
@@ -574,7 +577,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # We should be redirected back to the complete page, setting
         # the "logged in" cookie for the marketing site.
         self.assert_logged_in_cookie_redirect(actions.do_complete(
-            request.social_strategy, social_views._do_login, request.user, None,  # pylint: disable-msg=protected-access
+            request.backend, social_views._do_login, request.user, None,  # pylint: disable=protected-access
             redirect_field_name=auth.REDIRECT_FIELD_NAME
         ))
 
@@ -582,13 +585,13 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         self.set_logged_in_cookie(request)
 
         self.assert_redirect_to_dashboard_looks_correct(
-            actions.do_complete(strategy, social_views._do_login, user=user))
+            actions.do_complete(request.backend, social_views._do_login, user=user))
         self.assert_account_settings_context_looks_correct(account_settings_context(request), user)
 
     def test_signin_fails_if_account_not_active(self):
         _, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         user = self.create_user_models_for_existing_account(strategy, 'user@example.com', 'password', self.get_username())
 
         user.is_active = False
@@ -600,7 +603,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
     def test_signin_fails_if_no_account_associated(self):
         _, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
         self.create_user_models_for_existing_account(
             strategy, 'user@example.com', 'password', self.get_username(), skip_social_auth=True)
 
@@ -625,7 +628,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # Mock out wire traffic.
         request, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_REGISTER, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
 
         # Begin! Grab the registration page and check the login control on it.
         self.assert_register_response_before_pipeline_looks_correct(self.client.get('/register'))
@@ -637,8 +640,8 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
             pipeline.get_login_url(self.PROVIDER_CLASS.NAME, pipeline.AUTH_ENTRY_LOGIN)))
 
         # Next, the provider makes a request against /auth/complete/<provider>.
-        # pylint: disable-msg=protected-access
-        self.assert_redirect_to_register_looks_correct(actions.do_complete(strategy, social_views._do_login))
+        # pylint: disable=protected-access
+        self.assert_redirect_to_register_looks_correct(actions.do_complete(request.backend, social_views._do_login))
 
         mako_middleware_process_request(strategy.request)
         # At this point we know the pipeline has resumed correctly. Next we
@@ -675,7 +678,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # Since the user's account is not yet active, we should be redirected to /login
         self.assert_redirect_to_login_looks_correct(
             actions.do_complete(
-                request.social_strategy, social_views._do_login, request.user, None,  # pylint: disable-msg=protected-access
+                request.backend, social_views._do_login, request.user, None,  # pylint: disable=protected-access
                 redirect_field_name=auth.REDIRECT_FIELD_NAME
             )
         )
@@ -687,7 +690,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # Try again.  This time, we should be redirected back to the complete page, setting
         # the "logged in" cookie for the marketing site.
         self.assert_logged_in_cookie_redirect(actions.do_complete(
-            request.social_strategy, social_views._do_login, request.user, None,  # pylint: disable-msg=protected-access
+            request.backend, social_views._do_login, request.user, None,  # pylint: disable=protected-access
             redirect_field_name=auth.REDIRECT_FIELD_NAME
         ))
 
@@ -698,7 +701,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # and send the user to the dashboard, where the association will be
         # displayed.
         self.assert_redirect_to_dashboard_looks_correct(
-            actions.do_complete(strategy, social_views._do_login, user=created_user))
+            actions.do_complete(strategy.request.backend, social_views._do_login, user=created_user))
         self.assert_social_auth_exists_for_user(created_user, strategy)
         self.assert_account_settings_context_looks_correct(account_settings_context(request), created_user, linked=True)
 
@@ -710,18 +713,20 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
         # Create a colliding username in the backend, then proceed with
         # assignment via pipeline to make sure a distinct username is created.
         strategy.storage.user.create_user(username=self.get_username(), email='user@email.com', password='password')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
-        # pylint: disable-msg=protected-access
-        self.assert_redirect_to_register_looks_correct(actions.do_complete(strategy, social_views._do_login))
+        backend = strategy.request.backend
+        backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        # pylint: disable=protected-access
+        self.assert_redirect_to_register_looks_correct(actions.do_complete(backend, social_views._do_login))
         distinct_username = pipeline.get(request)['kwargs']['username']
         self.assertNotEqual(original_username, distinct_username)
 
     def test_new_account_registration_fails_if_email_exists(self):
         request, strategy = self.get_request_and_strategy(
             auth_entry=pipeline.AUTH_ENTRY_REGISTER, redirect_uri='social:complete')
-        strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
-        # pylint: disable-msg=protected-access
-        self.assert_redirect_to_register_looks_correct(actions.do_complete(strategy, social_views._do_login))
+        backend = strategy.request.backend
+        backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+        # pylint: disable=protected-access
+        self.assert_redirect_to_register_looks_correct(actions.do_complete(backend, social_views._do_login))
 
         mako_middleware_process_request(strategy.request)
         self.assert_register_response_in_pipeline_looks_correct(
@@ -733,21 +738,21 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
 
     def test_pipeline_raises_auth_entry_error_if_auth_entry_invalid(self):
         auth_entry = 'invalid'
-        self.assertNotIn(auth_entry, pipeline._AUTH_ENTRY_CHOICES)  # pylint: disable-msg=protected-access
+        self.assertNotIn(auth_entry, pipeline._AUTH_ENTRY_CHOICES)  # pylint: disable=protected-access
 
         _, strategy = self.get_request_and_strategy(auth_entry=auth_entry, redirect_uri='social:complete')
 
         with self.assertRaises(pipeline.AuthEntryError):
-            strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+            strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
 
     def test_pipeline_raises_auth_entry_error_if_auth_entry_missing(self):
         _, strategy = self.get_request_and_strategy(auth_entry=None, redirect_uri='social:complete')
 
         with self.assertRaises(pipeline.AuthEntryError):
-            strategy.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
+            strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
 
 
-class Oauth2IntegrationTest(IntegrationTest):  # pylint: disable-msg=abstract-method
+class Oauth2IntegrationTest(IntegrationTest):  # pylint: disable=abstract-method
     """Base test case for integration tests of Oauth2 providers."""
 
     # Dict of string -> object. Information about the token granted to the
diff --git a/common/djangoapps/third_party_auth/tests/test_change_enrollment.py b/common/djangoapps/third_party_auth/tests/test_change_enrollment.py
index 3fa4655..b793286 100644
--- a/common/djangoapps/third_party_auth/tests/test_change_enrollment.py
+++ b/common/djangoapps/third_party_auth/tests/test_change_enrollment.py
@@ -182,6 +182,6 @@ class PipelineEnrollmentTest(UrlResetMixin, ModuleStoreTestCase):
         request.user = self.user
         request.session = cache.SessionStore()
 
-        return social_utils.load_strategy(
-            backend=self.BACKEND_NAME, request=request
-        )
+        request.social_strategy = social_utils.load_strategy(request)
+        request.backend = social_utils.load_backend(request.social_strategy, self.BACKEND_NAME, redirect_uri='')
+        return request.social_strategy
diff --git a/common/djangoapps/third_party_auth/tests/utils.py b/common/djangoapps/third_party_auth/tests/utils.py
index 6dcc266..208930c 100644
--- a/common/djangoapps/third_party_auth/tests/utils.py
+++ b/common/djangoapps/third_party_auth/tests/utils.py
@@ -66,7 +66,7 @@ class ThirdPartyOAuthTestMixin(object):
 class ThirdPartyOAuthTestMixinFacebook(object):
     """Tests oauth with the Facebook backend"""
     BACKEND = "facebook"
-    USER_URL = "https://graph.facebook.com/me"
+    USER_URL = "https://graph.facebook.com/v2.3/me"
     # In facebook responses, the "id" field is used as the user's identifier
     UID_FIELD = "id"
 
@@ -74,6 +74,6 @@ class ThirdPartyOAuthTestMixinFacebook(object):
 class ThirdPartyOAuthTestMixinGoogle(object):
     """Tests oauth with the Google backend"""
     BACKEND = "google-oauth2"
-    USER_URL = "https://www.googleapis.com/oauth2/v1/userinfo"
+    USER_URL = "https://www.googleapis.com/plus/v1/people/me"
     # In google-oauth2 responses, the "email" field is used as the user's identifier
     UID_FIELD = "email"
diff --git a/common/test/db_cache/bok_choy_schema.sql b/common/test/db_cache/bok_choy_schema.sql
index 2473bda..2f2373a 100644
--- a/common/test/db_cache/bok_choy_schema.sql
+++ b/common/test/db_cache/bok_choy_schema.sql
@@ -2236,59 +2236,6 @@ CREATE TABLE `shoppingcart_registrationcoderedemption` (
   CONSTRAINT `registration_code_id_refs_id_4d01e47b` FOREIGN KEY (`registration_code_id`) REFERENCES `shoppingcart_courseregistrationcode` (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 /*!40101 SET character_set_client = @saved_cs_client */;
-DROP TABLE IF EXISTS `social_auth_association`;
-/*!40101 SET @saved_cs_client     = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `social_auth_association` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `server_url` varchar(255) NOT NULL,
-  `handle` varchar(255) NOT NULL,
-  `secret` varchar(255) NOT NULL,
-  `issued` int(11) NOT NULL,
-  `lifetime` int(11) NOT NULL,
-  `assoc_type` varchar(64) NOT NULL,
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-/*!40101 SET character_set_client = @saved_cs_client */;
-DROP TABLE IF EXISTS `social_auth_code`;
-/*!40101 SET @saved_cs_client     = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `social_auth_code` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `email` varchar(75) NOT NULL,
-  `code` varchar(32) NOT NULL,
-  `verified` tinyint(1) NOT NULL,
-  PRIMARY KEY (`id`),
-  UNIQUE KEY `email` (`email`,`code`),
-  KEY `social_auth_code_65da3d2c` (`code`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-/*!40101 SET character_set_client = @saved_cs_client */;
-DROP TABLE IF EXISTS `social_auth_nonce`;
-/*!40101 SET @saved_cs_client     = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `social_auth_nonce` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `server_url` varchar(255) NOT NULL,
-  `timestamp` int(11) NOT NULL,
-  `salt` varchar(65) NOT NULL,
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-/*!40101 SET character_set_client = @saved_cs_client */;
-DROP TABLE IF EXISTS `social_auth_usersocialauth`;
-/*!40101 SET @saved_cs_client     = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `social_auth_usersocialauth` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `user_id` int(11) NOT NULL,
-  `provider` varchar(32) NOT NULL,
-  `uid` varchar(255) NOT NULL,
-  `extra_data` longtext NOT NULL,
-  PRIMARY KEY (`id`),
-  UNIQUE KEY `provider` (`provider`,`uid`),
-  KEY `social_auth_usersocialauth_fbfc09f1` (`user_id`),
-  CONSTRAINT `user_id_refs_id_60fa311b` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-/*!40101 SET character_set_client = @saved_cs_client */;
 DROP TABLE IF EXISTS `south_migrationhistory`;
 /*!40101 SET @saved_cs_client     = @@character_set_client */;
 /*!40101 SET character_set_client = utf8 */;
diff --git a/common/test/db_cache/lettuce.db b/common/test/db_cache/lettuce.db
index 5060b4b..3bd9676 100644
Binary files a/common/test/db_cache/lettuce.db and b/common/test/db_cache/lettuce.db differ
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 38a30b9..ce6342d 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -242,8 +242,8 @@ THIRD_PARTY_AUTH = {
         "SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET": "test",
     },
     "Facebook": {
-        "SOCIAL_AUTH_GOOGLE_OAUTH2_KEY": "test",
-        "SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET": "test",
+        "SOCIAL_AUTH_FACEBOOK_KEY": "test",
+        "SOCIAL_AUTH_FACEBOOK_SECRET": "test",
     },
 }
 
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 37a5ccd..da14311 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -69,7 +69,7 @@ pyparsing==2.0.1
 python-memcached==1.48
 python-openid==2.2.5
 python-dateutil==2.1
-python-social-auth==0.1.23
+python-social-auth==0.2.7
 pytz==2015.2
 pysrt==0.4.7
 PyYAML==3.10
--
libgit2 0.26.0