麻烦整合facebook认证与cordova应用程序

Trouble integrating facebook authentication with cordova app

本文关键字:cordova 应用程序 认证 facebook 麻烦      更新时间:2023-09-26

抱歉,我要疯了。

我已经坐了3天这个问题了-我正在使用Javascript编写一个混合移动应用程序,我已经集成了Facebook登录按钮。我已经添加了Openfb.js api来处理我的master.js中的登录和所需的功能。

facebook身份验证工作完美,它打开了特定应用程序所需的登录页面,并允许您登录,返回"成功",您再次被重定向到应用程序(inAppBrowser),但您没有登录到应用程序并定向到main.html页面。我简直是在兜圈子,快把我逼疯了。

当点击facebook登录按钮时,下面将启动该函数:

function facebookLogin() {
    showLoading(true);
    openFB.login(
        function (response) {
            if (response.status === 'connected') {
                getFacebookInfo();
                //showMessage('Facebook login succeeded, got access token: ' + response.authResponse.token);
            } else {
                showMessage('Facebook login failed: ' + response.error);
            }
        },
    { scope: 'email' });
}
下面是getFacebookInfo函数:
    function getFacebookInfo() {
    openFB.api({
        path: '/me',
        success: function (facebookData) {
            debugger;
            var data = new Object();
            var now = new Date();
            var dataLogin = new Object();
            data.profilePicture = new Object();
            data.username = facebookData.first_name + ' ' + facebookData.last_name;
            data.password = randomPassword();
            console.log(data.username);
            data.email = facebookData.email;
            data.gender = facebookData.gender;
            data.firstName = facebookData.first_name;
            data.surname = facebookData.last_name;
            data.mode = 'facebook';
            var dt = new Date();
            data.dateOfBirth = dt.getFullYear() + '/' + (dt.getMonth() + 1) + '/' + dt.getDate();
            postData("users", data, successFacebookRegistration);
            //getData("users", dataLogin, successLogin);            
        },
        error: function (data, obj) {
            debugger;
            showMessage('An error occurred acquiring account information from Facebook');
        }
    });
}

OpenFB.js如下:

    /**
 * OpenFB is a micro-library that lets you integrate your JavaScript application with Facebook.
 * OpenFB works for both BROWSER-BASED apps and CORDOVA/PHONEGAP apps.
 * This library has no dependency: You don't need (and shouldn't use) the Facebook SDK with this library. Whe running in
 * Cordova, you also don't need the Facebook Cordova plugin. There is also no dependency on jQuery.
 * OpenFB allows you to login to Facebook and execute any Facebook Graph API request.
 * @author Christophe Coenraets @ccoenraets
 * @version 0.4
 */
var openFB = (function () {
    var FB_LOGIN_URL = 'https://www.facebook.com/dialog/oauth',
        FB_LOGOUT_URL = 'https://www.facebook.com/logout.php',
        // By default we store fbtoken in sessionStorage. This can be overridden in init()
        tokenStore = window.sessionStorage,
        fbAppId,
        context = window.location.pathname.substring(0, window.location.pathname.indexOf("/", 2)),
        baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '') + context,
        oauthRedirectURL = baseURL + '/oauthcallback.html',
        logoutRedirectURL = baseURL + '/logoutcallback.html',
        // Because the OAuth login spans multiple processes, we need to keep the login callback function as a variable
        // inside the module instead of keeping it local within the login function.
        loginCallback,
        // Indicates if the app is running inside Cordova
        runningInCordova,
        // Used in the exit event handler to identify if the login has already been processed elsewhere (in the oauthCallback function)
        loginProcessed;
    console.log(oauthRedirectURL);
    console.log(logoutRedirectURL);
    document.addEventListener("deviceready", function () {
        runningInCordova = true;
    }, false);
    /**
     * Initialize the OpenFB module. You must use this function and initialize the module with an appId before you can
     * use any other function.
     * @param params - init paramters
     *  appId: The id of the Facebook app,
     *  tokenStore: The store used to save the Facebook token. Optional. If not provided, we use sessionStorage.
     */
    console.log("init");
    function init(params) {
        if (params.appId) {
            fbAppId = params.appId;
            if (params.cordova != null) {
                runningInCordova = params.cordova;
            }
        } else {
            throw 'appId parameter not set in init()';
        }
        if (params.tokenStore) {
            tokenStore = params.tokenStore;
        }
    }
    /**
     * Checks if the user has logged in with openFB and currently has a session api token.
     * @param callback the function that receives the loginstatus
     */
    console.log("getLoginStatus");
    function getLoginStatus(callback) {
        var token = tokenStore['fbtoken'],
            loginStatus = {};
        if (token) {
            loginStatus.status = 'connected';
            loginStatus.authResponse = { token: token };
        } else {
            loginStatus.status = 'unknown';
        }
        if (callback) callback(loginStatus);
    }
    /**
     * Login to Facebook using OAuth. If running in a Browser, the OAuth workflow happens in a a popup window.
     * If running in Cordova container, it happens using the In-App Browser. Don't forget to install the In-App Browser
     * plugin in your Cordova project: cordova plugins add org.apache.cordova.inappbrowser.
     *
     * @param callback - Callback function to invoke when the login process succeeds
     * @param options - options.scope: The set of Facebook permissions requested
     * @returns {*}
     */
    console.log("login");
    function login(callback, options) {
        var loginWindow,
            startTime,
            scope = '';
        if (!fbAppId) {
            return callback({ status: 'unknown', error: 'Facebook App Id not set.' });
        }
        // Inappbrowser load start handler: Used when running in Cordova only
        function loginWindow_loadStartHandler(event) {
            var url = event.url;
            if (url.indexOf("access_token=") > 0 || url.indexOf("error=") > 0) {
                // When we get the access token fast, the login window (inappbrowser) is still opening with animation
                // in the Cordova app, and trying to close it while it's animating generates an exception. Wait a little...
                var timeout = 600 - (new Date().getTime() - startTime);
                setTimeout(function () {
                    loginWindow.close();
                }, timeout > 0 ? timeout : 0);
                oauthCallback(url);
            }
        }
        // Inappbrowser exit handler: Used when running in Cordova only
        function loginWindow_exitHandler() {
            console.log('exit and remove listeners');
            // Handle the situation where the user closes the login window manually before completing the login process
            deferredLogin.reject({ error: 'user_cancelled', error_description: 'User cancelled login process', error_reason: "user_cancelled" });
            loginWindow.removeEventListener('loadstop', loginWindow_loadStartHandler);
            loginWindow.removeEventListener('exit', loginWindow_exitHandler);
            loginWindow = null;
            console.log('done removing listeners');
        }
        if (options && options.scope) {
            scope = options.scope;
        }
        loginCallback = callback;
        loginProcessed = false;
        //logout();
        if (runningInCordova) {
            oauthRedirectURL = "https://www.facebook.com/connect/login_success.html";
        }
        startTime = new Date().getTime();
        loginWindow = window.open(FB_LOGIN_URL + '?client_id=' + fbAppId + '&redirect_uri=' + oauthRedirectURL +
            '&response_type=token&scope=' + scope, '_blank', 'location=yes');
        // If the app is running in Cordova, listen to URL changes in the InAppBrowser until we get a URL with an access_token or an error
        if (runningInCordova) {
            loginWindow.addEventListener('loadstart', loginWindow_loadStartHandler);
            loginWindow.addEventListener('exit', loginWindow_exitHandler);
        }
        // Note: if the app is running in the browser the loginWindow dialog will call back by invoking the
        // oauthCallback() function. See oauthcallback.html for details.
    }
    /**
     * Called either by oauthcallback.html (when the app is running the browser) or by the loginWindow loadstart event
     * handler defined in the login() function (when the app is running in the Cordova/PhoneGap container).
     * @param url - The oautchRedictURL called by Facebook with the access_token in the querystring at the ned of the
     * OAuth workflow.
     */
    console.log("oauthCallback");
    function oauthCallback(url) {
        // Parse the OAuth data received from Facebook
        var queryString,
            obj;
        debugger;
        loginProcessed = true;
        if (url.indexOf("access_token=") > 0) {
            queryString = url.substr(url.indexOf('#') + 1);
            obj = parseQueryString(queryString);
            tokenStore['fbtoken'] = obj['access_token'];
            if (loginCallback) loginCallback({ status: 'connected', authResponse: { token: obj['access_token'] } });
        } else if (url.indexOf("error=") > 0) {
            queryString = url.substring(url.indexOf('?') + 1, url.indexOf('#'));
            obj = parseQueryString(queryString);
            if (loginCallback) loginCallback({ status: 'not_authorized', error: obj.error });
        } else {
            if (loginCallback) loginCallback({ status: 'not_authorized' });
        }
    }
    /**
     * Logout from Facebook, and remove the token.
     * IMPORTANT: For the Facebook logout to work, the logoutRedirectURL must be on the domain specified in "Site URL" in your Facebook App Settings
     *
     */
    console.log("logout");
    function logout(callback) {
        var logoutWindow,
            token = tokenStore['fbtoken'];
        /* Remove token. Will fail silently if does not exist */
        tokenStore.removeItem('fbtoken');
        if (token) {
            logoutWindow = window.open(FB_LOGOUT_URL + '?access_token=' + token + '&next=' + logoutRedirectURL, '_blank', 'location=yes');
            if (runningInCordova) {
                setTimeout(function () {
                    logoutWindow.close();
                }, 700);
            }
        }
        if (callback) {
            callback();
        }
    }
    /**
     * Lets you make any Facebook Graph API request.
     * @param obj - Request configuration object. Can include:
     *  method:  HTTP method: GET, POST, etc. Optional - Default is 'GET'
     *  path:    path in the Facebook graph: /me, /me.friends, etc. - Required
     *  params:  queryString parameters as a map - Optional
     *  success: callback function when operation succeeds - Optional
     *  error:   callback function when operation fails - Optional
     */
    console.log("api");
    function api(obj) {
        var method = obj.method || 'GET',
            params = obj.params || {},
            xhr = new XMLHttpRequest(),
            url;
        console.log("access_token (api)");
        params['access_token'] = tokenStore['fbtoken'];
        url = 'https://graph.facebook.com' + obj.path + '?' + toQueryString(params);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    if (obj.success) obj.success(JSON.parse(xhr.responseText));
                } else {
                    var error = xhr.responseText ? JSON.parse(xhr.responseText).error : { message: 'An error has occurred' };
                    if (obj.error) obj.error(error);
                }
            }
        };
        xhr.open(method, url, true);
        xhr.send();
    }
    /**
     * Helper function to de-authorize the app
     * @param success
     * @param error
     * @returns {*}
     */
    console.log("revokePermissions");
    function revokePermissions(success, error) {
        return api({
            method: 'DELETE',
            path: '/me/permissions',
            success: function () {
                tokenStore['fbtoken'] = undefined;
                success();
            },
            error: error
        });
    }
    function parseQueryString(queryString) {
        var qs = decodeURIComponent(queryString),
            obj = {},
            params = qs.split('&');
        params.forEach(function (param) {
            var splitter = param.split('=');
            obj[splitter[0]] = splitter[1];
        });
        return obj;
    }
    function toQueryString(obj) {
        var parts = [];
        for (var i in obj) {
            if (obj.hasOwnProperty(i)) {
                parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
            }
        }
        return parts.join("&");
    }
    // The public API
    return {
        init: init,
        login: login,
        logout: logout,
        revokePermissions: revokePermissions,
        api: api,
        oauthCallback: oauthCallback,
        getLoginStatus: getLoginStatus
    }
}());

最后,postData指向用户api:

    <?php
header("Access-Control-Allow-Origin: *");
require("json.php");    
require("utilities.php");    
class users extends rest_json_server{
    function get(){        
        $utils = new utilities();
        $mode           =   (isset($this->input['mode'])      ? $this->input['mode']      : '');
        $username       =   (isset($this->input['username'])  ? $this->input['username']  : '');
        $rawPassword    =   (isset($this->input['password'])  ? $this->input['password']  : '');
        $hashPassword   =   $utils->hash($rawPassword, null, true);
        $id             =   (isset($this->input['id'])        ? $this->input['id']        : '');
        if ($mode != 'recover'){
            $sql =  " SELECT *, ".
                    " (SELECT COUNT(p2.id) FROM posts p2 where p2.user_id = u.id) as totalPosts, ".
                    " (SELECT COUNT(f.id) FROM followings f where f.follower_id = u.id) as totalFollowings, ".
                    " (SELECT COUNT(f.id) FROM followings f where f.followed_id = u.id) as totalFollowers " .
                    " FROM users u ";
            $filter = '';
            if ($username != '' && $hashPassword != ''){
                $filter .= " WHERE (username = '" . $username . "' OR email = '" . $username . "') AND password = '" . $hashPassword . "' ";
            }
            else if ($id != ''){
                $filter .= " WHERE id = " . $id;
            }
            $result = $utils->useDatabase($sql . $filter);
            if (mysql_num_rows($result) == 0 || $filter == '')
            {
                $this->response(200, 'Username and password combination not found');
            }
            else
            {
                $this->response(201, mysql_fetch_assoc($result));
            }
        }
        else {
            $sql = ' SELECT * FROM users WHERE username = ''' . $username . ''' OR email = ''' . $username . ''' ';
            $result = $utils->useDatabase($sql);
            if (mysql_num_rows($result) == 0)
            {
                $this->response(200, 'Username not found');
            }
            else
            {
                $result = mysql_fetch_assoc($result);
                if (!$utils->sendEmail(
                    $result['email'], 
                    'Hi<br/><br/>' .
                    'Your password is: ' . $result['confirm'] .
                    '<br/><br/>' . 
                    'Best regards<br/>' .
                    'The Stylista team'))
                {
                    $this->response(500);
                }
                else
                {
                    $this->response(201, 'Your password has been emailed to you');
                }
            }
        }
    }
    function post(){
        $uploaddir = '../img/user-edit/';
        if (isset($_GET['file']))
        {
            $fileName = date('Ymdhis');
            foreach($_FILES as $file)
            {
                $fileName .= basename($file['name']);
                move_uploaded_file($file['tmp_name'], $uploaddir . $fileName);
            }
            $this->response(201, $fileName);
        }
        else
        {
            $utils = new utilities();
            $username =     (isset($this->input_json->username) ? $this->input_json->username : '');
            $rawPassword =  (isset($this->input_json->password) ? $this->input_json->password : '');
            $hashedPassword = $utils->hash($rawPassword, null, true);
            $firstName =    (isset($this->input_json->firstName) ? $this->input_json->firstName : '');
            $surname =      (isset($this->input_json->surname) ? $this->input_json->surname : '');
            $email =        (isset($this->input_json->email) ? $this->input_json->email : '');
            $gender =       (isset($this->input_json->gender) ? $this->input_json->gender : '');
            $dateOfBirth =  (isset($this->input_json->dateOfBirth) ? $this->input_json->dateOfBirth : '');
            $country =      (isset($this->input_json->country) ? $this->input_json->country : '');
            $province =     (isset($this->input_json->province) ? $this->input_json->province : '');
            $city =         (isset($this->input_json->city) ? $this->input_json->city : '');
            $blogURL =      (isset($this->input_json->blogURL) ? $this->input_json->blogURL : '');
            $websiteURL =   (isset($this->input_json->websiteURL) ? $this->input_json->websiteURL : '');
            $id =           (isset($this->input_json->id) ? $this->input_json->id : '');
            $about =        (isset($this->input_json->about) ? $this->input_json->about : '');
            $profilePicFileName = '';
            $success        = true;
            $mode =         (isset($this->input_json->mode) ? $this->input_json->mode : '');
            $existsFacebook = false;
            $processSQL     = true;
            if (isset($this->input_json->profilePicture) && isset($this->input_json->profilePicture->imageData) && isset($this->input_json->profilePicture->fileName)){
                $profilePicFileName = $this->input_json->profilePicture->fileName;
                // Save profile picture
                $profilePicData = $this->input_json->profilePicture->imageData;
                $profilePicData = str_replace('data:image/jpeg;base64,', '', $profilePicData);
                $profilePicData = str_replace(' ', '+', $profilePicData);
                $data = base64_decode($profilePicData);
                $file = $uploaddir . $profilePicFileName;
                $success = file_put_contents($file, $data);
            }
            if (!$success){
                $processSQL = false;
                $this->response(500, 'Failed to save profile picture');
            }
            if ($username = '' || $firstName = '' || $surname = '' || $email = '') {
                $processSQL = false;
                $this->response(400, 'Certain fields are blank');
            }
            $result = $utils->useDatabase(" SELECT * FROM users WHERE email = '" . $email . "' ");
            if (mysql_num_rows($result)!=0 && $id == '')
            {
                if ($mode == '')
                {
                    $processSQL = false;
                    $this->response(200,'Email exists');
                }
                else {
                    if ($id == '')
                    {
                        $id = mysql_fetch_assoc($result)['id'];
                    }
                    $existsFacebook = true;
                }
            }
            $result = $utils->useDatabase(" SELECT * FROM users WHERE username = '" . $username . "' ");
            if (mysql_num_rows($result)!=0 && $id == '')
            {
                if ($mode == '')
                {
                    $processSQL = false;
                    $this->response(200,'Username already exists');
                }
                else {
                    if ($id == '')
                    {
                        $id = mysql_fetch_assoc($result)['id'];
                    }
                    $existsFacebook = true;
                }
            }
            if ($processSQL)
            {
                $sql = '';
                if (($id == '' && $mode == '') || ($mode == 'facebook' && $existsFacebook == false)){
                    $sql =  " INSERT INTO users (password, created, modified, country, state_province, city, birthday, blog_url, website_url, email, first_name, last_name, username, email_ver, gender, confirm, banning, profile_pic) " .
                            " VALUES ('".$hashedPassword."', NOW(), NOW(), '".$country."', '".$province."', '".$city."', '".$dateOfBirth."', '".$blogURL."', '".$websiteURL."', '".$email."', '".$firstName."', '" .
                            $surname."', '".$username."', 1, '".$gender."', '".$rawPassword."', '', '" . $profilePicFileName . "')";
                    $result = $utils->useDatabase($sql);
                    $sql = 
                        " SELECT *, ".
                            " (SELECT COUNT(p2.id) FROM posts p2 where p2.user_id = u.id) as totalPosts, ".
                            " (SELECT COUNT(f.id) FROM followings f where f.follower_id = u.id) as totalFollowers, ".
                            " (SELECT COUNT(f.id) FROM followings f where f.followed_id = u.id) as totalFollowings " .
                        " FROM users u " .
                        " WHERE u.email = '" . $email . "'";
                    $result = $utils->useDatabase($sql);
                    $this->response(201, mysql_fetch_assoc($result));
                }
                else{
                    $updateSet = ($rawPassword != '' ? " password = '" . $hashedPassword ."', " : '') .
                            " modified = NOW(), ".
                            ($country != '' ? " country = '" . $country . "', " : '') .
                            ($province != '' ? " state_province= '" . $province . "', " : '') .
                            ($city != '' ? " city = '" . $city ."', " : '') .
                            ($dateOfBirth != '' ? " birthday = '" . $dateOfBirth ."', " : '') .
                            ($blogURL != '' ? " blog_url = '" . $blogURL . "', " : '') .
                            ($websiteURL != '' ? " website_url = '" . $websiteURL . "', " : '') .
                            ($email != '' ? " email = '" . $email . "', " : '') .
                            ($firstName != '' ? " first_name = '" . $firstName . "', " : '') .
                            ($surname != '' ? " last_name = '" . $surname . "', " : '') .
                            ($username != '' ? " username = '" . $username . "', " : '') .
                            ($gender != '' ? " gender = '" . $gender . "', " : '') .
                            ($about != '' ? " about = '" . $about . "', " : '') .
                            ($rawPassword != '' ? " confirm = '" . $rawPassword . "', " : '') .
                            ($profilePicFileName != '' ? " profile_pic = '" . $profilePicFileName . "', " : '');
                    $sql =
                        " UPDATE users " .
                        " SET " . 
                            substr($updateSet, 0, strlen($updateSet) - 2) .
                        " WHERE id = " . $id;
                    $result = $utils->useDatabase($sql);
                    $sql = 
                        " SELECT *, ".
                            " (SELECT COUNT(p2.id) FROM posts p2 where p2.user_id = u.id) as totalPosts, ".
                            " (SELECT COUNT(f.id) FROM followings f where f.follower_id = u.id) as totalFollowers, ".
                            " (SELECT COUNT(f.id) FROM followings f where f.followed_id = u.id) as totalFollowings " .
                        " FROM users u " .
                        " WHERE u.id = " . $id;
                    $result = $utils->useDatabase($sql);
                    $this->response(201, mysql_fetch_assoc($result));
                }
            }
        }
    }
    function put(){
        /**
         * No supported
         */
        $this->response(405);
    }

}
$start = new users();
$start->handle_request();

根据我使用Cordova的经验,它不能很好地处理cookie和会话状态。我的直觉是Facebook设置了一个身份验证cookie——它会被立即删除。所以你可能做的一切都是对的,只是Cordova让你弯腰。

所以你可能需要编写一个自定义插件,将Facebook身份验证状态存储在webview之外。

你的目标是iOS还是Android,还是两者都有?我知道在iOS上创建一个自定义的Facebook插件来处理身份验证其实很容易(我自己就是这么做的)。它将认证状态存储在webview的之外,并存储在应用程序数据本身中,因此即使在切换页面时,它也会记住认证状态。

应该说的是,在我的iOS Cordova应用中做了大量的工作后,Cordova的多重缺陷开始滚雪球…然后决定直接写原生的。

CUSTOM PLUGIN FOR IOS

假设你对iOS上Cordova的插件是如何工作的有一个想法。还包括如何在别人的墙上分享锻炼活动的代码;)

Facebook.h

#import <Cordova/CDVCommandDelegate.h>
#import <Cordova/CDV.h>
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
@interface FacebookPlugin : CDVPlugin <FBSDKSharingDelegate>
- (void) IsLoggedIn:(CDVInvokedUrlCommand *)command;
- (void) LoginWithReadPermissions:(CDVInvokedUrlCommand *)command;
- (void) LoginWithWritePermissions:(CDVInvokedUrlCommand *)command;
- (void) Logout:(CDVInvokedUrlCommand *)command;
@end

Facebook.m

#import "Facebook.h"
#import <Foundation/Foundation.h>
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
#import <FBSDKShareKit/FBSDKShareOpenGraphObject.h>
#import <FBSDKShareKit/FBSDKShareOpenGraphAction.h>
@implementation FacebookPlugin
// [FBSDKAccessToken setCurrentAccessToken:nil] and [FBSDKProfile setCurrentProfile:nil].
- (void) IsLoggedIn:(CDVInvokedUrlCommand *)command
{
    FBSDKAccessToken* token = [FBSDKAccessToken currentAccessToken];
    NSString* stringResult;
    if (token != nil)
    {
        stringResult = @"YES";
    } else {
        stringResult = @"NO";
    }
    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:stringResult];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
- (void) Logout:(CDVInvokedUrlCommand *)command
{
    FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
    [login logOut];
}
- (void) LoginWithReadPermissions:(CDVInvokedUrlCommand *)command
{
    FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
    [login logInWithReadPermissions:@[@"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
        if (error) {
                CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
          } else if (result.isCancelled) {
                CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
          } else {
                CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
          }
    }];
}
- (void) LoginWithWritePermissions:(CDVInvokedUrlCommand *)command
{
    if ([[FBSDKAccessToken currentAccessToken] hasGranted:@"publish_actions"]) {
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
    } else {
        FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
        [login logInWithPublishPermissions:@[@"publish_actions"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
              if (error) {
                    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
                    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
              } else if (result.isCancelled) {
                    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
                    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
              } else {
                    // If you ask for multiple permissions at once, you
                    // should check if specific permissions missing
                    if ([result.grantedPermissions containsObject:@"publish_actions"]) {
                        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
                        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
                    } else {
                        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
                        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
                    }
              }
        }];
    };
}
- (void) ShareWorkout:(CDVInvokedUrlCommand *)command
{
    NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil];
    //get the app id
    NSString *appId = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FacebookAppID"];
    // Create an object
    NSDictionary *properties = @{
        @"fb:app_id": appId,
        @"og:type": @"fitness.course",
        @"og:url": [options objectForKey:@"websiteURL"],
        @"og:title": [options objectForKey:@"courseTitle"],
        @"og:image": [options objectForKey:@"logoURL"],
        //@"fitness:duration:value": [options objectForKey:@"workoutDuration"],
        //@"fitness:duration:value": [options objectForKey:@"workoutDurationUnit"],
        //@"fitness:custom_unit_energy:value": [options objectForKey:@"workoutCalories"],
        //@"fitness:custom_unit_energy:units": [options objectForKey:@"workoutCalorieUnit"]
    };
    FBSDKShareOpenGraphObject *graphObject = [FBSDKShareOpenGraphObject objectWithProperties:properties];
    // Create an action [fitness.runs, fitness.walks for actionType]
    FBSDKShareOpenGraphAction *action = [[FBSDKShareOpenGraphAction alloc] init];
    action.actionType = [options objectForKey:@"actionType"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterFullStyle];
    [action setObject:graphObject forKey:@"fitness:course"];
    //[action setString:[dateFormatter stringFromDate:[NSDate date]] forKey:@"created_time"];
    [action setObject:[options objectForKey:@"userMessage"] forKey:@"message"];
    [action setString:@"true" forKey:@"fb:explicitly_shared"];
    //[action setString:[options objectForKey:@"workoutStart"] forKey:@"start_time"];
    FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init];
    content.action = action;
    content.previewPropertyName = @"fitness:course";
    [FBSDKShareDialog showFromViewController:self.viewController withContent:content delegate:self];
}
#pragma mark === delegate method
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results
{
    NSLog(@"completed share:%@", results);
}
- (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
    NSLog(@"sharing error:%@", error);
    NSString *message = error.userInfo[FBSDKErrorLocalizedDescriptionKey] ?:
    @"There was a problem sharing, please try again later.";
    NSString *title = error.userInfo[FBSDKErrorLocalizedTitleKey] ?: @"Oops!";
    [[[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
}
- (void)sharerDidCancel:(id<FBSDKSharing>)sharer
{
    NSLog(@"share cancelled");
}
@end

Facebook.js

var Facebook = function() {}
cordova.addConstructor(function() {
    console.log('initializing Facebook');
    if (!window.Cordova) {
        window.Cordova = cordova;
    };
    if(!window.plugins) window.plugins = {};
    window.plugins.Facebook = new Facebook();
});

    Facebook.prototype.LoginWithReadPermissions = function(successCallback, errorCallback) {
            return cordova.exec(
                    function() {
                        successCallback();
                    }, 
                    function() {
                        errorCallback();
                    },
                    'FacebookPlugin', 'LoginWithReadPermissions', []);
    };
    Facebook.prototype.LoginWithWritePermissions = function(successCallback, errorCallback) {
            return cordova.exec(
                    function() {
                        successCallback();
                    }, 
                    function() {
                        errorCallback();
                    },
                    'FacebookPlugin', 'LoginWithWritePermissions', []);
    };
     Facebook.prototype.Logout = function(successCallback, errorCallback) {
            return cordova.exec(
                    function() {
                    }, 
                    function() {
                    },
                    'FacebookPlugin', 'Logout', []);
    };
    Facebook.prototype.ShareWorkout = function(successCallback, errorCallback, options) {
            return cordova.exec(
                    function() {
                        successCallback();
                    }, 
                    function() {
                        errorCallback();
                    },
                    'FacebookPlugin', 'ShareWorkout', [options]);
    };