]> git.parisson.com Git - django-social-auth.git/commitdiff
Google OAuth support
authorMatías Aguirre <matiasaguirre@gmail.com>
Fri, 14 Jan 2011 05:12:53 +0000 (03:12 -0200)
committerMatías Aguirre <matiasaguirre@gmail.com>
Fri, 14 Jan 2011 05:12:53 +0000 (03:12 -0200)
social_auth/auth.py
social_auth/backends.py
social_auth/conf.py

index b756e1be77805b00b5961f553b2d25a316f781dc..f533b77d5355d7d21594587b23711f9e93695fc2 100644 (file)
@@ -1,6 +1,7 @@
 """Authentication handling class"""
 import cgi
 import urllib
+import urllib2
 import httplib
 
 from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE
@@ -15,16 +16,17 @@ from django.contrib.auth import authenticate
 
 from .store import DjangoOpenIDStore
 from .backends import TwitterBackend, OrkutBackend, FacebookBackend, \
-                      OpenIDBackend, GoogleBackend, YahooBackend
+                      OpenIDBackend, GoogleBackend, YahooBackend, \
+                      GoogleOAuthBackend
 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, \
                   TWITTER_AUTHORIZATION_URL, TWITTER_CHECK_AUTH, \
                   FACEBOOK_CHECK_AUTH, FACEBOOK_AUTHORIZATION_URL, \
-                  FACEBOOK_ACCESS_TOKEN_URL, ORKUT_SERVER, ORKUT_SCOPE, \
-                  ORKUT_REQUEST_TOKEN_URL, ORKUT_ACCESS_TOKEN_URL, \
-                  ORKUT_AUTHORIZATION_URL, ORKUT_REST_ENDPOINT, \
-                  ORKUT_EXTRA_DATA
+                  FACEBOOK_ACCESS_TOKEN_URL, GOOGLE_REQUEST_TOKEN_URL, \
+                  GOOGLE_ACCESS_TOKEN_URL, GOOGLE_AUTHORIZATION_URL, \
+                  GOOGLE_SERVER, GOOGLE_OAUTH_SCOPE, GOOGLEAPIS_EMAIL, \
+                  ORKUT_REST_ENDPOINT, ORKUT_DEFAULT_DATA, ORKUT_SCOPE
 
 
 class BaseAuth(object):
@@ -97,8 +99,8 @@ class OpenIdAuth(BaseAuth):
     def setup_request(self):
         """Setup request"""
         openid_request = self.openid_request()
-        # Request some user details.  If the provider advertises support
-        # for attribute exchange, use that.
+        # Request some user details. Use attribute exchange if provider
+        # advertises support.
         if openid_request.endpoint.supportsType(ax.AXMessage.ns_uri):
             fetch_request = ax.FetchRequest()
             # Mark all attributes as required, Google ignores optional ones
@@ -279,31 +281,45 @@ class ConsumerBasedOAuth(BaseOAuth):
         raise NotImplementedError('Implement in subclass')
 
 
-class OrkutAuth(ConsumerBasedOAuth):
+class BaseGoogleOAuth(ConsumerBasedOAuth):
+    """Base class for Google OAuth mechanism"""
+    AUTHORIZATION_URL = GOOGLE_AUTHORIZATION_URL
+    REQUEST_TOKEN_URL = GOOGLE_REQUEST_TOKEN_URL
+    ACCESS_TOKEN_URL = GOOGLE_ACCESS_TOKEN_URL
+    SERVER_URL = GOOGLE_SERVER
+    AUTH_BACKEND = None
+
+    def user_data(self, access_token):
+        """Loads user data from G service"""
+        raise NotImplementedError('Implement in subclass')
+
+    def get_key_and_secret(self):
+        """Return Consumer Key and Consumer Secret pair"""
+        raise NotImplementedError('Implement in subclass')
+
+
+class OrkutAuth(BaseGoogleOAuth):
     """Orkut OAuth authentication mechanism"""
