Как обновить токен доступа пользователя Facebook, если я имею дело с большим количеством AJAX?

Пожалуйста, скажите мне, правильно ли я понимаю. (потому что я могу и не быть.)

  1. пользователь публикует что-то на моем сайте. (Он проверил "также опубликовать в Facebook".)
  2. клиент отправляет запрос AJAX POST на мой сервер, и мой сервер вставляет запись в мою базу данных.
  3. сервер понимает, что токен доступа пользователя facebook истек, поэтому он отправляет ответ обратно клиенту, сохраняя сообщение в сеансе.
  4. клиент делает window.location.replace(facebook_oauth_dialog_url)
  5. затем пользователь увидит внезапную "вспышку", Перейдя на Facebook, а затем вернется на веб-сайт. Мой сервер получает новый маркер доступа.
  6. мой сервер проверяет сессии, чтобы увидеть, что должно быть опубликовано в Facebook. И затем он использует новый токен доступа для публикации этого в Facebook.

это действительно так утомительно? Почему я не могу обновить сервер приложения без участия пользователя в диалоговом окне?

весь мой сайт является основой.js. Что значит, это одна большая страница. Я не могу перепрыгивать пользователя между Facebook и моим сайтом, как это.

7 ответов


идея в том, чтобы использовать Facebook JS-SDK методы:

  1. пользователь Регистрация Сообщение В Facebook опции
  2. вы проверяете, подключен ли текущий пользователь к вашему приложению (используя FB.getLoginStatus())
  3. если пользователь подключен, у вас есть два варианта:
    • сообщение непосредственно с помощью FB.api способ или
    • отправить access_token на ваш сервер для завершения процесса post там
  4. если пользователь не подключен (или не вошел в Facebook), используйте FB.login() метод

вот краткий пример (с Live демо!) для начала:

<!DOCTYPE html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<body>
<div id="fb-root"></div>
<script>
var fbLoaded = false;
window.fbAsyncInit = function() {
    FB.init({
      appId      : 'YOUR_APP_ID', // App ID
      //channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File
      status     : true, // check login status
      cookie     : true, // enable cookies to allow the server to access the session
      xfbml      : true  // parse XFBML
    });
    fbLoaded = true;
    // Additional initialization code here

};

function postForm() {
    var msg = document.myForm.msg.value;
    // do form validation here, e.g:
    if(!msg.length) {
        alert("You should enter a message!");
        return false;
    }

    // do we need to post to Facebook?
    if(document.myForm.toFB.checked) {
        // is the library loaded?
        if(!fbLoaded) {
            alert("Facebook JS-SDK is not yet loaded. Please try again later or uncheck Post To Facebook option");
            return false;
        }

        FB.getLoginStatus(function(response) {
            if (response.status === 'connected') {
                var uid = response.authResponse.userID;
                var accessToken = response.authResponse.accessToken;
                /* 
                *  message can be posted to Facebook directly
                *  using the FB.api method or accessToken
                *  can be sent to the server and do the call
                *  from there
                */
                myAjaxCall(msg, accessToken);
            } else {
                // status is either not_authorized or unknown
                FB.login(function(response) {
                    if (response.authResponse) {
                        var accessToken = response.authResponse.accessToken;
                        myAjaxCall(msg, accessToken);
                    } else {
                        alert('User cancelled login or did not fully authorize.');
                    }
                }, {scope: 'publish_stream'});
            }
        });
    } else {
        myAjaxCall(msg);
    }
    return false;
}

function myAjaxCall(m,a) {
    alert("Here you make the ajax call\nMessage: " + m + "\nAccess Token: " + a);
}

  // Load the SDK Asynchronously
  (function(d){
     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.js";
     ref.parentNode.insertBefore(js, ref);
   }(document));
</script>

<form id="myForm" name="myForm" action="post" onSubmit="return postForm()">
<p><label>Your Message:</label><br/><textarea name="msg"></textarea></p>
<p><label>Post to Facebook?</label><input type="checkbox" value="1" name="toFB" /></p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>

перед отправкой на сервер, вызов FB.getLoginStatus() на клиенте, чтобы получить последний маркер доступа. При использовании этого метода нет flash и нет взаимодействия с пользователем, так как он просто захватывает новый токен доступа.

FB.getLoginStatus( function ( response ) {
    if ( response.authResponse ) { 
        var accessToken = response.authResponse.accessToken;
        //post to server
    };
} );

Я надеюсь, вы знали, что если у вас есть разрешение publish_stream, вам не нужен маркер доступа, вот документация для publish_stream и Ниже приведено решение для четырех сценариев

