]> git.parisson.com Git - django-social-auth.git/commitdiff
BrowserID support. Closes #228
authorMatías Aguirre <matiasaguirre@gmail.com>
Sat, 11 Feb 2012 19:38:16 +0000 (17:38 -0200)
committerMatías Aguirre <matiasaguirre@gmail.com>
Sat, 11 Feb 2012 19:38:16 +0000 (17:38 -0200)
README.rst
doc/backends/browserid.rst [new file with mode: 0644]
doc/configuration.rst
doc/miscellaneous.rst
doc/use_cases.rst
example/settings.py
example/templates/base.html
example/templates/done.html
example/templates/home.html
social_auth/backends/__init__.py
social_auth/backends/browserid.py [new file with mode: 0644]

index 4e8c69ba3e0084fa91b485ed29c1528505738958..9f31fda6a2bd83521fd75ecb3f46235b2efe9b45 100644 (file)
@@ -112,6 +112,7 @@ Configuration
         'social_auth.backends.google.GoogleOAuth2Backend',
         'social_auth.backends.google.GoogleBackend',
         'social_auth.backends.yahoo.YahooBackend',
+        'social_auth.backends.browserid.BrowserIDBackend',
         'social_auth.backends.contrib.linkedin.LinkedinBackend',
         'social_auth.backends.contrib.livejournal.LiveJournalBackend',
         'social_auth.backends.contrib.orkut.OrkutBackend',
@@ -773,6 +774,19 @@ Flickr uses OAuth v1.0 for authentication.
       FLICKR_API_SECRET = ''
 
 
+---------
+BrowserID
+---------
+Support for BrowserID_ is possible by posting the ``assertion`` code to
+``/complete/browserid/`` URL.
+
+The setup doesn't need any setting, just the usual BrowserID_ javascript
+include in your document and the needed mechanism to trigger the POST to
+`django-social-auth`_.
+
+Check the second "Use Case" for an implementation example.
+
+
 -------
 Testing
 -------
@@ -828,6 +842,40 @@ Some particular use cases are listed below.
             disconnect, name='socialauth_disconnect_individual'),
     )
 
+2. Include a similar snippet in your page to make BrowserID_ work::
+    <!-- Include BrowserID JavaScript -->
+    <script src="https://browserid.org/include.js" type="text/javascript"></script>
+
+    <!-- Define a form to send the POST data -->
+    <form method="post" action="{% url socialauth_complete "browserid" %}">
+        <input type="hidden" name="assertion" value="" />
+        <a rel="nofollow" id="browserid" href="#">BrowserID</a>
+    </form>
+
+    <!-- Setup click handler that retieves BrowserID assertion code and sends
+         POST data -->
+    <script type="text/javascript">
+        $(function () {
+            $('#browserid').click(function (e) {
+                e.preventDefault();
+                var self = $(this);
+
+                navigator.id.get(function (assertion) {
+                    if (assertion) {
+                        self.parent('form')
+                                .find('input[type=hidden]')
+                                    .attr('value', assertion)
+                                    .end()
+                                .submit();
+                    } else {
+                        alert('Some error occurred');
+                    }
+                });
+            });
+        });
+    </script>
+
+
 -------------
 Miscellaneous
 -------------
@@ -964,7 +1012,7 @@ Base work is copyrighted by:
 .. _micrypt: https://github.com/micrypt
 .. _South: http://south.aeracode.org/
 .. _bedspax: https://github.com/bedspax
-.. _django-social-auth: https://convore.com/django-social-auth/
+.. _django-social-auth: https://github.com/omab/django-social-auth
 .. _Convore: https://convore.com/
 .. _Selenium: http://seleniumhq.org/
 .. _LinkedIn fields selectors: http://developer.linkedin.com/docs/DOC-1014
@@ -979,3 +1027,4 @@ Base work is copyrighted by:
 .. _Flickr App Garden: http://www.flickr.com/services/apps/create/
 .. _danielgtaylor: https://github.com/danielgtaylor
 .. _example application: https://github.com/omab/django-social-auth/blob/master/example/local_settings.py.template#L23
+.. _BrowserID: https://browserid.org
diff --git a/doc/backends/browserid.rst b/doc/backends/browserid.rst
new file mode 100644 (file)
index 0000000..021ad48
--- /dev/null
@@ -0,0 +1,14 @@
+---------
+BrowserID
+---------
+Support for BrowserID_ is possible by posting the ``assertion`` code to
+``/complete/browserid/`` URL.
+
+The setup doesn't need any setting, just the usual BrowserID_ javascript
+include in your document and the needed mechanism to trigger the POST to
+`django-social-auth`_.
+
+Check the second "Use Case" for an implementation example.
+
+.. _django-social-auth: https://github.com/omab/django-social-auth
+.. _BrowserID: https://browserid.org
index 91b5728c021b6f542a93925c1f0a8604c6cf3e52..c3cd3449f576e9b9984f1e06f470153f71b611b8 100644 (file)
@@ -17,6 +17,7 @@ Configuration
         'social_auth.backends.google.GoogleOAuth2Backend',
         'social_auth.backends.google.GoogleBackend',
         'social_auth.backends.yahoo.YahooBackend',
