Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Express 10 #5

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules
@@ -1,6 +1,3 @@
[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
105 changes: 46 additions & 59 deletions README.textile
Expand Up @@ -22,13 +22,6 @@ git submodule init
git submodule update
</code></pre>

Also, we need to install "hashlib":http://github.com/brainfucker/hashlib, a NodeJS library provided by mr Brainfucker:

<pre><code>git submodule add git://github.com/brainfucker/hashlib.git lib/support/hashlib
cd lib/support/hashlib
make
</code></pre>

h2. Setting up your Project

1. Create the necessary folders:
Expand Down Expand Up @@ -62,65 +55,62 @@ h3. /app.js

<pre><code>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')

configure(function(){
use(MethodOverride)
use(ContentLength)
use(Cookie)
use(Session)
use(Logger)
use(require('facebook').Facebook, {

var express = require('express')
var app = express.createServer();

app.configure(function(){
app.use(express.methodOverride())
app.use(ContentLength)
app.use(Cookie)
app.use(Session)
app.use(Logger)
app.use(require('facebook').Facebook(dx{
apiKey: 'e1249f7d4bc25b8f90e5c9c7523e3ee1',
apiSecret: '4ae45734dd66fa85c7b189fc2d7d5b4c'
})
set('root', __dirname)
app.set('root', __dirname)
})

// Called to get information about the current authenticated user
get('/fbSession', function(){
var fbSession = this.fbSession()
app.get('/fbSession', function(req, res){
var fbSession = req.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 || {}))
res.send(JSON.stringify(fbSession || {}), {'Content-Type: 'application/json'});
})

// Called after a successful FB Connect
post('/fbSession', function() {
var fbSession = this.fbSession() // Will return null if verification was unsuccesful
app.post('/fbSession', function(req, res) {
var fbSession = req.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']
fbSession.first_name = req.params['first_name']
}

this.contentType('json')
this.halt(200, JSON.stringify(fbSession || {}))
res.send(JSON.stringify(fbSession || {}), {'Content-Type: 'application/json'});
})

// Called on Facebook logout
post('/fbLogout', function() {
this.fbLogout();
this.halt(200, JSON.stringify({}))
app.post('/fbLogout', function(req, res) {
req.fbLogout();
res.send(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') })
app.get('/', function(req, res){ res.sendfile(__dirname + '/public/index.html') })
app.get('/xd_receiver.htm', function(req, res){ res.sendfile(__dirname + '/public/xd_receiver.htm') })
app.get('/javascripts/jquery.facebook.js', function(res, res){ res.sendfile(__dirname + '/public/javascripts/jquery.facebook.js') })

run()
app.listen()

</code></pre>

Expand Down Expand Up @@ -193,49 +183,46 @@ h3. /app.js

<pre><code>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')

configure(function(){
use(MethodOverride)
use(ContentLength)
use(Cookie)
use(Session)
use(Logger)
use(require('facebook').Facebook, {

var express = require('express')
var app = express,createServer();

app.configure(function(){
app.use(express.methodOverride())
app.use(express.cookieDecoder())
app.use(express.session())
app.use(express.logger())
app.use(require('facebook').Facebook({
apiKey: 'e1249f7d4bc25b8f90e5c9c7523e3ee1',
apiSecret: '4ae45734dd66fa85c7b189fc2d7d5b4c'
})
set('root', __dirname)
express.staticProvider(__dirname + '/public')
})

// This is the canvas URL set in the Facebook Application settings
get('/iframe', function (){
var fbSession = this.fbSession() // Will create a session based on verified data from the GET params
this.sendfile(__dirname + '/public/iframe.html')
app.get('/iframe', function(req, res) {
var fbSession = req.fbSession() // Will create a session based on verified data from the GET params
res.sendfile(__dirname + '/public/iframe.html')
})

// Called to get information about the current authenticated user
get('/fbSession', function(){
var fbSession = this.fbSession()
app.get('/fbSession', function(req, res){
var fbSession = req.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 || {}))
res.send(JSON.stringify(fbSession || {}), {'Content-Type: 'application/json'});
})

// Static files in ./public
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') })
app.get('/xd_receiver.htm', function(req, res){ res.sendfile(__dirname + '/public/xd_receiver.htm') })
app.get('/javascripts/jquery.facebook.js', function(req, res){ res.sendfile(__dirname + '/public/javascripts/jquery.facebook.js') })

run()
app.listen()

</code></pre>

Expand Down
146 changes: 69 additions & 77 deletions lib/facebook.js
@@ -1,23 +1,27 @@

// Express - Facebook - By Dominiek ter Heide (MIT Licensed)

sys = require('sys')
hashlib = require('hashlib')
var sys = require('sys')
var base64 = require('base64')
var crypto = require('crypto')
var querystring = require('querystring')

exports.FBSession = Class({
init: function (userId) {
function FBSession(userId) {
this.userId = userId;
}
});
};

exports.getFingerprintForCookie = function (apiKey, cookies) {
var fields = ['expires', 'session_key', 'ss', 'user'];
var values = querystring.parse(cookies[apiKey]);
var keys = [];
for (var i in values)
keys.push(i);
var fingerprint = '';
fields.sort();
for(var i in fields) {
fingerprint += fields[i]+'='+cookies[apiKey + '_' + fields[i]];
}
return fingerprint;
keys.sort();
keys.forEach(function(key) {
if (key != 'sig')
fingerprint += key +'='+ values[key];
});
return {fingerprint: fingerprint, sig: values['sig']};
}

exports.getFingerprintForParams = function (params) {
Expand All @@ -38,85 +42,73 @@ exports.getFingerprintForParams = function (params) {

// --- Facebook

exports.Facebook = Plugin.extend({
extend: {

/**
* Initialize extensions.
*/

init: function(options) {
var apiKey = options['apiKey']
var apiSecret = options['apiSecret']

// --- Internal methods

Request.include({
exports.Facebook = function(options) {
options = options || {};
var apiKey = options['apiKey']
var apiSecret = options['apiSecret']

return function(req, res, next) {
/**
* Find or create Facebook session based on stored session, GET params or cookie
* Try authenticating by verifying Facebook data in GET params and cookie
*
* @param {hash} options
* @return {FBSession}
* @api public
*/
req.fbAuthenticate = function() {
var cookies = req.cookies;
var params = req.query;

// Get a fingerprint and signature
var fingerprint = null;
var signature = null;

if(cookies && cookies['fbs_'+apiKey]) {
var r = exports.getFingerprintForCookie('fbs_'+ apiKey, cookies)
fingerprint = r.fingerprint
signature = r.sig
}
if(params && params['fb_sig']) {
fingerprint = exports.getFingerprintForParams(params)
signature = params['fb_sig']
}
if(!fingerprint)
return null;

// Verify signature using apiSecret
var hash = crypto.createHash('md5')
hash.update(fingerprint+apiSecret)
var expected_signature = hash.digest('hex')
var valid = (expected_signature === signature)
if(!valid)
sys.puts("Warning, invalid signature: "+fingerprint)
return valid
};

fbSession: function(options) {
var session = this.session.fbSession;
if(session && session.userId)
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
*
* @param {hash} options
* Find or create Facebook session based on stored session, GET params or cookie
* @return {FBSession}
* @api public
*/

fbAuthenticate: function(options) {
var cookies = this.cookies;
var params = this.params.get;

// Get a fingerprint and signature
var fingerprint = null;
var signature = null;
if(cookies && cookies[apiKey]) {
fingerprint = exports.getFingerprintForCookie(apiKey, cookies)
signature = cookies[apiKey]
}
if(params && params['fb_sig']) {
fingerprint = exports.getFingerprintForParams(params)
signature = params['fb_sig']
}
if(!fingerprint)
req.fbSession = function() {
var session = req.session.fbSession;
if(session && session.userId)
return session;
if(req.fbAuthenticate()) {
var fbUserId = req.query['fb_sig_user'] ? req.query['fb_sig_user'] : querystring.parse(req.cookies['fbs_'+apiKey])['uid']
req.session.fbSession = new FBSession(fbUserId);
return req.session.fbSession;
}
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
},
})
}
}
})
req.fbLogout = function() {
req.session.fbSession = null
return null
};
next();
};
};
1 change: 0 additions & 1 deletion lib/support/hashlib
Submodule hashlib deleted from a1a03f
10 changes: 10 additions & 0 deletions package.json
@@ -0,0 +1,10 @@
{
"name":"facebook",
"description": "Simple Facebook Integration for NodeJS (and Express)",
"version":"0.0.3",
"author": "Dominiek ter Heide",
"main": "./lib/facebook",
"dependencies": {
"base64":""
}
}