1.Срок действия токена истекает по истечении времени (по умолчанию-2 часа).
2.Пользователь меняет свой пароль, который делает недействительным маркер доступа.
3.Пользователь де-авторизует ваше приложение.
4.Пользователь выходит из Фейсбук.

чтобы обеспечить лучший опыт для ваших пользователей, ваше приложение должно быть готово ловить ошибки для вышеуказанных сценариев. В коде ниже показано, как обрабатывать эти ошибки и получить новый маркер доступа.

при перенаправлении пользователя в диалоговое окно auth пользователю не запрашиваются разрешения, если пользователь уже авторизовал приложение. Facebook вернет вам действительный токен доступа без диалогового окна пользователя. Однако если пользователь деавторизовал ваше приложение, то пользователю необходимо будет повторно авторизовать приложение для вас, чтобы получить access_token.

<?php
$app_id = "YOUR_APP_ID";
$app_secret = "YOUR_APP_SECRET"; 
$my_url = "YOUR_POST_LOGIN_URL";

// known valid access token stored in a database 
$access_token = "YOUR_STORED_ACCESS_TOKEN";

$code = $_REQUEST["code"];

// If we get a code, it means that we have re-authed the user 
//and can get a valid access_token. 
if (isset($code)) {
$token_url="https://graph.facebook.com/oauth/access_token?client_id="
  . $app_id . "&redirect_uri=" . urlencode($my_url) 
  . "&client_secret=" . $app_secret 
  . "&code=" . $code . "&display=popup";
$response = file_get_contents($token_url);
$params = null;
parse_str($response, $params);
$access_token = $params['access_token'];
}


// Attempt to query the graph:
$graph_url = "https://graph.facebook.com/me?"
. "access_token=" . $access_token;
$response = curl_get_file_contents($graph_url);
$decoded_response = json_decode($response);

//Check for errors 
if ($decoded_response->error) {
// check to see if this is an oAuth error:
if ($decoded_response->error->type== "OAuthException") {
  // Retrieving a valid access token. 
  $dialog_url= "https://www.facebook.com/dialog/oauth?"
    . "client_id=" . $app_id 
    . "&redirect_uri=" . urlencode($my_url);
  echo("<script> top.location.href='" . $dialog_url 
  . "'</script>");
}
else {
  echo "other error has happened";
}
} 
else {
// success
echo("success" . $decoded_response->name);
echo($access_token);
}

// note this wrapper function exists in order to circumvent PHP’s 
//strict obeying of HTTP error codes.  In this case, Facebook 
//returns error code 400 which PHP obeys and wipes out 
//the response.
function curl_get_file_contents($URL) {
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_URL, $URL);
$contents = curl_exec($c);
$err  = curl_getinfo($c,CURLINFO_HTTP_CODE);
curl_close($c);
if ($contents) return $contents;
else return FALSE;
}
?>

для получения более детально информации вы можете посетить этот ссылке
Спасибо


обновление

ну, вы делаете что-то не так, вам не нужно продлевать токен доступа, даже если его истек все, что вам нужно, это отправить пользователю facebook id & контент, который вы хотите опубликовать вместе на свой сервер ajax, а затем опубликовать его без токена доступа просто проверьте здесь

опубликовать поток из приложения - для не вошедшего в систему пользователя, используя Graph API, php SDK

Если у вас есть разрешение publish_stream, вам не нужен маркер доступа это-документация для publish_stream https://developers.facebook.com/docs/reference/api/permissions/

позволяет вашему приложению публиковать контент, комментарии и лайки для пользователя stream и к потокам друзей пользователя. С этого разрешения, вы можете публиковать контент в ленте пользователя в любое время, без требующее области offline_access. Однако, обратите внимание, что Facebook рекомендует инициированную пользователем модель общего доступа.


У меня проблема в другом проекте.

способ, которым я его обрабатываю, - создать скрытый iframe. В первый раз, когда вам нужно, чтобы пользователь принял привилегию, используйте главное окно для перенаправления. тогда, когда вы уверены, что пользователь уже принял привилегию, используйте скрытый iframe для взаимодействия с facebook.

пользователь не увидит "flash", потому что это будет сделано в iframe.

Я сделал это с GWT. Вот код, который я использовал : это общается с Facebook через iframe и проверяет токен доступа каждые 500 мс, чтобы узнать, действителен ли токен.

код находится в java (скомпилирован в javascript с использованием gwt).

public class FacebookConnector extends Composite
{
        public static final String                                      ARG_ACCESS_TOKEN_EXPIRES        = "fb_accessTokenExpires";
        public static final String                                      ARG_GAME_FACEBOOK_NAME          = "gameFBName";
        public static final String                                      ARG_GAME_FACEBOOK_ID            = "gameFBId";

        private static FacebookConnectorUiBinder        uiBinder                                        = GWT.create(FacebookConnectorUiBinder.class);