+        'social_auth.backends.browserid.BrowserIDBackend',
         'social_auth.backends.contrib.linkedin.LinkedinBackend',
         'social_auth.backends.contrib.livejournal.LiveJournalBackend',
         'social_auth.backends.contrib.orkut.OrkutBackend',
index 410e69f63c5d492ba21fbf74f51a8876c9ec23a7..1c1b4ee5293d8d4ac6d271262273e281e79095e8 100644 (file)
@@ -26,6 +26,7 @@ package and link it there.
 
 
 .. _South: http://south.aeracode.org/
-.. _django-social-auth: https://convore.com/django-social-auth/
+.. _django-social-auth: https://github.com/omab/django-social-auth
 .. _Convore: https://convore.com/
 .. _djangopackages.com: http://djangopackages.com/grids/g/social-auth-backends/
+
index daec210a5943883ba315241c70d7694d64637928..2934b86aa81598c114792c87798060d88c07366d 100644 (file)
@@ -15,3 +15,36 @@ Some particular use cases are listed below.
         url(r'^disconnect/(?P<backend>[^/]+)/(?P<association_id>[^/]+)/$',
             disconnect, name='socialauth_disconnect_individual'),
     )
+
+2. Include a similar snippet in your page to make BrowserID_ work::
+    <!-- Include BrowserID JavaScript -->
+    <script src="https://browserid.org/include.js" type="text/javascript"></script>
+
+    <!-- Define a form to send the POST data -->
+    <form method="post" action="{% url socialauth_complete "browserid" %}">
+        <input type="hidden" name="assertion" value="" />
+        <a rel="nofollow" id="browserid" href="#">BrowserID</a>
+    </form>
+
+    <!-- Setup click handler that retieves BrowserID assertion code and sends
+         POST data -->
+    <script type="text/javascript">
+        $(function () {
+            $('#browserid').click(function (e) {
+                e.preventDefault();
+                var self = $(this);
+
+                navigator.id.get(function (assertion) {
+                    if (assertion) {
+                        self.parent('form')
+                                .find('input[type=hidden]')
+                                    .attr('value', assertion)
+                                    .end()
+                                .submit();
+                    } else {
+                        alert('Some error occurred');
+                    }
+                });
+            });
+        });
+    </script>
index 6fc3b9bf7a19da54a84998871adf7f1d665bda6b..5bd8ad31c6400094191ee0c7c4012d2912de8e98 100644 (file)
@@ -78,6 +78,7 @@ AUTHENTICATION_BACKENDS = (
     'social_auth.backends.contrib.flickr.FlickrBackend',
     'social_auth.backends.OpenIDBackend',
     'social_auth.backends.contrib.livejournal.LiveJournalBackend',
+    'social_auth.backends.browserid.BrowserIDBackend',
     'django.contrib.auth.backends.ModelBackend',
 )
 
index b528e2fd37a571fb6ad83fed5f4ae9869bbdf1c2..9e1fd712e23380c479904a96ccc43a3856045ec3 100644 (file)
@@ -30,6 +30,8 @@
       #valid-badges {position: fixed; right: 10px; bottom: 10px;}
       #valid-badges p {display: inline;}
     </style>
+
+    {% block script %}{% endblock %}
   </head>
   <body>
     <h1>Django Social Auth (v{{ version }})</h1>
index 6b12fd974c265a8ce47a46ee255592d327f83910..2ef4e0dc13011d59a73404c0c76df53a4d01fe91 100644 (file)
@@ -1,5 +1,10 @@
 {% extends "base.html" %}
 
+{% block script %}
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
+<script src="https://browserid.org/include.js" type="text/javascript"></script>
+{% endblock %}
+
 {% block heading %}Logged in!{% endblock %}
 
 {% block content %}
     </li>
   {% endfor %}
   </ul>
+
+  <h3>Associate new <a href="https://browserid.org/" title="BrowserID">BrowserID</a>:</h3>
+  <form method="post" action="{% url socialauth_complete "browserid" %}">
+    <input type="hidden" name="assertion" value="" />
+    <a rel="nofollow" id="browserid" href="#">BrowserID</a>
+    <script type="text/javascript">
+      $(function () {
+        $('#browserid').click(function (e) {
+          e.preventDefault();
+          var self = $(this);
+
+          navigator.id.get(function (assertion) {
+            if (assertion) {
+              self.parent('form')
+                    .find('input[type=hidden]')
+                      .attr('value', assertion)
+                    .end()
+                    .submit();
+            } else {
+              alert('Some error occurred');
+            }
+          });
+        });
+      });
+    </script>
+  </form>
 </div>
 
 <div>
index 1a39af4d174451fd76c850026717ee91e2afcbc9..c90ba1c8b76a765f687d3fb7e7f30928493538ca 100644 (file)
@@ -1,5 +1,10 @@
 {% extends "base.html" %}
 