-    AUTHORIZATION_URL = ORKUT_AUTHORIZATION_URL
-    REQUEST_TOKEN_URL = ORKUT_REQUEST_TOKEN_URL
-    ACCESS_TOKEN_URL = ORKUT_ACCESS_TOKEN_URL
-    SERVER_URL = ORKUT_SERVER
     AUTH_BACKEND = OrkutBackend
 
     def user_data(self, access_token):
         """Loads user data from Orkut service"""
-        fields = 'name,displayName,emails'
-        if ORKUT_EXTRA_DATA:
-            fields += ',' + ORKUT_EXTRA_DATA
+        fields = ORKUT_DEFAULT_DATA
+        if hasattr(settings, 'ORKUT_EXTRA_DATA'):
+            fields += ',' + settings.ORKUT_EXTRA_DATA
+        scope = ORKUT_SCOPE + \
+                getattr(settings, 'ORKUT_EXTRA_SCOPE', [])
         params = {'method': 'people.get',
                   'id': 'myself',
                   'userId': '@me',
                   'groupId': '@self',
                   'fields': fields,
-                  'scope': ORKUT_SCOPE}
+                  'scope': ' '.join(scope)}
         request = self.oauth_request(access_token, ORKUT_REST_ENDPOINT, params)
         response = urllib.urlopen(request.to_url()).read()
         try:
-            json = simplejson.loads(response)
-            return json['data']
-        except simplejson.JSONDecodeError:
+            return simplejson.loads(response)['data']
+        except (simplejson.JSONDecodeError, KeyError):
             return None
 
     def get_key_and_secret(self):
@@ -311,6 +327,50 @@ class OrkutAuth(ConsumerBasedOAuth):
         return settings.ORKUT_CONSUMER_KEY, settings.ORKUT_CONSUMER_SECRET
 
 
+class GoogleOAuth(BaseGoogleOAuth):
+    """Google OAuth authorization mechanism"""
+    AUTH_BACKEND = GoogleOAuthBackend
+
+    def user_data(self, access_token):
+        """Loads user data data from googleapis service, only email so far
+        as it's described in:
+            http://sites.google.com/site/oauthgoog/Home/emaildisplayscope
+        OAuth parameters needs to be passed in the queryset and
+        Authorization header, this behavior is listed in:
+            http://groups.google.com/group/oauth/browse_thread/thread/d15add9beb418ebc
+        """
+        url = self.oauth_request(access_token, GOOGLEAPIS_EMAIL,
+                                 {'alt': 'json'}).to_url()
+        params = url.split('?', 1)[1]
+        request = urllib2.Request(url)
+        request.headers['Authorization'] = params # setup header
+        response = urllib2.urlopen(request).read()
+        try:
+            return simplejson.loads(response)['data']
+        except (simplejson.JSONDecodeError, KeyError):
+            return None
+
+    def oauth_request(self, token, url, extra_params=None):
+        extra_params = extra_params or {}
+        scope = GOOGLE_OAUTH_SCOPE + \
+                getattr(settings, 'GOOGLE_OAUTH_EXTRA_SCOPE', [])
+        extra_params.update({
+            'scope': ' '.join(scope),
+            'xoauth_displayname': getattr(settings, 'GOOGLE_DISPLAY_NAME',
+                                          'Social Auth')
+        })
+        return super(GoogleOAuth, self).oauth_request(token, url, extra_params)
+
+    def get_key_and_secret(self):
+        """Return Google OAuth Consumer Key and Consumer Secret pair, uses
+        anonymous by default, beware that this marks the application as not
+        registered and a security badge is displayed on authorization page.
+        http://code.google.com/apis/accounts/docs/OAuth_ref.html#SigningOAuth
+        """
+        return getattr(settings, 'GOOGLE_CONSUMER_KEY', 'anonymous'), \
+               getattr(settings, 'GOOGLE_CONSUMER_SECRET', 'anonymous')
+
+
 class TwitterAuth(ConsumerBasedOAuth):
     """Twitter OAuth authentication mechanism"""
     AUTHORIZATION_URL = TWITTER_AUTHORIZATION_URL
