diff --git a/lib/passport-facebook-token/strategy.js b/lib/passport-facebook-token/strategy.js index d47b991..a604edd 100644 --- a/lib/passport-facebook-token/strategy.js +++ b/lib/passport-facebook-token/strategy.js @@ -22,6 +22,9 @@ var util = require('util') * Options: * - `clientID` your Facebook application's App ID * - `clientSecret` your Facebook application's App Secret + * - `getLongLivedToken` default false, if set to true, will try to obtain a + * long lived token from Facebook which will be accessible + * via req.longLivedToken * * Examples: * @@ -44,6 +47,7 @@ function FacebookTokenStrategy(options, verify) { options = options || {} options.authorizationURL = options.authorizationURL || 'https://www.facebook.com/dialog/oauth'; options.tokenURL = options.tokenURL || 'https://graph.facebook.com/oauth/access_token'; + this.tokenURL = options.tokenURL; options.scopeSeparator = options.scopeSeparator || ','; this._passReqToCallback = options.passReqToCallback; @@ -51,9 +55,11 @@ function FacebookTokenStrategy(options, verify) { OAuth2Strategy.call(this, options, verify); this._profileURL = options.profileURL || 'https://graph.facebook.com/me'; this.name = 'facebook-token'; + this._clientID = options.clientID; this._clientSecret = options.clientSecret; this._enableProof = options.enableProof; this._profileFields = options.profileFields || null; + this._getLongLivedToken = options.getLongLivedToken || false; } /** @@ -94,18 +100,39 @@ FacebookTokenStrategy.prototype.authenticate = function(req, options) { self._loadUserProfile(accessToken, function(err, profile) { if (err) { return self.fail(err); }; - - function verified(err, user, info) { - if (err) { return self.error(err); } - if (!user) { return self.fail(info); } - self.success(user, info); - } - - if (self._passReqToCallback) { - self._verify(req, accessToken, refreshToken, profile, verified); - } else { - self._verify(accessToken, refreshToken, profile, verified); - } + + function passCallback(req,accessToken,refreshToken,profile) { + function verified(err, user, info) { + if (err) { + return self.error(err); + } + if (!user) { + return self.fail(info); + } + self.success(user, info); + } + + if (self._passReqToCallback) { + self._verify(req, accessToken, refreshToken, profile, verified); + } else { + self._verify(accessToken, refreshToken, profile, verified); + } + } + if(self._getLongLivedToken) { + self._getLLT(accessToken, function (err, longLivedToken, expires) { + if(err){ + self.error(err); + return; + } + req.longLivedToken = longLivedToken; + if(expires !== null){ + req.longLivedTokenExpires = expires; + } + passCallback(req, accessToken, refreshToken, profile); + }); + } else { + passCallback(req, accessToken, refreshToken, profile); + } }); } @@ -266,6 +293,55 @@ FacebookTokenStrategy.prototype._convertProfileFields = function(profileFields) return fields.join(','); }; +/** + * Attempts to get a Long-Lived Token from Facebook. + * Requires a valid clientID (AppID), clientSecret (AppSecret) and accessToken + * + * @param {String} accessToken + * @param {Function} done + * @api private + */ +FacebookTokenStrategy.prototype._getLLT = function(accessToken,done){ + var url = this.tokenURL + "?" + + "grant_type=fb_exchange_token" + "&" + + "client_id=" + this._clientID + "&" + + "client_secret=" + this._clientSecret + "&" + + "fb_exchange_token=" + accessToken; + url = uri.parse(url); + if (this._enableProof) { + // Secure API call by adding proof of the app secret. This is required when + // the "Require AppSecret Proof for Server API calls" setting has been + // enabled. The proof is a SHA256 hash of the access token, using the app + // secret as the key. + // + // For further details, refer to: + // https://developers.facebook.com/docs/reference/api/securing-graph-api/ + var proof = crypto.createHmac('sha256', this._clientSecret).update(accessToken).digest('hex'); + url.search = (url.search ? url.search + '&' : '') + 'appsecret_proof=' + encodeURIComponent(proof); + } + url = uri.format(url); + this._oauth2.getProtectedResource(url, accessToken, function (err, body, res) { + if (err) { + return done(new InternalOAuthError('failed to get long-lived token', err)); } + try { + var var_chunks = body.split("&"); + var final_vars = {}; + for(var i=0;i