Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting facebook authentication running within the MeteorJS framework?

I'm starting out my little quest into the world of the Meteor framework and I thought it'd be fun to do something a little bit facebooky with it.

step one was to follow the create an app tutorial in meteor then add the FB code to the template as found here: https://developers.facebook.com/docs/opengraph/tutorial/#authenticate

Sadly it doesn't seem to work on the page at all. In fact, I've just realized that if I add something like alert('foo'); to my meteor page it doesn't execute. Interesting.

So Metor, despite being completely amazing doesn't work like I expect it to... (shock, horror!).

How do I execute client side JS in this framework? (specifcially with the hope of creating a facebook JS object on the page?)

Thanks!

UPDATE (Jan 2013): Meteor released 0.5.0 which has built in authentication and facebook login support.

Documentation is here: http://docs.meteor.com/#accountsui

Essentially you run some commands in the shell

     meteor add accounts-password
     meteor add accounts-ui
     meteor add accounts-facebook

then in your code you add the login button.

     {{loginButtons}}

then you're in.

like image 612
Alex C Avatar asked May 05 '12 17:05

Alex C


3 Answers

It's probably a very bad idea to do any sort of authentication to an external API, while passing around App IDs, secrets, etc, from the browser (client in the meteor stack).

I've implemented full facebook authentication on the server successfully.

Add the accounts-facebook smart package by running meteor add accounts-facebook from your application's working directory. This will allow you to be able to configure support for both the OAuth single user as well as the OAuth multi-user facebook authentication workflows. Please see Meteor Accounts System docs for more info.

After adding the accounts-facebook smart package, you may do something along these lines...

Under your application's working directory, in server/server.js (or similar file under the server directory), implement something like this:

Meteor.startup(function () {
  Accounts.loginServiceConfiguration.remove({
    service: "facebook"
  });

  Accounts.loginServiceConfiguration.insert({
    service: "facebook",
    appId: process.env.FACEBOOK_APP_ID,
    secret: process.env.FACEBOOK_APP_SECRET
  });
});

Take note of these lines:

appId: process.env.FACEBOOK_APP_ID,
secret: process.env.FACEBOOK_APP_SECRET

You will need to set the environment variables FACEBOOK_APP_ID and FACEBOOK_APP_SECRET properly for the above code to use the correct values.

In client/client.js (or similar file under the client directory), implement something like this:

Meteor.startup(function() {
  Meteor.loginWithFacebook({
    requestPermissions: ['publish_actions']
  }, function (err) {
    if (err) {
      Session.set('errorMessage', err.reason || 'Unknown error');
    }
  });
});

As per Meteor.loginWithExternalService([options], [callback]), the callback function for Meteor.loginWithFacebook allows you to easily distinguish between error states, and non-error states:

Optional callback. Called with no arguments on success, or with a single Error argument on failure.

like image 170
zealoushacker Avatar answered Nov 09 '22 21:11

zealoushacker


It appears that running client side code is done by placing it inside the "myapp.js" file

  Template.hello.greeting = function () {
     // ADD YOUR CODE HERE
     alert('foo');
     return "Welcome to matchmakeur.";
  };

So in order to connect your code to Facebook authentication you've got to do something like

  Template.fbconnect.connect = function () {
      window.fbAsyncInit = function() {
        FB.init({
          appId      : '[YOUR_APP_ID]', // App ID
          status     : true, // check login status
          cookie     : true, // enable cookies to allow the server to access the session
          xfbml      : true  // parse XFBML
        });
      };

      // Load the SDK Asynchronously
      (function(d){
        var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
        js = d.createElement('script'); js.id = id; js.async = true;
        js.src = "//connect.facebook.net/en_US/all.js";
        d.getElementsByTagName('head')[0].appendChild(js);
      }(document));
     return true;
  };

and have a template

   <template name="fbconnect">
        <div id="fb-root"></div>
        {{connect}}
        <fb:login-button show-faces="true" width="200" max-rows="1" scope="publish_actions">
        </fb:login-button>
   <template>
like image 40
Alex C Avatar answered Nov 09 '22 20:11

Alex C


It is not a bad idea to use Facebook JavaScript SDK if you want to build a full client-side app with Meteor...

Alex C answer works at first glance, but I had some problems with FB.logout() after "navigating" and going back to the "page" where <div id="fb-root"></div> was defined, because when fb-root is re-rendered, FB.logout() stops working.

So I think the best way to load Facebook JavaScript SDK is to use a Template created callback:

Template.fbLogin.created = function () {
  if (!Session.get("is Facebook JDK loaded?")) {
    Session.set("is Facebook JDK loaded?", true);

    // https://developers.facebook.com/docs/reference/javascript/
    window.fbAsyncInit = function() {
      // init the FB JS SDK
      FB.init({
        appId      : '[YOUR_APP_ID]', // App ID
        status     : true, // check login status
        cookie     : true, // enable cookies to allow the server to access the session
        xfbml      : true  // parse XFBML
      });

      // Additional initialization code such as adding Event Listeners goes here
    };

    // Load the SDK's source Asynchronously
    // Note that the debug version is being actively developed and might 
    // contain some type checks that are overly strict. 
    // Please report such bugs using the bugs tool.
    (function(d, debug){
       var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
       if (d.getElementById(id)) {return;}
       js = d.createElement('script'); js.id = id; js.async = true;
       js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
       ref.parentNode.insertBefore(js, ref);
     }(document, /*debug*/ false));
  }
};

Also, other thing that needs to be done for FB.logout() to work correctly is using a constant template helper around <div id="fb-root"></div>. So your code should be like this:

<body>
  {{#constant}}
    <div id="fb-root"></div>
  {{/constant}}
  <!-- rest of body... -->

I also found that it is necessary to put fb-root immediately after body.

There is a live app running code like this at http://evee.meteor.com
And you can find its source-code here: https://github.com/fjsj/evee/ (check fbLogin.js for created and app.html for constant and fb-root)

Bonus (how to serve a channel.html file with Meteor)

As of January 2013, Meteor does not support server-side routes to serve static HTML. So how to make a channel.html with Meteor? Putting it in /public won't work, since .html files are processed by Meteor as templates.

By using Meteor internals, it is possible! As suggested by this answer, we need to use a middleware-like approach (powered by Connect). Just put this on your /server (note it will be served at yourapp.meteor.com/fb/channel.html):

// serve channel.html file
var connect = __meteor_bootstrap__.require("connect");

__meteor_bootstrap__.app
  .use(connect.query())
  .use(function(req, res, next) {
    // Need to create a Fiber since we're using synchronous http
    // calls and nothing else is wrapping this in a fiber
    // automatically
    Fiber(function () {
      if (req.url === "/fb/channel.html") {
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.end('<script src="//connect.facebook.net/en_US/all.js"></script>');
      } else {
        // not an channel.html request. pass to next middleware.
        next();
        return;
      }
    }).run();
  });

Also this is working in production and the code is available at GitHub (includes cache headers).

like image 2
fjsj Avatar answered Nov 09 '22 20:11

fjsj