Commits

Alen Mujezinovic committed 5d33cb9

Social registration application for django

  • Participants

Comments (0)

Files changed (28)

+* Alen Mujezinovic
+Copyright (c) 2009 Caffeinehit Ltd, Alen Mujezinovic and Contributors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+include AUTHORS
+include LICENSE
+include README
+==========================
+Django Social Registration
+==========================
+
+Django Social Registration enables developers to add alternative registration
+methods based on third party sites.
+
+Requirements
+============
+django
+oauth
+python-openid
+
+Installation
+============
+
+#. Add the *socialregistration* directory to your *PYTHON_PATH*.
+#. Add *socialregistration* to your *INSTALLED_APPS* settings of Django.
+#. Add *socialregistration.urls* to your *urls.py* file.
+
+Configuration
+=============
+
+Facebook Connect
+----------------
+#. Add *FACEBOOK_API_KEY* and *FACEBOOK_SECRET_KEY* to your settings file 
+representing the keys you were given by Facebook.
+
+#. Add tags to your template file:
+
+	{% load facebook_tags %} 
+ 	{% facebook_button %}
+ 	{% facebook_js %}
+ 
+Twitter
+-------
+#. Add the following variables to your *settings.py* file with the values you 
+were given by Twitter:
+
+	TWITTER_CONSUMER_KEY
+	TWITTER_CONSUMER_SECRET_KEY
+	TWITTER_REQUEST_TOKEN_URL
+	TWITTER_ACCESS_TOKEN_URL
+	TWITTER_AUTHORIZATION_URL
+
+#. Add tags to your template file:
+	{% load twitter_tags %}
+	{% twitter_button %}
+
+Other OAuth Services
+--------------------
+There is an example of how FriendFeed integration could work. 
+*socialregistration.models* provides a *FriendFeedProfile* model to save account
+data, *socialregistration.auth* provides examples for different auth backends for
+different service providers, *socialregistration.utils* provides a Twitter
+and FriendFeed interface and *socialregistration.urls* provides examples based
+on Twitter and FriendFeed how to hook in more OAuth based services.
+
+OpenID
+------
+#. Add tags to your template file:
+	{% load openid_tags %}
+	{% openid_form %}
+#!/usr/bin/env python
+
+METADATA = dict(
+    name='django-socialregistration',
+    version='0.1',
+    author='Alen Mujezinovic',
+    author_email='alen@caffeinehit.com',
+    description='Django application enabling registration through a variety of APIs',
+    long_description=open('README').read(),
+    url='http://code.google.com/p/django-socialregistration',
+    keywords='django facebook twitter oauth openid registration',
+)
+SETUPTOOLS_METADATA = dict(
+    install_requires=['setuptools', 'django', 'oauth', 'python-openid'],
+    include_package_data=True,
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: Developers',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+        'Environment :: Web Environment',
+        'Topic :: Internet',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Framework :: Django',
+    ],
+    packages=['socialregistration']
+)
+
+if __name__ == '__main__':
+    try:
+        import setuptools
+        METADATA.update(SETUPTOOLS_METADATA)
+        setuptools.setup(**METADATA)
+    except ImportError:
+        import distutils.core
+        distutils.core.setup(**METADATA)

socialregistration/__init__.py

Empty file added.

socialregistration/admin.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from django.contrib import admin
+from socialregistration.models import (FacebookProfile, TwitterProfile,
+    OpenIDProfile, OpenIDStore, OpenIDNonce)
+
+admin.site.register([FacebookProfile, TwitterProfile, OpenIDProfile, OpenIDStore, OpenIDNonce])
+
+

