Facebook aided registration in Django
Compared to existing tools like Facebook login button or RPXnow this tool may be useful when you need more data from user than Facebook can provide with the login button or through RPX. You can display a nice one form to the user. If you don't need extra data from the user - you can stick with existing solutions.
Requirements - Facebook application
- At start we have to create a basic Facebooku application that will be used by the registration form (if you use RPX or FB Login button you have it already). In the app settings (second tab) we have set the website domain under which we will use the form.
- Write down the application identifier and "App Secret". You can even add that to settings.py.
Embedding the registration widget
The widget can be embedded by IFRAME or by XFBML. In case of simple iframe it will look like this:<iframe src="http://www.facebook.com/plugins/registration.php?
client_id=FACEBOOK_APP_ID&
redirect_uri=http://www.youdomain.com/url/receiver/&
fields=name,birthday,gender,location,email"
scrolling="auto"
frameborder="no"
style="border:none"
allowTransparency="true"
width="100%"
height="310px">
</iframe>
Receiving registration data
Facebook will send the data to the specified URL via POST request. Under signed_request we will get a string containing a signature(dot)registration_data. The registration data is JSON encoded by base64. We have to split the string, decode the data and check if the signature is valid (if this is valid Facebook response and not some hacking). The signature is made from the JSON data and "App Secret" of you app.
This snippet of code (helper function + Django view) will handle checking and decoding the registration data and making a new user if he isn't registered yet:from random import choice
import hashlib
import urllib2
import json
import base64
import hmac
def parse_signed_request(signed_request, app_secret):
"""Return dictionary with signed request data.
Code taken from https://github.com/facebook/python-sdk"""
try:
l = signed_request.split('.', 2)
encoded_sig = str(l[0])
payload = str(l[1])
except IndexError:
raise ValueError("'signed_request' malformed")
sig = base64.urlsafe_b64decode(encoded_sig + "=" * ((4 - len(encoded_sig) % 4) % 4))
data = base64.urlsafe_b64decode(payload + "=" * ((4 - len(payload) % 4) % 4))
data = json.loads(data)
if data.get('algorithm').upper() != 'HMAC-SHA256':
raise ValueError("'signed_request' is using an unknown algorithm")
else:
expected_sig = hmac.new(app_secret, msg=payload, digestmod=hashlib.sha256).digest()
if sig != expected_sig:
raise ValueError("'signed_request' signature mismatch")
else:
return data
def fb_registration(request):
"""
Register a user from Facebook-aided registration form
"""
if request.POST:
if 'signed_request' in request.POST:
# parse and check data
data = parse_signed_request(request.POST['signed_request'], settings.FACEBOOK_CONNECT_SECRET)
# lets try to check if user exists based on username or email
try:
check_user = User.objects.get(username=data['registration']['name'])
except:
pass
else:
return HttpResponseRedirect('/user/login/')
try:
check_user = User.objects.get(email=data['registration']['email'])
except:
pass
else:
return HttpResponseRedirect('/user/login/')
# user does not exist. We create an account
# in this example I assume that he will login via Facebook button or RPXnow
# so no password is needed for him - using random password to make Django happy
randompass = ''.join([choice('1234567890qwertyuiopasdfghjklzxcvbnm') for i in range(7)])
user = User.objects.create_user(data['registration']['name'], data['registration']['email'], randompass)
user.save()
user = authenticate(username=data['registration']['name'], password=randompass)
if user is not None:
# save in user profile his facebook id. In this case for RPXNow login widget
fbid = 'http://www.facebook.com/profile.php?id=%s' % data['user_id']
r = RPXAssociation(user=user, identifier=fbid)
r.save()
login(request, user)
return HttpResponseRedirect('/')
return render_to_response('userpanel/fb_registration.html', {}, context_instance=RequestContext(request, userpanelContext(request)))
If you want user to be able to login later on your site with login/password without the use of Facebook then you have to add a custom field for the password, or generate a random one and mail it to the user (less user friendly).
Comment article