+{% block script %}
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
+<script src="https://browserid.org/include.js" type="text/javascript"></script>
+{% endblock %}
+
 {% block heading %}Login using any of the following methods{% endblock %}
 
 {% block content %}
   {% endfor %}
   </ul>
 </div>
+
+<div>
+  <h3>Login using <a href="https://browserid.org/" title="BrowserID">BrowserID</a>:</h3>
+  <form method="post" action="{% url socialauth_complete "browserid" %}">
+    <input type="hidden" name="assertion" value="" />
+    <a rel="nofollow" id="browserid" href="#">BrowserID</a>
+    <script type="text/javascript">
+      $(function () {
+        $('#browserid').click(function (e) {
+          e.preventDefault();
+          var self = $(this);
+
+          navigator.id.get(function (assertion) {
+            if (assertion) {
+              self.parent('form')
+                    .find('input[type=hidden]')
+                      .attr('value', assertion)
+                    .end()
+                    .submit();
+            } else {
+              alert('Some error occurred');
+            }
+          });
+        });
+      });
+    </script>
+  </form>
+</div>
 {% endblock %}
index 4a946d243c23c3f7d8fffbf8c79b8954b230c35a..b52869cccfc0a07ebae66d8929abcc3a8b24db10 100644 (file)
@@ -150,7 +150,7 @@ class SocialAuthBackend(ModelBackend):
 
     def extra_data(self, user, uid, response, details):
         """Return default blank user extra data"""
-        return ''
+        return {}
 
     def get_user_id(self, details, response):
         """Must return a unique ID from values returned on details"""
@@ -634,9 +634,11 @@ if setting('SOCIAL_AUTH_IMPORT_BACKENDS'):
     from warnings import warn
     warn("SOCIAL_AUTH_IMPORT_SOURCES is deprecated")
 
+
 # Cache for discovered backends.
 BACKENDSCACHE = {}
 
+
 def get_backends(force_load=False):
     """
     Entry point to the BACKENDS cache. If BACKENDSCACHE hasn't been
@@ -685,6 +687,7 @@ def get_backend(name, *args, **kwargs):
         except KeyError:
             return None
 
+
 BACKENDS = {
     'openid': OpenIdAuth
 }
diff --git a/social_auth/backends/browserid.py b/social_auth/backends/browserid.py
new file mode 100644 (file)
index 0000000..a8774b5
--- /dev/null
@@ -0,0 +1,90 @@
+"""
+BrowserID support
+"""
+import time
+from datetime import datetime
+from urllib import urlencode
+from urllib2 import urlopen
+
+from django.contrib.auth import authenticate
+from django.utils import simplejson
+
+from social_auth.backends import SocialAuthBackend, BaseAuth, USERNAME
+from social_auth.utils import log, setting
+
+
+# BrowserID verification server
+BROWSER_ID_SERVER = 'https://browserid.org/verify'
+
+
+class BrowserIDBackend(SocialAuthBackend):
+    """BrowserID authentication backend"""
+    name = 'browserid'
+
+    def get_user_id(self, details, response):
+        """Use BrowserID email as ID"""
+        return details['email']
+
+    def get_user_details(self, response):
+        """Return user details, BrowserID only provides Email."""
+        # {'status': 'okay',
+        #  'audience': 'localhost:8000',
+        #  'expires': 1328983575529,
+        #  'email': 'name@server.com',
+        #  'issuer': 'browserid.org'}
+        email = response['email']
+        return {USERNAME: email.split('@', 1)[0],
+                'email': email,
+                'fullname': '',
+                'first_name': '',
+                'last_name': ''}
+
+    def extra_data(self, user, uid, response, details):
+        """Return users extra data"""
+        # BrowserID sends timestamp for expiration date, here we
+        # comvert it to the remaining seconds
+        expires = (response['expires'] / 1000) - \
+                  time.mktime(datetime.now().timetuple())
+        return {
+            'audience': response['audience'],
+            'issuer': response['issuer'],
+            setting('SOCIAL_AUTH_EXPIRATION', 'expires'): expires
+        }
+
+
+# Auth classes
+class BrowserIDAuth(BaseAuth):
+    """BrowserID authentication"""
+    AUTH_BACKEND = BrowserIDBackend
+
+    def auth_complete(self, *args, **kwargs):
+        """Completes loging process, must return user instance"""
+        if not 'assertion' in self.data:
+            raise ValueError('Missing assertion parameter')
+
+        data = urlencode({
+            'assertion': self.data['assertion'],
+            'audience': self.request.get_host()
+        })
+
+        try:
+            response = simplejson.load(urlopen(BROWSER_ID_SERVER, data=data))
+        except ValueError:
+            log('error', 'Could not load user data from BrowserID.',
+                exc_info=True)
+        else:
+            if response.get('status') == 'failure':
+                log('debug', 'Authentication failed.')
+                raise ValueError('Authentication failed')
+
+            kwargs.update({
+                'response': response,
+                self.AUTH_BACKEND.name: True
+            })
+            return authenticate(*args, **kwargs)
+
+
+# Backend definition
+BACKENDS = {
+    'browserid': BrowserIDAuth
+}