Permalink
Browse files

Initial commit. FB Connect working

  • Loading branch information...
Dominiek ter Heide
Dominiek ter Heide committed Mar 4, 2010
0 parents commit 2c9eb0ba2c3b760bf40b4dc46d98928b13c91c92
@@ -0,0 +1,6 @@
+[submodule "lib/support/express"]
+ path = lib/support/express
+ url = git://github.com/visionmedia/express.git
+[submodule "lib/support/hashlib"]
+ path = lib/support/hashlib
+ url = git://github.com/brainfucker/hashlib.git
@@ -0,0 +1,8 @@
+h1. Facebook Connect for NodeJS
+
+
+h2. Example: FB Connect
+
+
+h2. Example: A Facebook Application
+
@@ -0,0 +1,63 @@
+
+require.paths.unshift(__dirname + '/../../lib')
+require.paths.unshift(__dirname + '/../../lib/support/express/lib')
+require.paths.unshift(__dirname + '/../../lib/support/hashlib/build/default')
+
+require('express')
+require('express/plugins')
+require('sys')
+
+configure(function(){
+ use(MethodOverride)
+ use(ContentLength)
+ use(Cookie)
+ use(Session)
+ use(CommonLogger)
+ use(require('facebook').Facebook, {
+ apiKey: 'e1249f7d4bc25b8f90e5c9c7523e3ee1',
+ apiSecret: '4ae45734dd66fa85c7b189fc2d7d5b4c'
+ })
+ set('root', __dirname)
+})
+
+// Called to get information about the current authenticated user
+get('/fbSession', function(){
+ var fbSession = this.fbSession()
+
+ if(fbSession) {
+ // Here would be a nice place to lookup userId in the database
+ // and supply some additional information for the client to use
+ }
+
+ // The client will only assume authentication was OK if userId exists
+ this.contentType('json')
+ this.halt(200, JSON.stringify(fbSession || {}))
+})
+
+// Called after a successful FB Connect
+post('/fbSession', function() {
+ var fbSession = this.fbSession() // Will return null if verification was unsuccesful
+
+ if(fbSession) {
+ // Now that we have a Facebook Session, we might want to store this new user in the db
+ // Also, in this.params there is additional information about the user (name, pic, first_name, etc)
+ // Note of warning: unlike fbSession, this additional information has not been verified
+ fbSession.first_name = this.params.post['first_name']
+ }
+
+ this.contentType('json')
+ this.halt(200, JSON.stringify(fbSession || {}))
+})
+
+// Called on Facebook logout
+post('/fbLogout', function() {
+ this.fbLogout();
+ this.halt(200, JSON.stringify({}))
+})
+
+// Static files in ./public
+get('/', function(file){ this.sendfile(__dirname + '/public/index.html') })
+get('/xd_receiver.htm', function(file){ this.sendfile(__dirname + '/public/xd_receiver.htm') })
+get('/javascripts/jquery.facebook.js', function(file){ this.sendfile(__dirname + '/public/javascripts/jquery.facebook.js') })
+
+run()
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
+ <script type="text/javascript" src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"></script>
+ <script type="text/javascript" src="/javascripts/jquery.facebook.js"></script>
+ <script type="text/javascript">
+ $(document).ready(function () {
+ $.fbInit('e1249f7d4bc25b8f90e5c9c7523e3ee1');
+
+ // FB Connect action
+ $('#fb-connect').bind('click', function () {
+ $.fbConnect({'include': ['first_name', 'last_name', 'name', 'pic']}, function (fbSession) {
+ $('.not_authenticated').hide();
+ $('.authenticated').show();
+ $('#fb-first_name').html(fbSession.first_name);
+ });
+ return false;
+ });
+
+ // FB Logout action
+ $('#fb-logout').bind('click', function () {
+ $.fbLogout(function () {
+ $('.authenticated').hide();
+ $('.not_authenticated').show();
+ });
+ return false;
+ });
+
+ // Check whether we're logged in and arrange page accordingly
+ $.fbIsAuthenticated(function (fbSession) {
+ // Authenticated!
+ $('.authenticated').show();
+ $('#fb-first_name').html(fbSession.first_name);
+ }, function () {
+ // Not authenticated
+ $('.not_authenticated').show();
+ });
+
+ });
+ </script>
+ </head>
+
+ <body>
+ <div id="fb-root"></div>
+ <div id="my-account">
+ <div class="authenticated" style="display: none">
+ Hi there <span id="fb-first_name"></span>!
+ <a href="#" id="fb-logout">Logout</a>
+ </div>
+ <div class="not_authenticated" style="display: none">
+ <a href="#" id="fb-connect">Connect with Facebook</a>
+ </div>
+ </div>
+ </body>
+</html>
@@ -0,0 +1,89 @@
+
+(function($){
+
+ // Note: these variables are cache, validation is still server-side
+ var authenticated = null;
+ var authenticatedFbSession = null;
+ var apiKey = null;
+ var fbOptions = null;
+
+ $.fbInit = function (api_key, options) {
+ apiKey = api_key;
+ fbOptions = options || {};
+ FB_RequireFeatures(["Api"], function() {
+ FB.Facebook.init(api_key, fbOptions['xd_receiver'] || '/xd_receiver.htm');
+ });
+ };
+
+ $.fbConnect = function(options, callback) {
+ options = options || {};
+ if(!options['include']) {
+ options['include'] = ['name', 'pic'];
+ }
+ FB.Connect.requireSession(function () {
+ FB.Facebook.apiClient.fql_query("SELECT " + options['include'].join(', ') + " FROM user WHERE uid="+$.fbCookie('user'), function(rows) {
+ $.post("/fbSession", rows[0], function (fbSession) {
+ if (fbSession['userId']) {
+ authenticated = true;
+ authenticatedFbSession = fbSession;
+ if(callback) {
+ callback(fbSession);
+ }
+ } else {
+ authenticated = false;
+ if(callback) {
+ callback();
+ }
+ }
+ }, 'json');
+ });
+ });
+ };
+
+ $.fbLogout = function (callback) {
+ FB.Connect.logout(function () {
+ $.post("/fbLogout", {}, function () {
+ callback();
+ }, 'json');
+ });
+ };
+
+ $.fbIsAuthenticated = function (authenticated_callback, not_authenticated_callback) {
+ if(authenticated === null) {
+ $.get(fbOptions['sessionSyncAction'] || '/fbSession', {}, function (fbSession) {
+ if (fbSession['userId']) {
+ authenticated = true;
+ authenticatedFbSession = fbSession;
+ authenticated_callback(authenticatedFbSession);
+ } else {
+ authenticated = false;
+ not_authenticated_callback();
+ }
+ }, 'json');
+ return;
+ }
+ if(authenticated === true) {
+ authenticated_callback(authenticatedFbSession);
+ } else {
+ not_authenticated_callback();
+ }
+ };
+
+ $.fbCookie = function (name) {
+ name = apiKey + '_' +name;
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = jQuery.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ };
+
+})(jQuery);
@@ -0,0 +1,10 @@
+<!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>Cross-Domain Receiver Page</title>
+</head>
+<body>
+ <script src="http://static.ak.facebook.com/js/api_lib/v0.4/XdCommReceiver.js?v2" type="text/javascript"></script>
+</body>
+</html>
@@ -0,0 +1,136 @@
+
+// Express - Facebook - By Dominiek ter Heide (MIT Licensed)
+
+sys = require('sys')
+hashlib = require('hashlib')
+events = require('events')
+http = require('express/http')
+
+exports.FBSession = Class({
+ init: function (userId) {
+ this.userId = userId;
+ }
+});
+
+// --- Facebook
+
+exports.Facebook = Plugin.extend({
+ extend: {
+
+ /**
+ * Initialize extensions.
+ */
+
+ init: function(options) {
+ var apiKey = options['apiKey']
+ var apiSecret = options['apiSecret']
+
+ // --- Internal methods
+
+ var getFingerprintForCookie = function (cookies) {
+ var fields = ['expires', 'session_key', 'ss', 'user'];
+ var fingerprint = '';
+ fields.sort();
+ for(var i in fields) {
+ fingerprint += fields[i]+'='+cookies[apiKey + '_' + fields[i]];
+ }
+ return fingerprint;
+ }
+
+ var getFingerprintForParams = function (params) {
+ var fields = [];
+ for(var i in params) {
+ if(i.match(/^fb_sig_/)) {
+ fields.push(i);
+ }
+ }
+ fields.sort();
+ var fingerprint = '';
+ fields.sort();
+ for(var i in fields) {
+ fingerprint += fields[i].replace(/^fb_sig_/, '')+'='+params[fields[i]];
+ }
+ return fingerprint;
+ }
+
+ Request.include({
+
+ /**
+ * Find or create Facebook session based on stored session, GET params or cookie
+ *
+ * Options:
+ *
+ * -
+ * -
+ *
+ * @param {hash} options
+ * @return {string}
+ * @api public
+ */
+
+ fbSession: function(options) {
+ var session = this.session.fbSession;
+ if(session)
+ sys.puts("Cool, found existing session!")
+ if(session)
+ return session;
+ if(this.fbAuthenticate()) {
+ var fbUserId = this.param('fb_sig_user') ? this.param('fb_sig_user') : this.cookie(apiKey + '_user')
+ this.session.fbSession = new exports.FBSession(fbUserId);
+ return this.session.fbSession;
+ }
+ return null;
+ },
+
+ /**
+ * Try authenticating by verifying Facebook data in GET params and cookie
+ *
+ * Options:
+ *
+ * -
+ * -
+ *
+ * @param {hash} options
+ * @return {string}
+ * @api public
+ */
+
+ fbAuthenticate: function(options) {
+ var cookies = this.cookies;
+ var params = this.params;
+
+ // Get a fingerprint and signature
+ var fingerprint = null;
+ var signature = null;
+ if(cookies[apiKey]) {
+ fingerprint = getFingerprintForCookie(cookies)
+ signature = cookies[apiKey]
+ }
+ if(params['fb_sig']) {
+ fingerprint = getFingerprintForParams(params)
+ signature = params['fb_sig']
+ }
+ if(!fingerprint)
+ return null;
+
+ // Verify signature using apiSecret
+ var expected_signature = hashlib.md5(fingerprint+apiSecret);
+ var valid = (expected_signature === signature)
+ if(!valid)
+ sys.puts("Warning, invalid signature: "+fingerprint)
+ return valid
+ },
+
+ /**
+ * Logout
+ * @return null
+ * @api public
+ */
+ fbLogout: function() {
+ this.session.fbSession = null
+ return null
+ },
+ })
+ }
+ }
+})
Submodule express added at 7bd834
Submodule hashlib added at a1a03f

0 comments on commit 2c9eb0b

Please sign in to comment.