Skip to content
Browse files

Strawman for changing authenticate interface

  • Loading branch information...
1 parent 1f8b65c commit 331871191f34f7c352147628f5daa42aa39c726f @ozten ozten committed Nov 23, 2011
Showing with 115 additions and 33 deletions.
  1. +12 −0 CHANGELOG.rst
  2. +33 −13 README.rst
  3. +65 −17 django_browserid/auth.py
  4. +2 −1 django_browserid/context_processors.py
  5. +3 −2 django_browserid/views.py
View
12 CHANGELOG.rst
@@ -0,0 +1,12 @@
+CHANGELOG
+---------
+
+0.9.2 - *NOTE* get_audience API breakage - Train 2011-10-20 now accepts the scheme and port number as part of the audience.
+
+ get_audience(self, host, port) has become get_audience(self, request, host, port)
+
+0.9.1 - Create User refactoring, navigator.id, httplib2/verify fixes
+
+ auth.create_user and auth.filter_users_by_email are two integration points which you may wish to override.
+
+0.9.0 - betafarm production release
View
46 README.rst
@@ -35,7 +35,35 @@ Edit your ``urls.py`` file and add the following: ::
# ...
)
-You can also set the following optional in ``settings.py`` (they have sensible defaults): ::
+You can also set the following config in ``settings.py``: ::
+
+ # Note: No trailing slash
+ SITE_URL = 'https://example.com'
+
+BrowserID uses an assertion and an audience to verify a the user. This
+``SITE_URL`` is used to determine the audience. If you don't want to use
+SITE_URL or it is being used for another purpose, you can use PROTOCOL and
+DOMAIN, such as: ::
+
+ PROTOCOL = 'https://'
+ DOMAIN = 'example.com'
+ # Optional
+ PORT = 8001
+
+Either way, for security reasons, it is *very important* to set either SITE_URL
+or DOMAIN.
+
+You can also set the following optional config in ``settings.py``
+(they have sensible defaults): ::
+
+ # Path to redirect to on successful login.
+ LOGIN_REDIRECT_URL = '/'
+
+ # Path to redirect to on unsuccessful login attempt.
+ LOGIN_REDIRECT_URL_FAILURE = '/'
+
+Unless your really noodling around with BrowserID, you probably won't need these
+optional config in ``settings.py`` (they have sensible defaults): ::
# URL of a BrowserID verification service.
BROWSERID_VERIFICATION_URL = 'https://browserid.org/verify'
@@ -52,12 +80,6 @@ You can also set the following optional in ``settings.py`` (they have sensible d
# Create user accounts automatically if no user is found.
BROWSERID_CREATE_USER = True
- # Path to redirect to on successful login.
- LOGIN_REDIRECT_URL = '/'
-
- # Path to redirect to on unsuccessful login attempt.
- LOGIN_REDIRECT_URL_FAILURE = '/'
-
Somewhere in one of your templates, you'll need to create a link and a form with a single hidden input element, which you'll use to submit the BrowserID assertion to the server. If you want to use ``django_browserid.forms.BrowserIDForm``, you could use something like the following template snippet: ::
{% if not user.is_authenticated %}
@@ -109,12 +131,10 @@ If you do not wish to automatically create user accounts, you may manually verif
if request.method == 'POST':
form = BrowserIDForm(data=request.POST)
if not form.is_valid():
- # do something
- host = request.get_host()
- https = request.is_secure()
- audience = get_audience(host, https)
- result = verify(form.cleaned_data['assertion'], audience)
- # ...
+ result = verify(form.cleaned_data['assertion'], get_audience())
+ if result:
+ # check for user account, create account for new users, etc
+ user = my_get_or_create_user(result.email)
``result`` will be False if the assertion failed, or a dictionary similar to the following: ::
View
82 django_browserid/auth.py
@@ -1,4 +1,3 @@
-
try:
import json
except ImportError:
@@ -21,23 +20,61 @@
OKAY_RESPONSE = 'okay'
-class BrowserIDBackend(object):
- supports_anonymous_user = False
- supports_object_permissions = False
+def get_audience(request):
+ """Uses Django settings to format the audience.
- def get_audience(self, host, https):
- if https:
- scheme = 'https'
- default_port = 443
- else:
- scheme = 'http'
- default_port = 80
+ To use this function, make sure there is either a SITE_URL in
+ your settings.py file or PROTOCOL and DOMAIN.
- audience = "%s://%s" % (scheme, host)
- if ':' in host:
- return audience
+ Examples using SITE_URL:
+ SITE_URL = 'http://127.0.0.1:8001'
+ SITE_URL = 'https://example.com'
+ SITE_URL = 'http://example.com'
+
+ If you don't have a SITE_URL you can also use these varables:
+ PROTOCOL, DOMAIN, and (optionally) PORT.
+ Example 1:
+ PROTOCOL = 'https://'
+ DOMAIN = 'example.com'
+
+ Example 2:
+ PROTOCOL = 'http://'
+ DOMAIN = '127.0.0.1'
+ PORT = '8001'
+
+ If none are set, we trust the request to populate the audience.
+ This is *not secure*!
+ """
+ site_url = getattr(settings, 'SITE_URL', False)
+
+ # If we don't define it explicitly
+ if not site_url:
+ if request.is_secure():
+ req_proto = 'https://'
+ else:
+ req_proto = 'http://'
+ protocol = getattr(settings, 'PROTOCOL', req_proto)
+ req_domain = request.get_host()
+ if not getattr(settings, 'DOMAIN'):
+ log.warning('django-browserid WARNING you are missing '
+ 'settings.SITE_URL. This is not a secure way '
+ 'to verify assertions. Please fix me. '
+ 'Setting domain to %s.' % req_domain)
+
+ domain = getattr(settings, 'DOMAIN', req_domain)
+ standards = {'https://': 443, 'http://': 80}
+ port = getattr(settings, 'PORT', standards[protocol])
+ if port == standards[protocol]:
+ site_url = ''.join(map(str, (protocol, domain)))
else:
- return "%s:%s" % (audience, default_port)
+ site_url = ''.join(map(str, (protocol, domain, ':', port)))
+
+ return site_url
+
+
+class BrowserIDBackend(object):
+ supports_anonymous_user = False
+ supports_object_permissions = False
def _verify_http_request(self, url, qs):
params = {'timeout': getattr(settings, 'BROWSERID_HTTP_TIMEOUT',
@@ -86,8 +123,19 @@ def create_user(self, username, email):
"""Return object for a newly created user account."""
return User.objects.create_user(username, email)
- def authenticate(self, assertion=None, host=None, https=None):
- result = self.verify(assertion, self.get_audience(host, https))
+ def authenticate(self, assertion=None, audience=None):
+ """``django.contrib.auth`` compatible authentication method.
+
+ Given a BrowserID assertion and an audience, it attempts to
+ verify them and then extract the email address for the authenticated
+ user.
+
+ An audience should be in the form ``https://example.com`` or
+ ``http://localhost:8001``.
+
+ See django_browserid.auth.get_audience()
+ """
+ result = self.verify(assertion, audience)
if result is None:
return None
email = result['email']
View
3 django_browserid/context_processors.py
@@ -1,5 +1,6 @@
from django_browserid.forms import BrowserIDForm
+
def browserid_form(request):
"""
A context processor that adds a BrowserID form to the request
@@ -8,4 +9,4 @@ def browserid_form(request):
if request.user.is_authenticated():
return {}
else:
- return { 'browserid_form' : BrowserIDForm() }
+ return {'browserid_form': BrowserIDForm()}
View
5 django_browserid/views.py
@@ -4,6 +4,7 @@
from django.views.decorators.http import require_POST
from django_browserid.forms import BrowserIDForm
+from django_browserid.auth import get_audience
@require_POST
@@ -16,8 +17,8 @@ def verify(request, redirect_field_name=auth.REDIRECT_FIELD_NAME):
form = BrowserIDForm(data=request.POST)
if form.is_valid():
assertion = form.cleaned_data['assertion']
- user = auth.authenticate(assertion=assertion, host=request.get_host(),
- https=request.is_secure())
+ user = auth.authenticate(assertion=assertion,
+ audience=get_audience(request))
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect(redirect_to)

0 comments on commit 3318711

Please sign in to comment.
Something went wrong with that request. Please try again.