Skip to content

Instantly share code, notes, and snippets.

@hramos
Last active June 16, 2021 09:34
Show Gist options
  • Select an option

  • Save hramos/63dd9ea0c05086a57a5c to your computer and use it in GitHub Desktop.

Select an option

Save hramos/63dd9ea0c05086a57a5c to your computer and use it in GitHub Desktop.

Revisions

  1. hramos revised this gist Oct 21, 2014. 1 changed file with 22 additions and 0 deletions.
    22 changes: 22 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    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](https://developers.facebook.com/apps)'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.
  2. hramos revised this gist Oct 21, 2014. 3 changed files with 42 additions and 0 deletions.
    34 changes: 34 additions & 0 deletions app.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    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();
    1 change: 1 addition & 0 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    require('cloud/app.js');
    7 changes: 7 additions & 0 deletions views_slash_hello.jade
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    doctype 5
    html
    head
    title Sample App
    body
    h1 Hello World
    p= message
  3. hramos created this gist Oct 21, 2014.
    167 changes: 167 additions & 0 deletions parse-facebook-user-session.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,167 @@
    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;