socialregistration/auth.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+
+from socialregistration.models import (FacebookProfile, TwitterProfile,
+    FriendFeedProfile, OpenIDProfile)
+
+class Auth(object):
+    def get_user(self, user_id):
+        try:
+            return User.objects.get(pk=user_id)
+        except User.DoesNotExist:
+            return None
+
+class FacebookAuth(Auth):
+    def authenticate(self, uid=None):
+        try:
+            return FacebookProfile.objects.get(
+                uid=uid,
+                site=Site.objects.get_current()
+            ).user
+        except:
+            return None
+
+class TwitterAuth(Auth):
+    def authenticate(self, twitter_id=None):
+        try:
+            return TwitterProfile.objects.get(
+                twitter_id=twitter_id,
+                site=Site.objects.get_current()
+            ).user
+        except:
+            return None
+        
+class OpenIDAuth(Auth):
+    def authenticate(self, identity=None):
+        try:
+            return OpenIDProfile.objects.get(
+                identity=identity,
+                site=Site.objects.get_current()
+            ).user
+        except:
+            return None

socialregistration/forms.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from django import forms
+from django.utils.translation import gettext as _
+
+from django.contrib.auth.models import User
+
+class UserForm(forms.Form):
+    username = forms.RegexField(r'\w+', max_length=255,)
+    email = forms.EmailField(required=False)
+    
+    def __init__(self, user, profile, *args, **kwargs):
+        super(UserForm, self).__init__(*args, **kwargs)
+        self.user = user
+        self.profile = profile
+    
+    def clean_username(self):
+        username = self.cleaned_data.get('username')
+        try:
+            user = User.objects.get(username=username)
+        except User.DoesNotExist:
+            return username
+        else:
+            raise forms.ValidationError(_('This username is already in use.'))
+    
+    def save(self):
+        self.user.username = self.cleaned_data.get('username')
+        self.user.email = self.cleaned_data.get('email')
+        self.user.save()
+        self.profile.user = self.user
+        self.profile.save()        
+        return self.user

socialregistration/models.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+
+from django.db import models
+
+from django.contrib.auth import authenticate
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+# Create your models here.
+
+
+class FacebookProfile(models.Model):
+    user = models.ForeignKey(User)
+    site = models.ForeignKey(Site, default=Site.objects.get_current)
+    uid = models.CharField(max_length=255, blank=False, null=False)
+    
+    def __unicode__(self):
+        return '%s: %s' % (self.user, self.uid)
+    
+    def authenticate(self):
+        return authenticate(uid=self.uid)
+    
+class TwitterProfile(models.Model):
+    user = models.ForeignKey(User)
+    site = models.ForeignKey(Site, default=Site.objects.get_current)
+    twitter_id = models.PositiveIntegerField()
+    
+    def __unicode__(self):
+        return '%s: %s' % (self.user, self.twitter_id)
+    
+    def authenticate(self):
+        return authenticate(twitter_id=self.twitter_id)
+
+class FriendFeedProfile(models.Model):
+    user = models.ForeignKey(User)
+    site = models.ForeignKey(Site, default=Site.objects.get_current)
+
+class OpenIDProfile(models.Model):
+    user = models.ForeignKey(User)
+    site = models.ForeignKey(Site, default=Site.objects.get_current)
+    identity = models.TextField()
+    
+    def authenticate(self):
+        return authenticate(identity=self.identity)
+
+class OpenIDStore(models.Model):
+    site = models.ForeignKey(Site, default=Site.objects.get_current)
+    server_url = models.CharField(max_length=255)
+    handle = models.CharField(max_length=255)
+    secret = models.TextField()
+    issued = models.IntegerField()
+    lifetime = models.IntegerField()
+    assoc_type = models.TextField()
+
+class OpenIDNonce(models.Model):
+    server_url = models.CharField(max_length=255)
+    timestamp = models.IntegerField()
+    salt = models.CharField(max_length=255)
+    date_created = models.DateTimeField(auto_now_add=True)
+    

socialregistration/templates/socialregistration/facebook.html

Empty file added.

socialregistration/templates/socialregistration/facebook_button.html

+
+<form class="connect-button" name="login" method="post" action="{% if logged_in %}{% url facebook_connect %}{% else %}{% url facebook_login %}{% endif %}">
+{% if next %}
+    <input type="hidden" name="next" value="{{ next }}" />
+{% endif %}
+<input type="image" onclick="facebookConnect(this.form);return false;" src="http://static.ak.fbcdn.net/images/fbconnect/login-buttons/connect_light_large_long.gif" />
+</form>

socialregistration/templates/socialregistration/facebook_js.html