@@ -384,6 +444,7 @@ BACKENDS = {
     'twitter': TwitterAuth,
     'facebook': FacebookAuth,
     'google': GoogleAuth,
+    'google-oauth': GoogleOAuth,
     'yahoo': YahooAuth,
     'orkut': OrkutAuth,
     'openid': OpenIdAuth,
index 62ffc0170aa6902594018c6c1a4edf4da8dd2d38..591030ac4f1ec6ae26b037b7f15686c275d54e5c 100644 (file)
@@ -186,6 +186,24 @@ class OrkutBackend(OAuthBackend):
                 'lastname': response['name']['familyName']}
 
 
+class GoogleOAuthBackend(OAuthBackend):
+    """Google OAuth authentication backend"""
+    name = 'google-oauth'
+
+    def get_user_id(self, details, response):
+        "Use google email as unique id"""
+        return details['email']
+
+    def get_user_details(self, response):
+        """Return user details from Orkut account"""
+        email = response['email']
+        return {USERNAME: email.split('@', 1)[0],
+                'email': email,
+                'fullname': '',
+                'first_name': '',
+                'last_name': ''}
+
+
 class FacebookBackend(OAuthBackend):
     """Facebook OAuth authentication backend"""
     name = 'facebook'
index b3b145c2732dad00e2630d6658357397142dea39..7a75d1a382a32af85e9c8ae2e4f1f58d0e478ebb 100644 (file)
@@ -12,17 +12,22 @@ FACEBOOK_AUTHORIZATION_URL = 'https://%s/oauth/authorize' % FACEBOOK_SERVER
 FACEBOOK_ACCESS_TOKEN_URL = 'https://%s/oauth/access_token' % FACEBOOK_SERVER
 FACEBOOK_CHECK_AUTH = 'https://%s/me' % FACEBOOK_SERVER
 
+# Google OAuth base configuration
+GOOGLE_SERVER = 'www.google.com'
+GOOGLE_REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken'
+GOOGLE_ACCESS_TOKEN_URL  = 'https://www.google.com/accounts/OAuthGetAccessToken'
+GOOGLE_AUTHORIZATION_URL = 'https://www.google.com/accounts/OAuthAuthorizeToken'
+# scope for user email, specify extra scopes in settings, for example:
+# GOOGLE_OAUTH_EXTRA_SCOPE = ['https://www.google.com/m8/feeds/']
+GOOGLE_OAUTH_SCOPE       = ['https://www.googleapis.com/auth/userinfo#email']
+GOOGLEAPIS_EMAIL         = 'https://www.googleapis.com/userinfo/email'
+
 # Orkut configuration
-ORKUT_SERVER = 'www.google.com'
-ORKUT_REQUEST_TOKEN_URL = 'https://%s/accounts/OAuthGetRequestToken' % \
-                            ORKUT_SERVER
-ORKUT_ACCESS_TOKEN_URL = 'https://%s/accounts/OAuthGetAccessToken' % \
-                            ORKUT_SERVER
-ORKUT_AUTHORIZATION_URL = 'https://%s/accounts/OAuthAuthorizeToken' % \
-                            ORKUT_SERVER
-ORKUT_SCOPE = 'http://orkut.gmodules.com/social/'
+# default scope, specify extra scope in settings as in:
+# ORKUT_EXTRA_SCOPE = ['...']
+ORKUT_SCOPE = ['http://orkut.gmodules.com/social/']
 ORKUT_REST_ENDPOINT = 'http://www.orkut.com/social/rpc'
-ORKUT_EXTRA_DATA = ''
+ORKUT_DEFAULT_DATA = 'name,displayName,emails'
 
 # OpenID configuration
 OLD_AX_ATTRS = [