        interface FacebookConnectorUiBinder extends UiBinder<Widget, FacebookConnector>
        {
        }

        private static FacebookConnector        me;

        public static FacebookConnector getInstance()
        {
                if (me == null)
                {
                        me = new FacebookConnector();
                }

                return me;
        }

        @UiField
        IFrameElement   iframe;

        private Date    accessToken;
        private Timer   timer;

        protected FacebookConnector()
        {
                initWidget(uiBinder.createAndBindUi(this));

                if (ArgManager.getArg(ARG_ACCESS_TOKEN_EXPIRES) != null)
                {
                        accessToken = new Date(Long.parseLong(ArgManager.getArg(ARG_ACCESS_TOKEN_EXPIRES)));
                }
        }

        public void checkAccessToken(final AbstractAsyncCallback<Void> callback)
        {
                if (accessToken == null || accessToken.before(new Date()))
                {
                        // send authentication
                        String url = "https://graph.facebook.com/oauth/authorize?client_id="
                                        + ArgManager.getArg(ARG_GAME_FACEBOOK_ID) + "&scope=user_birthday,email&redirect_uri="
                                        + ArgManager.getArg(ArgManager.ARG_URL_FACEBOOK_BASE) + "page/facebook-step2%3FgameName%3D"
                                        + ArgManager.getGameShortcut();

                        iframe.setSrc(url);

                        // check url
                        timer = new Timer() {

                                @Override
                                public void run()
                                {
                                        ClientFactory.getInstance().getService().getAccessTokenExpires(new AbstractAsyncCallback<Date>() {

                                                @Override
                                                public void onSuccess(Date result)
                                                {
                                                        super.onSuccess(result);

                                                        if (result != null && result.after(new Date()))
                                                        {
                                                                accessToken = result;

                                                                // call the callback
                                                                callback.onSuccess(null);
                                                        }
                                                        else
                                                        {
                                                                // try again in one second
                                                                timer.schedule(1000);
                                                        }

                                                }

                                        });

                                }
                        };

                        // launch timer in 500 milliseconds
                        timer.schedule(500);
                }
                else
                {
                        callback.onSuccess(null);
                }
        }
}

надеюсь, это поможет вам.


вы не можете просто выполнить обмен на стороне сервера, потому что это обходит контроль пользователя над авторизацией.

как уже говорили другие, вы должны использовать JavaScript SDK для облегчения обновления маркера доступа. По умолчанию он использует iframe и возвращается во всплывающее окно для связи с Facebook. Это должно хорошо работать с вашим позвоночником.приложение js.

мне нравится определять функцию javascript, которая принимает успех и отрицает обратные вызовы для выполнения после проверка статуса авторизации facebook:

function checkFBAuth(success, denied, scope) {
    FB.getLoginStatus(function (response) {
        if (response.status === 'connected') {
            success(response);
        } else {
            FB.login(function(response) {
                if (response.status === 'connected') {
                    success(response);
                } else {
                    denied(response);
                }
            }, scope);
        } 
    });
};

это будет идти вперед и работать FB.login если сеанс пользователя истек. В вашем успехе обратного вызова, вы также можете пройти response.authResponse.signedRequest как signed_request в ваших данных AJAX POST. Это позволит большинству FB SDK (например, PHP SDK) распознавать и проверять подписанный запрос и устанавливать идентификатор пользователя и маркер доступа. Вы также можете пройти весь response.authResponse данные с вашего поста. Это имеет accessToken, userID и expiresIn время.

см.https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/ для документов на сайте разработчиков Facebook.

кроме того, если вы включите миграцию устаревания автономного доступа, вы можете обменять токен доступа, чтобы продлить срок действия до 60 дней вместо 2 часов по умолчанию. См.https://developers.facebook.com/docs/offline-access-deprecation/


Как сказал @ThinkingStiff, ключевым моментом является то, что вам нужно вызов FB.getLoginStatus () на клиенте, чтобы получить последний токен доступа

в эти дни все классные дети обрабатывают свои логины и извлекают свои маркеры доступа через JavaScript с помощью SDK. А почему бы и нет? Пользователи любят его!

после того, как JavaScript SDK извлекает маркер доступа, все запросы AJAX на ваш сервер также будут иметь доступ к этому маркеру доступа. То есть, это автоматически передается вместе с каждым Запрос AJAX в виде файла cookie.

Итак, на стороне сервера вы можете получить маркер доступа через cookies (у нашего друга StackOverflow есть некоторые ответы, связанные с поиском этого cookie). Однако, если вы сделаете другую классную вещь и используете PHP SDK, вам даже не придется задумываться об этом, потому что он автоматически захватит cookie для вас, если он есть!