Skip to content

Instantly share code, notes, and snippets.

@chanon
Created November 3, 2010 19:50
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save chanon/661597 to your computer and use it in GitHub Desktop.
Save chanon/661597 to your computer and use it in GitHub Desktop.
Facebook iframe Canvas App Authentication in node.js
var querystring = require("querystring");
var base64ToString = function(str) {
return (new Buffer(str || "", "base64")).toString("ascii");
};
var base64UrlToString = function(str) {
return base64ToString( base64UrlToBase64(str) );
};
var base64UrlToBase64 = function(str) {
var paddingNeeded = (4- (str.length%4));
for (var i = 0; i < paddingNeeded; i++) {
str = str + '=';
}
return str.replace(/\-/g, '+').replace(/_/g, '/')
};
var getAuthorizeUrl = function() {
var oauth_url = 'https://www.facebook.com/dialog/oauth/?'
var options = {
client_id: YOUR_APP_ID,
redirect_uri: 'https://apps.facebook.com/YOUR_APP/',
scope: COMMA_SEPARATED_LIST_OF_PERMISSION_NAMES
};
// for scope see https://developers.facebook.com/docs/authentication/permissions/
// eg. it could be "publish_stream,email"
return oauth_url + querystring.stringify(options);
};
app.get('/fb', function(req, res) {
var signed_request = req.param('signed_request');
var parts = signed_request.split('.');
var sig = base64UrlToBase64(parts[0]);
var payload = parts[1];
var data = JSON.parse(base64UrlToString(payload));
if (!data.user_id) {
// send over to authorize url
res.send("<script>window.top.location='" + getAuthorizeUrl() + "'</script>");
}
else {
// lets verify
if (data.algorithm.toUpperCase() !== 'HMAC-SHA256') {
res.send('Unknown algorithm. Expected HMAC-SHA256');
return;
}
var secret = YOUR_APP_SECRET;
var hmac = require('crypto').createHmac('sha256', secret);
hmac.update(payload);
var expected_sig = hmac.digest('base64');
if (sig != expected_sig){
console.log('expected [' + expected_sig + '] got [' + sig + ']');
res.send('Hello, this is my app! you are CHEATING! .. expected [' + expected_sig + '] got [' + sig + ']');
}
else {
res.send('Hello, this is my app! you passed verification and are ' + data.user_id);
}
}
});
@chanon
Copy link
Author

chanon commented Nov 3, 2010

Anyone trying to do a Facebook iframe canvas app using the new OAuth 2.0 authentication for canvas apps (http://developers.facebook.com/docs/authentication/canvas) will need to decode the signed_request parameter. Well, here is a code snippet that will let you do this without any additional dependencies. It just uses the node crypto module and Buffer object.

@chanon
Copy link
Author

chanon commented Nov 3, 2010

BTW, if you use this don't forget to add try/catch in case input is mangled

@gcfuser223
Copy link

Hi chanon,

I'm using your snippet and got it to work for me, but how would I go about requesting facebook permissions? My guess is I would have to do that on the client using the fb sdk ...

cheers

@chanon
Copy link
Author

chanon commented Apr 17, 2012

Good question.

That should actually go in the part that I wrote "// send over to authorize url".

I just updated the code so there is a full example of how to do that (line 39).

The facebook docs here: https://developers.facebook.com/docs/authentication/canvas/ explain it under "2a. Redirect to OAuth Dialog upon page load". I'm just using 'getAuthorizeUrl()' the prepare the url server-side instead.

Not 100% sure if this addition works, might be typos.

@chanon
Copy link
Author

chanon commented Apr 17, 2012

So to explain, what it's doing: First it checks if there is a user_id in the signed_request. If there is no user_id, that means the user hasn't authorized the app. So we redirect them to the facebook authorization page so they can authorize our app. At the same time we send some permissions to that page through the "scope" option to request from the user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment