How can I login to Meteor with native device Facebook?

facebook, ios, meteor, meteorite, node.js

Solution

To login with Facebook using an access token obtained by another means, like iOS Facebook SDK, define a method on the server that calls the appropriate `Accounts` method:

$FB = function () {
    if (Meteor.isClient) {
        throw new Meteor.Error(500, "Cannot run on client.");
    }

    var args = Array.prototype.slice.call(arguments);
    if (args.length === 0) {
        return;
    }
    var path = args[0];
    var i = 1;
    // Concatenate strings together in args
    while (_.isString(args[i])) {
        path = path + "/" + args[i];
        i++;
    }

    if (_.isUndefined(path)) {
        throw new Meteor.Error(500, 'No Facebook API path provided.');
    }
    var FB = Meteor.npmRequire('fb');

    var fbResponse = Meteor.sync(function (done) {
        FB.napi.apply(FB, [path].concat(args.splice(i)).concat([done]));
    });

    if (fbResponse.error !== null) {
        console.error(fbResponse.error.stack);
        throw new Meteor.Error(500, "Facebook API error.", {error: fbResponse.error, request: args});
    }

    return fbResponse.result;
};

Meteor.methods({
    /**
     * Login to Meteor with a Facebook access token
     * @param accessToken Your Facebook access token
     * @returns {*}
     */
    facebookLoginWithAccessToken: function (accessToken) {
        check(accessToken, String);

        var serviceData = {
            accessToken: accessToken
        };

        // Confirm that your accessToken is you
        try {
            var tokenInfo = $FB('debug_token', {
                input_token: accessToken,
                access_token: Meteor.settings.facebook.appId + '|' + Meteor.settings.facebook.secret
            });
        } catch (e) {
            throw new Meteor.Error(500, 'Facebook login failed. An API error occurred.');
        }

        if (!tokenInfo.data.is_valid) {
            throw new Meteor.Error(503, 'This access token is not valid.');
        }

        if (tokenInfo.data.app_id !== Meteor.settings.facebook.appId) {
            throw new Meteor.Error(503, 'This token is not for this app.');
        }

        // Force the user id to be the access token's user id
        serviceData.id = tokenInfo.data.user_id;

        // Returns a token you can use to login
        var loginResult = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});

        // Login the user
        this.setUserId(loginResult.userId);

        // Return the token and the user id
        return loginResult;
    }
}

This code depends on the `meteorhacks:npm` package. You should call `meteor add meteorhacks:npm` and have a `package.json` file with the Facebook node API: `{ "fb": "0.7.0" }`.

If you use `demeteorizer` to deploy your app, you will have to edit the output `package.json` and set the `scrumptious` dependency from `"0.0.1"` to `"0.0.0"`.

On the client, call the method with the appropriate parameters, and you're logged in!

In Meteor 0.8+, the result of `Accounts.updateOrCreateUserFromExternalService` has changed to an object containing `{userId: ...}` and furthermore, no longer has the stamped token.

Problem

Suppose I logged into my device's Facebook authentication, like system Facebook on iOS. I obtain an `access token`. How can I use the `access token` to login to Meteor's Facebook `Oauth` provider?

Original source