+<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US" type="text/javascript"></script>
+<script type="text/javascript">
+    FB_RequireFeatures(["XFBML"],
+        function() {
+            FB.Facebook.init("{{ facebook_api_key }}", "{% url facebook_xd_receiver %}")
+        }
+    );
+    function facebookConnect(form){
+        FB.Connect.requireSession();
+        FB.Facebook.get_sessionState().waitUntilReady(
+            function(){
+                form.submit();
+            }
+        );
+    }
+</script>

socialregistration/templates/socialregistration/friendfeed_button.html

Empty file added.

socialregistration/templates/socialregistration/openid.html

Empty file added.

socialregistration/templates/socialregistration/openid_form.html

+<form action="{% url openid_redirect %}" method="GET">
+    <input type="text" name="openid_provider" />
+    <input type="submit" value="Connect with OpenID" />
+</form>

socialregistration/templates/socialregistration/setup.html

+<form action="." method="post">
+    <table>
+        {{ form }}
+    </table>
+    <input type="submit" value="save" />
+</form>

socialregistration/templates/socialregistration/twitter_button.html

+{% load twitter_tags %}
+<form class="connect-button" name="login" method="post" action="{% url twitter_redirect %}">
+{% if next %}
+    <input type="hidden" name="next" value="{{ next }}" />
+{% endif %}
+<input type="image" onclick="this.form.submit();" src="http://apiwiki.twitter.com/f/1242697608/Sign-in-with-Twitter-lighter.png" />
+</form>

socialregistration/templates/socialregistration/xd_receiver.html

+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title></title>
+</head>
+<body>
+<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.debug.js" type="text/javascript">
+</script>
+</body>
+</html>

socialregistration/templatetags/__init__.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""

socialregistration/templatetags/facebook_tags.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from django import template
+from django.conf import settings
+
+register = template.Library()
+
+@register.inclusion_tag('socialregistration/facebook_js.html')
+def facebook_js():
+    return {'facebook_api_key' : settings.FACEBOOK_API_KEY}
+
+@register.inclusion_tag('socialregistration/facebook_button.html', takes_context=True)
+def facebook_button(context):
+    if not 'request' in context:
+        raise AttributeError, 'Please add the ``django.core.context_processors.request`` context processors to your settings.CONTEXT_PROCESSORS set'
+    logged_in = context['request'].user.is_authenticated()
+    next = context['next'] if 'next' in context else None
+    return dict(next=next, logged_in=logged_in)

socialregistration/templatetags/friendfeed_tags.py

+"""
+Created on 25.09.2009
+
+@author: alen
+"""
+from django import template
+
+register = template.Library()
+
+@register.inclusion_tag('socialregistration/friendfeed_button.html')
+def friendfeed_button():
+    return {}

socialregistration/templatetags/openid_tags.py

+"""
+Created on 24.09.2009
+
+@author: alen
+"""
+from django import template
+
+register = template.Library()
+
+@register.inclusion_tag('socialregistration/openid_form.html')
+def openid_form():
+    return {}

socialregistration/templatetags/twitter_tags.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from django import template
+
+register = template.Library()
+
+@register.inclusion_tag('socialregistration/twitter_button.html', takes_context=True)
+def twitter_button(context):
+    if not 'request' in context:
+        raise AttributeError, 'Please add the ``django.core.context_processors.request`` context processors to your settings.CONTEXT_PROCESSORS set'
+    logged_in = context['request'].user.is_authenticated()
+    next = context['next'] if 'next' in context else None
+    return dict(next=next, logged_in=logged_in)

socialregistration/tests.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.failUnlessEqual(1 + 1, 2)
+
+__test__ = {"doctest": """
+Another way to test that 1 + 1 is equal to 2.
+
+>>> 1 + 1 == 2
+True
+"""}
+

