Skip to content

Commit

Permalink
Initial commit. FB Connect working
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominiek ter Heide committed Mar 4, 2010
0 parents commit 2c9eb0b
Show file tree
Hide file tree
Showing 9 changed files with 370 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
@@ -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
8 changes: 8 additions & 0 deletions README.textile
@@ -0,0 +1,8 @@
h1. Facebook Connect for NodeJS


h2. Example: FB Connect


h2. Example: A Facebook Application

63 changes: 63 additions & 0 deletions examples/fb_connect/app.js
@@ -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()
56 changes: 56 additions & 0 deletions examples/fb_connect/public/index.html
@@ -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>
89 changes: 89 additions & 0 deletions examples/fb_connect/public/javascripts/jquery.facebook.js
@@ -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);
10 changes: 10 additions & 0 deletions examples/fb_connect/public/xd_receiver.htm
@@ -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>
136 changes: 136 additions & 0 deletions lib/facebook.js
@@ -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
},
})
}
}
})
1 change: 1 addition & 0 deletions lib/support/express
Submodule express added at 7bd834
1 change: 1 addition & 0 deletions lib/support/hashlib
Submodule hashlib added at a1a03f

0 comments on commit 2c9eb0b

Please sign in to comment.