Skip to content

Instantly share code, notes, and snippets.

@hramos
Last active June 16, 2021 09:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hramos/63dd9ea0c05086a57a5c to your computer and use it in GitHub Desktop.
Save hramos/63dd9ea0c05086a57a5c to your computer and use it in GitHub Desktop.
A middleware module for logging in a Parse.User using Facebook in express.

Parse Cloud Module for managing Facebook Login server side using Express.

To log in, visit https://YOUR_SUBDOMAIN.parseapp.com/login.

Installation

Move main.js and app.js into your cloud/ folder in Cloud Code if it's a new project. If you're already using Express, update your app.js accordingly.

If you already have a Parse Hosting app set up, but you're not using Express yet, add the following to your `main.js:

require('cloud/app.js');

Update app.js to use your actual Facebook App's client id and app secret.

This module will create a TokenRequest class in your Parse app. If you have client-side class creation turned off, you will need to manually create this class.

Note: This is a work in progress

This example code is provided for demonstration purposes and no support is implied for the use of this user module at this time.

var express = require('express');
var parseExpressHttpsRedirect = require('parse-express-https-redirect');
var parseExpressCookieSession = require('parse-express-cookie-session');
var parseFacebookUserSession = require('cloud/parse-facebook-user-session');
var app = express();
app.set('views', 'cloud/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(parseExpressHttpsRedirect());
app.use(express.cookieParser('foobarbazqux'));
app.use(parseExpressCookieSession({ fetchUser: true }));
app.use(parseFacebookUserSession({
clientId: 'YOUR_FACEBOOK_APP_CLIENT_ID',
appSecret: 'YOUR_FACEBOOK_APP_SECRET',
redirectUri: '/login',
}));
app.get('/', function(req, res) {
var user = Parse.User.current();
res.render('hello', {
message: 'Congrats, you are logged in, ' + user.get('name') + '!'
});
});
app.get('/logout', function(req, res) {
Parse.User.logOut();
res.render('hello', { message: 'You are now logged out!' });
});
app.listen();
require('cloud/app.js');
var moment = require('moment');
var querystring = require('querystring');
/**
* A middleware module for logging in a Parse.User using Facebook in express.
*
* Params includes the following:
* clientId (required): The client id of the Facebook App.
* appSecret (required): The app secret of the Facebook App.
* redirectUri (optional): The path on this server to use for handling the
* redirect callback from Facebook. If this is omitted, it defaults to
* /login.
*/
var parseFacebookUserSession = function(params) {
if (!params.clientId || !params.appSecret) {
throw "You must specify a Facebook clientId and appSecret.";
}
var relativeRedirectUri = params.redirectUri || "/login";
/**
* Returns the absolute url of the redirect path for this request.
*/
var getAbsoluteRedirectUri = function(req) {
return 'https://' + req.host + relativeRedirectUri;
};
/**
* Starts the Facebook login OAuth process.
*/
var beginLogin = function(req, res) {
console.log("Starting Facebook login...");
Parse.Promise.as().then(function() {
// Make a request object. Its objectId will be our XSRF token.
console.log("Creating TokenRequest...");
var url = 'https://' + req.host + req.path;
var request = new Parse.Object("TokenRequest");
return request.save({
url: url,
ACL: new Parse.ACL()
});
}).then(function(request) {
console.log("Redirecting for Facebook OAuth.");
// Put the XSRF token into a cookie so that we can match it later.
res.cookie("requestId", request.id);
// Redirect the user to start the Facebook OAuth flow.
var url = 'https://www.facebook.com/dialog/oauth?';
url = url + querystring.stringify({
client_id: params.clientId,
redirect_uri: getAbsoluteRedirectUri(req),
state: request.id
});
res.redirect(302, url);
});
};
/**
* Handles the last stage of the Facebook login OAuth redirect.
*/
var endLogin = function(req, res) {
console.log("Handling request callback for Facebook login...");
if (req.query.state !== req.cookies.requestId) {
console.log("Request failed XSRF validation.");
res.send(500, "Bad Request");
return;
}
var url = 'https://graph.facebook.com/oauth/access_token?';
url = url + querystring.stringify({
client_id: params.clientId,
redirect_uri: getAbsoluteRedirectUri(req),
client_secret: params.appSecret,
code: req.query.code
});
var accessToken = null;
var expires = null;
var facebookData = null;
Parse.Promise.as().then(function() {
console.log("Fetching access token...");
return Parse.Cloud.httpRequest({ url: url });
}).then(function(response) {
console.log("Fetching user data from Facebook...");
var data = querystring.parse(response.text);
accessToken = data.access_token;
expires = data.expires;
var url = 'https://graph.facebook.com/me?';
url = url + querystring.stringify({
access_token: accessToken
});
return Parse.Cloud.httpRequest({ url: url });
}).then(function(response) {
console.log("Logging into Parse with Facebook token...");
facebookData = response.data;
var expiration = moment().add('seconds', expires).format(
"YYYY-MM-DDTHH:mm:ss.SSS\\Z");
return Parse.FacebookUtils.logIn({
id: response.data.id,
access_token: accessToken,
expiration_date: expiration
});
}).then(function(response) {
console.log("Becoming Parse user...");
return Parse.User.become(response.sessionToken);
}).then(function(user) {
console.log("Saving Facebook data for user...");
user.set("name", facebookData.name);
return user.save();
}).then(function(user) {
console.log("Fetching TokenRequest for " + req.query.state + "...");
var request = new Parse.Object("TokenRequest");
request.id = req.query.state;
return request.fetch({ useMasterKey: true });
}).then(function(request) {
console.log("Deleting used TokenRequest...");
// Let's delete this request so that no one can reuse the token.
var url = request.get("url");
return request.destroy({ useMasterKey: true }).then(function() {
return url;
});
}).then(function(url) {
console.log("Success!");
res.redirect(302, url);
}, function(error) {
console.log("Failed! " + JSON.stringify(error));
res.send(500, error);
});
};
/**
* The actual middleware method.
*/
return function(req, res, next) {
// If the user is already logged in, there's nothing to do.
if (Parse.User.current()) {
return next();
}
// If this is the Facebook login redirect URL, then handle the code.
var absoluteRedirectUri = 'https://' + req.host + relativeRedirectUri;
if (req.path === relativeRedirectUri) {
endLogin(req, res);
} else {
beginLogin(req, res);
}
};
};
module.exports = parseFacebookUserSession;
doctype 5
html
head
title Sample App
body
h1 Hello World
p= message
@grahamoregan
Copy link

Thanks for this, I couldn't figure out why my client side was working but the server wasn't, shame this isn't part of the SDK :/

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