socialregistration/urls.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from django.conf import settings
+from django.conf.urls.defaults import *
+
+
+urlpatterns = patterns('',
+    url('^setup/$', 'socialregistration.views.setup',
+        name='socialregistration_setup'),
+)
+
+# Setup Facebook URLs if there's an API key specified
+if getattr(settings, 'FACEBOOK_API_KEY', None) is not None:
+    urlpatterns = urlpatterns + patterns('',
+        url('^facebook/login/$', 'socialregistration.views.facebook_login',
+            name='facebook_login'),
+        
+        url('^facebook/connect/$', 'socialregistration.views.facebook_connect',
+            name='facebook_connect'),
+        
+        url('^xd_receiver.html$', 'django.views.generic.simple.direct_to_template',
+            {'template':'socialregistration/xd_receiver.html'},
+            name='facebook_xd_receiver'),
+    )
+
+#Setup Twitter URLs if there's an API key specified
+if getattr(settings, 'TWITTER_CONSUMER_KEY', None) is not None:
+    urlpatterns = urlpatterns + patterns('',
+        url('^twitter/redirect/$', 'socialregistration.views.oauth_redirect',
+            dict(
+                consumer_key=settings.TWITTER_CONSUMER_KEY,
+                secret_key=settings.TWITTER_CONSUMER_SECRET_KEY,
+                request_token_url=settings.TWITTER_REQUEST_TOKEN_URL,
+                access_token_url=settings.TWITTER_ACCESS_TOKEN_URL,
+                authorization_url=settings.TWITTER_AUTHORIZATION_URL,
+                callback_url='twitter_callback'
+            ),
+            name='twitter_redirect'),
+        
+        url('^twitter/callback/$', 'socialregistration.views.oauth_callback',
+            dict(
+                consumer_key=settings.TWITTER_CONSUMER_KEY,
+                secret_key=settings.TWITTER_CONSUMER_SECRET_KEY,
+                request_token_url=settings.TWITTER_REQUEST_TOKEN_URL,
+                access_token_url=settings.TWITTER_ACCESS_TOKEN_URL,
+                authorization_url=settings.TWITTER_AUTHORIZATION_URL,
+                callback_url='twitter'
+            ),
+            name='twitter_callback'
+        ),
+        url('^twitter/$', 'socialregistration.views.twitter', name='twitter'),
+    )
+    
+# Setup FriendFeed URLs if there's an API key specified
+if getattr(settings, 'FRIENDFEED_CONSUMER_KEY', None) is not None:
+    urlpatterns = urlpatterns + patterns('',
+        url('^friendfeed/redirect/$', 'socialregistration.views.oauth_redirect',
+            dict(
+                consumer_key=settings.FRIENDFEED_CONSUMER_KEY,
+                secret_key=settings.FRIENDFEED_CONSUMER_SECRET_KEY,
+                request_token_url=settings.FRIENDFEED_REQUEST_TOKEN_URL,
+                access_token_url=settings.FRIENDFEED_ACCESS_TOKEN_URL,
+                authorization_url=settings.FRIENDFEED_AUTHORIZATION_URL,
+                callback_url='friendfeed_callback'
+            ),
+            name='friendfeed_redirect'),
+        
+        url('^friendfeed/callback/$', 'socialregistration.views.oauth_callback',
+            dict(
+                consumer_key=settings.FRIENDFEED_CONSUMER_KEY,
+                secret_key=settings.FRIENDFEED_CONSUMER_SECRET_KEY,
+                request_token_url=settings.FRIENDFEED_REQUEST_TOKEN_URL,
+                access_token_url=settings.FRIENDFEED_ACCESS_TOKEN_URL,
+                authorization_url=settings.FRIENDFEED_AUTHORIZATION_URL,
+                callback_url='friendfeed'
+            ),
+            name='friendfeed_callback'
+        ),
+        url('^friendfeed/$', 'socialregistration.views.friendfeed', name='friendfeed'),
+    )
+
+urlpatterns = urlpatterns + patterns('',
+    url('^openid/redirect/', 'socialregistration.views.openid_redirect', name='openid_redirect'),
+    url('^openid/callback/', 'socialregistration.views.openid_callback', name='openid_callback')
+)

socialregistration/utils.py

