Facebook Connect - Django integration on filmaster.com
Facebook allows developers to integrate Facebook accounts with their sites using Facebook Connect. User see a Facebook button on a site, he click/logs in on that site and the button sets Facebook cookies on it. I've made such integration for Filmaster.com -- film buffs community. In this solution users are registered/logged in automatically. There are also other solutions: fbconnect or a middleware.
The base of this integration is a middleware that checks for FB cookies (and if they are good), and then gets the user ID and try to login existing user if it has assigned Django account, or makes a new one. The FB Connect will set cookie name as you Facebook Connect API key (and few KEY_* cookies, like KEY_user with the user id).
class fbMiddleware(object):
"""
Handle Facebook association, autologin
"""
def process_request(self, request):
request.facebookconn = False
request.facebookconn_new = False
f_name = False
l_name = False
# Check if we have the FBConnect cookie
if settings.FACEBOOK_CONNECT_KEY in request.COOKIES:
signature_hash = self.get_facebook_signature(request.COOKIES, True)
# check if cookie is valud
if signature_hash == request.COOKIES[settings.FACEBOOK_CONNECT_KEY]:
# check if it didn't expired
if datetime.fromtimestamp(float(request.COOKIES[settings.FACEBOOK_CONNECT_KEY+'_expires'])) > datetime.now():
# get the FB user ID from cookie
uid = '%s_user' % settings.FACEBOOK_CONNECT_KEY
cookie_uid = request.COOKIES[uid]
request.facebookconn = cookie_uid
# check if the FB user id is associated to a Django User account
try:
f = FBAssociation.objects.get(fb_uid=cookie_uid)
except Exception, e:
logging.debug("new Facebook association")
# no account, so we make a new one
password = ''.join([choice('1234567890qwertyuiopasdfghjklzxcvbnm') for i in range(10)])
if 'fb_name' in request.POST:
username = slughifi(request.POST['fb_name'])
else:
logging.error("NO Facebook username")
return None
if 'fb_mail' in request.POST:
email = request.POST['fb_mail']
if len(email) >= 255:
# too long
email = str(cookie_uid)
else:
logging.error("No Facebook proxy mail")
return None
username = username.replace('-', '')
# check if username taken
try:
u = User.objects.get(username=username)
except:
pass
else:
username = '%s%s' % (username, str(cookie_uid))
# this shouldn't exist
try:
u = User.objects.get(username=username)
except:
pass
else:
logging.error("Existing Facebook usernames %s" % username)
return None
# check if mail is taken
try:
u = User.objects.get(email=email)
except:
pass
else:
email = str(cookie_uid)
try:
u = User.objects.get(email=email)
except:
pass
else:
logging.error("Existing proxy email %s" % email)
return None
#make the user
user = None
try:
user = User.objects.create_user(username, email, password)
user.save()
user = authenticate(username=username, password=password)
except Exception, e:
try:
user.delete()
except:
pass
logging.error("Facebook User Creation Exception")
logging.error(e)
return None
else:
# save the Facebook association
o = FBAssociation(user=user, fb_uid=request.facebookconn, is_new=True, is_from_facebook=True)
o.save()
if user is not None:
# login user and make the association
try:
login(request, user)
except Exception, e:
logging.error("Facebook User Login Exception")
logging.error(e)
return None
if 'fb_pic' in request.POST:
try:
path = settings.MEDIA_ROOT + date.today().strftime("avatars/%Y/%b/%d")
if not os.path.isdir(path):
os.makedirs(path)
image = '%s/%s.jpg' % (path, str(user.username))
img = urllib2.urlopen(request.POST['fb_pic']).read()
tmp = open('%s/%s.jpg' % (path, str(user.username)), 'wb')
tmp.write(img)
tmp.close()
i = Image.open(image)
i.thumbnail((480, 480), Image.ANTIALIAS)
i.convert("RGB").save(image, "JPEG")
image = '%s/%s.jpg' % (date.today().strftime("avatars/%Y/%b/%d"), str(user.username))
avatar = Avatar(user=user, image=image, valid=True)
avatar.save()
except Exception, e:
logging.error("Could not save avatar from Facebook")
logging.error(e)
return None
else:
# FB ID is assigned to a Django User account. Login user
if not request.user.is_authenticated():
user = authenticate(user_id = f.user.id, fb_uid=cookie_uid)
if user is not None:
login(request, user)
else:
if request.user.is_authenticated():
# not FB cookie but user from FB logged in? logout
try:
f = FBAssociation.objects.get(user=request.user)
except:
pass
else:
if f.is_from_facebook:
logout(request)
def get_facebook_signature(self, values_dict, is_cookie_check=False):
"""
Generates signatures for FB requests/cookies
"""
signature_keys = []
for key in sorted(values_dict.keys()):
if (is_cookie_check and key.startswith(settings.FACEBOOK_CONNECT_KEY + '_')):
signature_keys.append(key)
elif (is_cookie_check is False):
signature_keys.append(key)
if (is_cookie_check):
signature_string = ''.join(['%s=%s' % (x.replace(settings.FACEBOOK_CONNECT_KEY + '_',''), values_dict[x]) for x in signature_keys])
else:
signature_string = ''.join(['%s=%s' % (x, values_dict[x]) for x in signature_keys])
signature_string = signature_string + settings.FACEBOOK_CONNECT_SECRET
return md5.new(signature_string).hexdigest()
If the middleware finds assigned Django user account for Facebook user id - it will login the user. If not it will create a new one, set the relation, and login new user. To create a new user it needs some data about the user. They can be obtained only with client-side JavaScript, that is called after the Facebook button login:
<fb:login-button autologoutlink="true"></fb:login-button>
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script>
<script type="text/javascript">
FB_RequireFeatures(["XFBML"], function()
{
FB.Facebook.init("{{ connect_key }}", "/fb/xd_receiver.htm");
FB.Facebook.get_sessionState().waitUntilReady(function()
{
var uid = FB.Facebook.apiClient.get_session().uid;
if (uid)
{
{% if not user.is_authenticated %}
var viewer = FB.Facebook.apiClient.fql_query('SELECT name, pic_small, proxied_email FROM user WHERE uid='+uid,
function(results) {
$.post("/", {fb_name: results[0].name, fb_pic: results[0].pic_small , fb_mail: results[0].proxied_email} , function(data){
{% ifequal request.path request.login_url %}
location.assign("/dashboard/");
{% else %}
location.assign("http://{{ request.META.HTTP_HOST }}{{request.path }}");
{% endifequal %}
});
}
);
{% endif %}
}
});
});
</script>
Comment article