]> git.parisson.com Git - django-social-auth.git/commitdiff
replaced with a version based on the flickr backend. Works for basic functionality.
authorLarry Price <laprice@gmail.com>
Fri, 24 Feb 2012 08:41:16 +0000 (00:41 -0800)
committerLarry Price <laprice@gmail.com>
Fri, 24 Feb 2012 08:41:16 +0000 (00:41 -0800)
social_auth/backends/contrib/fitbit.py

index ae78f2acc6d5a021c9f9a3f09014e8c227351c0a..5084fa6ba2969811bb6f867ed846330e66e6e5ce 100644 (file)
@@ -5,31 +5,27 @@ This contribution adds support for Fitbit OAuth service. The settings
 FITBIT_CONSUMER_KEY and FITBIT_CONSUMER_SECRET must be defined with the values
 given by Fitbit application registration process.
 
-Extended permissions are supported by defining FITBIT_EXTENDED_PERMISSIONS
-setting, it must be a list of values to request.
-
-By default account id and token expiration time are stored in extra_data
-field, check OAuthBackend class for details on how to extend it.
+By default account id, username and token expiration time are stored in
+extra_data field, check OAuthBackend class for details on how to extend it.
 """
-import logging
-logger = logging.getLogger('buttekicker')
-
-import cgi
-import urllib
+try:
+    from urlparse import parse_qs
+    parse_qs # placate pyflakes
+except ImportError:
+    # fall back for Python 2.5
+    from cgi import parse_qs
 
 from django.conf import settings
 from django.utils import simplejson
-from django.contrib.auth import authenticate
-
-from social_auth.backends import BaseOAuth, OAuthBackend, USERNAME
 
+from oauth2 import Token
+from social_auth.backends import ConsumerBasedOAuth, OAuthBackend, USERNAME
 
 # Fitbit configuration
-FITBIT_SERVER = 'api.fitbit.com'
-FITBIT_REQUEST_TOKEN_URL = 'https://%s/oauth/request_token' % FITBIT_SERVER
-FITBIT_AUTHORIZATION_URL = 'https://%s/oauth/authorize' % FITBIT_SERVER
-FITBIT_ACCESS_TOKEN_URL = 'https://%s/oauth/access_token' % FITBIT_SERVER
-FITBIT_API_URL = 'https://api.%s' % FITBIT_SERVER
+FITBIT_SERVER = 'https://api.fitbit.com'
+FITBIT_REQUEST_TOKEN_URL = '%s/oauth/request_token' % FITBIT_SERVER
+FITBIT_AUTHORIZATION_URL = '%s/oauth/authorize' % FITBIT_SERVER
+FITBIT_ACCESS_TOKEN_URL = '%s/oauth/access_token' % FITBIT_SERVER
 EXPIRES_NAME = getattr(settings, 'SOCIAL_AUTH_EXPIRATION', 'expires')
 
 
@@ -37,67 +33,52 @@ class FitbitBackend(OAuthBackend):
     """Fitbit OAuth authentication backend"""
     name = 'fitbit'
     # Default extra data to store
-    EXTRA_DATA = [('id', 'id'), ('expires', EXPIRES_NAME)]
+    EXTRA_DATA = [('id', 'id'),
+                  ('username', 'username'),
+                  ('expires', EXPIRES_NAME)]
 
     def get_user_details(self, response):
         """Return user details from Fitbit account"""
-        return {USERNAME: response.get('login'),
-                'email': response.get('email') or '',
-                'first_name': response.get('name')}
+        return {USERNAME: response.get('id'),
+                'email': '',
+                'first_name': response.get('fullname')}
 
-class FitbitAuth(BaseOAuth):
-    """Fitbit OAuth mechanism"""
-    AUTH_BACKEND = FitbitBackend
 
-    def auth_url(self):
-        """Returns redirect url"""
-        args = {'client_id': settings.FITBIT_CONSUMER_KEY,
-                'redirect_uri': self.redirect_uri}
-        if hasattr(settings, 'FITBIT_EXTENDED_PERMISSIONS'):
-            args['scope'] = ','.join(settings.FITBIT_EXTENDED_PERMISSIONS)
-        args.update(self.auth_extra_arguments())
-        return FITBIT_AUTHORIZATION_URL + '?' + urllib.urlencode(args)
-
-    def auth_complete(self, *args, **kwargs):
-        """Returns user, might be logged in"""
-        if 'code' in self.data:
-            url = FITBIT_ACCESS_TOKEN_URL + '?' + \
-                  urllib.urlencode({'client_id': settings.FITBIT_CONSUMER_KEY,
-                                'redirect_uri': self.redirect_uri,
-                                'client_secret': settings.FITBIT_CONSUMER_SECRET,
-                                'code': self.data['code']})
-            response = cgi.parse_qs(urllib.urlopen(url).read())
-            if response.get('error'):
-                error = self.data.get('error') or 'unknown error'
-                raise ValueError('Authentication error: %s' % error)
-            access_token = response['access_token'][0]
-            data = self.user_data(access_token)
-            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
-            kwargs.update({'response': data, FitbitBackend.name: True})
-            return authenticate(*args, **kwargs)
-        else:
-            error = self.data.get('error') or 'unknown error'
-            raise ValueError('Authentication error: %s' % error)
+class FitbitAuth(ConsumerBasedOAuth):
+    """Fitbit OAuth authentication mechanism"""
+    AUTHORIZATION_URL = FITBIT_AUTHORIZATION_URL
+    REQUEST_TOKEN_URL = FITBIT_REQUEST_TOKEN_URL
+    ACCESS_TOKEN_URL = FITBIT_ACCESS_TOKEN_URL
+    SERVER_URL = FITBIT_SERVER
+    AUTH_BACKEND = FitbitBackend
+    SETTINGS_KEY_NAME = 'FITBIT_CONSUMER_KEY'
+    SETTINGS_SECRET_NAME = 'FITBIT_CONSUMER_SECRET'
+
+    def access_token(self, token):
+        """Return request for access token value"""
+        # Fitbit is a bit different - it passes user information along with
+        # the access token, so temporarily store it to vie the user_data
+        # method easy access later in the flow!
+        request = self.oauth_request(token, self.ACCESS_TOKEN_URL)
+        response = self.fetch_response(request)
+        token = Token.from_string(response)
+        params = parse_qs(response)
+
+        token.user_nsid = params['user_nsid'][0] if 'user_nsid' in params \
+                                                 else None
+        token.fullname = params['fullname'][0] if 'fullname' in params \
+                                               else None
+        token.username = params['username'][0] if 'username' in params \
+                                               else None
+        return token
 
     def user_data(self, access_token):
         """Loads user data from service"""
-        params = {'access_token': access_token}
-        url = FITBIT_API_URL + '/user?' + urllib.urlencode(params)
-        try:
-            return simplejson.load(urllib.urlopen(url))
-        except ValueError:
-            return None
-
-    @classmethod
-    def enabled(cls):
-        """Return backend enabled status by checking basic settings"""
-        return all(hasattr(settings, name) for name in
-                        ('FITBIT_CONSUMER_KEY',
-                         'FITBIT_CONSUMER_SECRET'))
+        return {
+            'id': access_token.user_nsid,
+            'username': access_token.username,
+            'fullname': access_token.fullname,
+        }
 
 
 # Backend definition