+"""
+Created on 22.09.2009
+
+@author: alen
+Inspired by:
+    http://github.com/leah/python-oauth/blob/master/oauth/example/client.py
+    http://github.com/facebook/tornado/blob/master/tornado/auth.py
+"""
+import time
+import base64
+import urllib
+import urllib2
+
+from oauth import oauth
+from openid.consumer import consumer as openid
+from openid.store.interface import OpenIDStore as OIDStore
+from openid.association import Association as OIDAssociation
+
+from django.http import HttpResponseRedirect, HttpResponseServerError
+from django.core.urlresolvers import reverse
+from django.utils.translation import gettext as _
+
+from django.utils import simplejson
+
+from django.contrib.sites.models import Site
+
+from socialregistration.models import OpenIDStore as OpenIDStoreModel, OpenIDNonce
+
+
+class OpenIDStore(OIDStore):
+    max_nonce_age = 6 * 60 * 60
+    
+    def storeAssociation(self, server_url, assoc=None):
+        stored_assoc = OpenIDStoreModel.objects.create(
+            server_url=server_url,
+            handle=assoc.handle,
+            secret=base64.encodestring(assoc.secret),
+            issued=assoc.issued,
+            lifetime=assoc.issued,
+            assoc_type=assoc.assoc_type
+        )
+        
+    
+    def getAssociation(self, server_url, handle=None):
+        stored_assocs = OpenIDStoreModel.objects.filter(
+            server_url=server_url
+        )
+        if handle:
+            stored_assocs = stored_assocs.filter(handle=handle)
+        
+        stored_assocs.order_by('-issued')
+        
+        if stored_assocs.count() == 0:
+            return None
+        
+        stored_assoc = stored_assocs[0]
+        
+        assoc = OIDAssociation(
+            stored_assoc.handle, base64.decodestring(stored_assoc.secret),
+            stored_assoc.issued, stored_assoc.lifetime, stored_assoc.assoc_type
+        )
+        
+        return assoc
+
+    def useNonce(self, server_url, timestamp, salt):
+        try:
+            nonce = OpenIDNonce.objects.get(
+                server_url=server_url,
+                timestamp=timestamp,
+                salt=salt
+            )
+        except OpenIDNonce.DoesNotExist:
+            nonce = OpenIDNonce.objects.create(
+                server_url=server_url,
+                timestamp=timestamp,
+                salt=salt
+            )
+            return True
+        
+        return False
+            
+
+class OpenID(object):
+    def __init__(self, request, return_to, endpoint):
+        """
+        @param request: : django.http.HttpRequest object
+        @param return_to: URL to redirect back to once the user authenticated
+            the application on the OpenID provider
+        @param endpoint: URL to the OpenID provider we're connecting to
+        """
+        self.request = request
+        self.return_to = return_to
+        self.endpoint = endpoint
+        self.store = OpenIDStore()
+        self.consumer = openid.Consumer(self.request.session, self.store)
+
+        self.result = None
+    
+    def get_redirect(self):
+        auth_request = self.consumer.begin(self.endpoint)
+        redirect_url = auth_request.redirectURL(
+            'http://%s/' % Site.objects.get_current().domain,
+            self.return_to
+        )
+        return HttpResponseRedirect(redirect_url)
+    
+    def complete(self):
+        self.result = self.consumer.complete(
+            dict(self.request.GET.items()),
+            'http://%s%s' % (Site.objects.get_current(), self.request.path)
+        )
+        
+    def is_valid(self):
+        if self.result is None:
+            self.complete()
+            
+        return self.result.status == openid.SUCCESS
+
+class OAuthClient(oauth.OAuthClient):
+    """
+    Simple OAuth client to perform OAuth requests 
+    ( primarily connect accounts - for other requests see class OAuth below )
+    """
+    
+    def __init__(self, request, consumer_key, consumer_secret,
+        request_token_url, access_token_url, authorization_url, callback_url):
+    
+        self.request = request
+    
+        self.request_token_url = request_token_url
+        self.access_token_url = access_token_url
+        self.authorization_url = authorization_url
+    
+        self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
+        self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+        
+        self.errors = []
+        
+        self.callback_url = callback_url
+
+    
+    def _get_response(self, oauth_request):
+        try:
+            return urllib2.urlopen(oauth_request.to_url()).read()
+        except:
+            raise HttpResponseServerError(_('We couldn\'t reach the service. Please try again later.'))
+        
+    def get_request_token(self):
+        """
+        Get a request token
+        """
+        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+            self.consumer, http_url=self.request_token_url
+        )
+        oauth_request.sign_request(self.signature_method, self.consumer, None)
+        response = self._get_response(oauth_request)
+        return oauth.OAuthToken.from_string(response)
+    
+    def get_access_token(self):
+        """
+        Get an access token
+        """
+        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+            self.consumer, http_url=self.access_token_url, token=self.token
+        )
+        oauth_request.sign_request(self.signature_method, self.consumer, self.token)
+        response = self._get_response(oauth_request)
+        return oauth.OAuthToken.from_string(response)
+    
+    def token_prefix(self):
+        """
+        Returns a prefix for the token to store in the session so we can hold
+        more than one single oauth provider's access key in the session 
+        """
+        if getattr(self, '_prefix', None) is None:
+            self._prefix = urllib2.urlparse.urlparse(self.request_token_url).netloc
+        return self._prefix
+    
+    @property
+    def token(self):
+        """ Short wrapper around get_request_token to cache the token """
+        if getattr(self, '_token', None) is None:
+            self._token = self.get_request_token()
+        return self._token
+    
+    def session_token(self):
+        """ Short wrapper around the token we've stored in the session """
+        return self.request.session.get(
+            'oauth_%s_unauthed_token' % self.token_prefix(),
+            None
+        )
+    
+    def get_authorization_url(self):
+        """
+        Returns the url to redirect the user to
+        """
+        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+            self.consumer,
+            http_url=self.authorization_url,
+            token=self.token
+        )
+        oauth_request.sign_request(self.signature_method, self.consumer, self.token)
+        return oauth_request.to_url()
+    
+    def get_redirect(self):
+        """
+        Returns a HttpResponseRedirect object to redirect the user to the url
+        where authorization of the current application is handled.
+        """
+        self.request.session['oauth_%s_unauthed_token' % self.token_prefix()] = self.token.to_string()
+        return HttpResponseRedirect(self.get_authorization_url())
+    
+    def is_valid(self):
+        """
+        Check if everything is valid after the user got redirected back to our 
+        site.
+        """
+        if not self.session_token():
+            self.errors.append(_('No un-authorized token given.'))
+            return False
+        
+        self._token = oauth.OAuthToken.from_string(self.session_token())
+        
+        if not self.token.key == self.request.GET.get('oauth_token', 'no-token-given'):
+            self.errors.append(_('The given authorization tokens do not match.'))
+            return False
+        
+        self._token = self.get_access_token()
+        self.request.session['oauth_%s_access_token' % self.token_prefix()] = self.token.to_string()
+        
+        return True
+
+class OAuth(object):
+    """
+    Base object to perform OAuth signed requests to a service provider
+    """      
+    def __init__(self, request, consumer_key, secret_key, request_token_url):
+        self.request = request
+        self.consumer = oauth.OAuthConsumer(consumer_key, secret_key)
+        self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+        
+        self.request_token_url = request_token_url
+
+    def token_prefix(self):
+        """ 
+        Create a prefix for the token so we can hold multiple different oauth
+        tokens in the session 
+        """
+        return urllib2.urlparse.urlparse(self.request_token_url).netloc
+
+    @property
+    def access_token(self):
+        if getattr(self, '_access_token', None) is None:
+            self._access_token = oauth.OAuthToken.from_string(
+                self.request.session['oauth_%s_access_token' % self.token_prefix()]
+            )
+        return self._access_token
+
+    def get_request(self, url, parameters=None):
+        """ Build a request object """
+        oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+            self.consumer, http_url=url, token=self.access_token,
+            parameters=parameters
+        )
+        oauth_request.sign_request(
+            self.signature_method, self.consumer, self.access_token
+        )
+        return oauth_request
+
+    def get_response(self, oauth_request):
+        """ Submit the request and fetch the response body 
+        TODO: Add POST support"""
+        try:
+            return urllib2.urlopen(oauth_request.to_url()).read()
+        except:
+            raise HttpResponseServerError(_('We couldn\'t reach the service. Please try again later'))
+
+    def query(self, url, parameters=None):
+        return self.get_response(
+            self.get_request(url, parameters)
+        )
+        
+class OAuthTwitter(OAuth):
+    """
+    Verifying twitter credentials
+    """
+    url = 'https://twitter.com/account/verify_credentials.json'
+    
+    def get_user_info(self):
+        user = simplejson.loads(self.query(self.url))
+        return user
+    
+class OAuthFriendFeed(OAuth):
+    """
+    Verifying friendfeed credentials
+    """
+    url = 'http://friendfeed-api.com/v2/validate'
+    
+    def get_user_info(self):
+        user = simplejson.loads(self.query(self.url))
+        return user

socialregistration/views.py

+"""
+Created on 22.09.2009
+
+@author: alen
+"""
+from oauth import oauth
+
+from django.conf import settings
+from django.template import RequestContext
+from django.core.urlresolvers import reverse
+from django.shortcuts import render_to_response
+from django.utils.translation import gettext as _
+from django.http import HttpResponseRedirect, HttpResponse
+
+from django.contrib.auth.models import User
+from django.contrib.auth import login, authenticate
+from django.contrib.sites.models import Site
+
+from socialregistration.forms import UserForm
+from socialregistration.utils import (OAuthClient, OAuthTwitter, OAuthFriendFeed,
+    OpenID)
+from socialregistration.models import FacebookProfile, TwitterProfile, OpenIDProfile
+
+
+FB_ERROR = _('We couldn\'t validate your Facebook credentials')
+
+def _get_next(request):
+    """
+    Returns a url to redirect to after the login
+    """
+    if 'next' in request.session:
+        next = request.session['next']
+        del request.session['next']
+        return next
+    elif 'next' in request.GET:
+        return request.GET.get('next')
+    elif 'next' in request.POST:
+        return request.POST.get('next')
+    else:
+        return getattr(settings, 'LOGIN_REDIRECT_URL', '/')
+
+def setup(request, template='socialregistration/setup.html',
+    form_class=UserForm, extra_context=dict()):
+    """
+    Setup view to create a username & set email address after authentication
+    """
+    if not request.method == "POST":
+        form = form_class(
+            request.session['socialregistration_user'],
+            request.session['socialregistration_profile'],
+        )
+    else:
+        form = form_class(
+            request.session['socialregistration_user'],
+            request.session['socialregistration_profile'],
+            request.POST
+        )
+        if form.is_valid():
+            form.save()
+            user = form.profile.authenticate()
+            login(request, user)
+            del request.session['socialregistration_user']
+            del request.session['socialregistration_profile']
+            return HttpResponseRedirect(_get_next(request))
+    
+    extra_context.update(dict(form=form))
+    
+    return render_to_response(
+        template,
+        extra_context,
+        context_instance=RequestContext(request)
+    )
+
+def facebook_login(request, template='socialregistration/facebook.html',
+    extra_context=dict()):
+    """
+    View to handle the Facebook login 
+    """
+    if not request.facebook.check_session(request):
+        extra_context.update(
+            dict(error=FB_ERROR)
+        )
+        return render_to_response(
+            template, extra_context, context_instance=RequestContext(request)
+        )
+    
+    user = authenticate(uid=request.facebook.uid)
+    
+    if user is None:
+        request.session['socialregistration_user'] = User()
+        request.session['socialregistration_profile'] = FacebookProfile(
+            uid=request.facebook.uid
+        )
+        request.session['next'] = _get_next(request)
+        return HttpResponseRedirect(reverse('socialregistration_setup'))
+    
+    login(request, user)
+    
+    return HttpResponseRedirect(_get_next(request))
+
+def facebook_connect(request, template='socialregistration/facebook.html',
+    extra_context=dict()):
+    """
+    View to handle connecting existing accounts with facebook
+    """
+    if not request.facebook.check_session(request) \
+        or not request.user.is_authenticated():
+        extra_context.update(
+            dict(error=FB_ERROR)
+        )
+        return render_to_response(
+            template,
+            extra_context,
+            context_dict=RequestContext(request)
+        )
+    
+    profile, created = FacebookProfile.objects.get_or_create(
+        user=request.user, uid=request.facebook.uid
+    )
+    
+    return HttpResponseRedirect(_get_next(request))
+
+def twitter(request):
+    """
+    Actually setup/login an account relating to a twitter user after the oauth 
+    process is finished successfully
+    """
+    client = OAuthTwitter(
+        request, settings.TWITTER_CONSUMER_KEY,
+        settings.TWITTER_CONSUMER_SECRET_KEY,
+        settings.TWITTER_REQUEST_TOKEN_URL,
+    )
+    
+    user_info = client.get_user_info()
+    
+    user = authenticate(twitter_id=user_info['id'])
+    
+    if user is None:
+        profile = TwitterProfile(twitter_id=user_info['id'])
+        user = User()
+        request.session['socialregistration_profile'] = profile
+        request.session['socialregistration_user'] = user
+        request.session['next'] = _get_next(request)
+        return HttpResponseRedirect(reverse('socialregistration_setup'))
+
+    login(request, user)
+    
+    return HttpResponseRedirect(_get_next(request))
+
+
+def friendfeed(request):
+    """
+    Actually setup an account relating to a friendfeed user after the oauth process
+    is finished successfully
+    """
+    raise NotImplementedError()
+
+def oauth_redirect(request, consumer_key=None, secret_key=None,
+    request_token_url=None, access_token_url=None, authorization_url=None,
+    callback_url=None):
+    """
+    View to handle the OAuth based authentication redirect to the service provider
+    """
+    request.session['next'] = _get_next(request)
+    client = OAuthClient(request, consumer_key, secret_key,
+        request_token_url, access_token_url, authorization_url, callback_url)
+    return client.get_redirect()
+
+def oauth_callback(request, consumer_key=None, secret_key=None,
+    request_token_url=None, access_token_url=None, authorization_url=None,
+    callback_url=None, template='socialregistration/oauthcallback.html',
+    extra_context=dict()):
+    """
+    View to handle final steps of OAuth based authentication where the user 
+    gets redirected back to from the service provider
+    """
+    client = OAuthClient(request, consumer_key, secret_key, request_token_url,
+        access_token_url, authorization_url, callback_url)
+    
+    extra_context.update(dict(oauth_client=client))
+    
+    if not client.is_valid():
+        return render_to_response(
+            template, extra_context, context_instance=RequestContext(request)
+        )
+    
+    # We're redirecting to the setup view for this oauth service
+    return HttpResponseRedirect(reverse(client.callback_url))
+
+def openid_redirect(request):
+    """
+    Redirect the user to the openid provider
+    """
+    request.session['next'] = _get_next(request)
+    request.session['openid_provider'] = request.GET.get('openid_provider')
+    
+    client = OpenID(
+        request,
+        'http://%s%s' % (
+            Site.objects.get_current().domain,
+            reverse('openid_callback')
+        ),
+        request.GET.get('openid_provider')
+    )
+    return client.get_redirect()
+
+def openid_callback(request, template='socialregistration/openid.html',
+    extra_context=dict()):
+    """
+    Catches the user when he's redirected back from the provider to our site
+    """
+    client = OpenID(
+        request,
+        'http://%s%s' % (
+            Site.objects.get_current().domain,
+            reverse('openid_callback')
+        ),
+        request.session.get('openid_provider')
+    )
+    
+    if client.is_valid():
+        user = authenticate(identity=request.GET.get('openid.claimed_id'))
+        if user is None:
+            request.session['socialregistration_user'] = User()
+            request.session['socialregistration_profile'] = OpenIDProfile(
+                identity=request.GET.get('openid.claimed_id')
+            )
+            return HttpResponseRedirect(reverse('socialregistration_setup'))
+        else:
+            login(request, user)
+            return HttpResponseRedirect(_get_next(request))            
+    
+    return render_to_response(
+        template,
+        dict(),
+        context_instance=RequestContext(request)
+    )