FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri()) возвращает ноль в Facebook

Я разработал приложение mvc 5, используя nopcommerce, и я использую вход в Facebook с помощью внешнего обратного вызова, он работал, но теперь он не работает, и я не могу выяснить реальную проблему. И используя этот код ниже

this.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri());

и он всегда возвращает мне значение NULL, и статус аутентификации не удался. Я искал в Интернете и делал все, и следовал этим шагам, но все равно не могу войти через Facebook.

Мой код такой в ​​FacebookProviderAuthorizer.cs

private AuthorizeState VerifyAuthentication(string returnUrl)
{
   var authResult = DotNetOpenAuth.AspNet.Clients.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri());

   if (authResult.IsSuccessful)
   {
   }
}

А затем напишите метод обратного вызова

private Uri GenerateLocalCallbackUri()
{
    string url = string.Format("{0}plugins/externalauthFacebook/logincallback/", _webHelper.GetStoreLocation());
    return new Uri(url);            
}

Затем создайте URL-адрес для входа в службу

private Uri GenerateServiceLoginUrl()
{
   //code copied from DotNetOpenAuth.AspNet.Clients.FacebookClient file
   var builder = new UriBuilder("https://www.facebook.com/dialog/oauth");
   var args = new Dictionary<string, string>();
   args.Add("client_id", _facebookExternalAuthSettings.ClientKeyIdentifier);
   args.Add("redirect_uri", GenerateLocalCallbackUri().AbsoluteUri);
   args.Add("response_type", "token");
   args.Add("scope", "email");
   AppendQueryArgs(builder, args);
   return builder.Uri;
}

6 ответов

Решение

Мы столкнулись с этой же проблемой в понедельник, 27.03.2017, когда Facebook прекратил поддержку их Graph API v2.2.

Мы также используем DotNetOpenAuth, который изначально был установлен через Nuget. Исходный код доступен по ссылке ниже:

https://github.com/DotNetOpenAuth/DotNetOpenAuth

В частности, мы обнаружили, что наш код использует ветку 4.3, которая содержит исходный код для DotNetOpenAuth.AspNet.DLL. После проверки источника мы обнаружили, что проблема заключается в следующем фрагменте кода из DotNetOpenAuth.AspNet\Clients\OAuth2\FacebookClient.cs, расположенного в методе QueryAccessToken:

using (WebClient client = new WebClient()) {
     string data = client.DownloadString(builder.Uri);
     if (string.IsNullOrEmpty(data)) {
          return null;
     }

     var parsedQueryString = HttpUtility.ParseQueryString(data);
     return parsedQueryString["access_token"];
}

Проблема, в частности, заключается в вызове ParseQueryString. Начиная с v2.3 API, данные больше не возвращаются в виде строки запроса HTML, а в стандартном формате JSON.

Чтобы это исправить, мы создали наш собственный класс, унаследованный от OAuth2Client, и импортировали большую часть того же кода из FacebookClient.cs. Затем мы заменили приведенный выше фрагмент кода кодом, который анализирует ответ JSON для извлечения access_token и возвращает его вместо этого. Вы можете увидеть пример того, как сделать это в том же классе FacebookClient, внутри метода GetUserData:

FacebookGraphData graphData;
var request =
    WebRequest.Create(
        "https://graph.facebook.com/me?access_token=" +
             MessagingUtilities.EscapeUriDataStringRfc3986(accessToken));
using (var response = request.GetResponse()) {
     using (var responseStream = response.GetResponseStream()) {
        graphData = JsonHelper.Deserialize<FacebookGraphData>(responseStream);
     }
}

Единственное другое изменение состояло в том, чтобы зарегистрировать наш пользовательский класс вместо класса FacebookClient, чтобы обратный вызов OAuth использовал его для обработки сообщения из API Facebook. Как только мы это сделали, все снова заработало.

Я использовал код, предоставленный @Vishal, и получил то же самое.

Главное, на что мы должны обратить внимание - переопределить метод QueryAccessToken, чтобы использовать ответ json.

protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
    {
        var uri = BuildUri(TokenEndpoint, new NameValueCollection
            {
                { "code", authorizationCode },
                { "client_id", _appId },
                { "client_secret", _appSecret },
                { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
            });

        var webRequest = (HttpWebRequest)WebRequest.Create(uri);
        string accessToken = null;            
        HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

        // handle response from FB 
        // this will not be a url with params like the first request to get the 'code'
        Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);

        using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
        {
            var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
            var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));

            Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
            accessToken = desirializedJsonObject["access_token"].ToString();
        }
        return accessToken;
    }

Шаги для достижения этой цели: Шаг 1. Что вам нужно сделать, это добавить один файл с именем FacebookClientOverride.cs(кроме FacebookClient.cs)

Вот фрагмент кода всего файла.

 using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;

public class FacebookClient : OAuth2Client
{
    #region Constants and Fields

    /// <summary>
    /// The authorization endpoint.
    /// </summary>
    private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";

    /// <summary>
    /// The token endpoint.
    /// </summary>
    private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";

    /// <summary>
    /// The user info endpoint.
    /// </summary>
    private const string UserInfoEndpoint = "https://graph.facebook.com/me";

    /// <summary>
    /// The app id.
    /// </summary>
    private readonly string _appId;

    /// <summary>
    /// The app secret.
    /// </summary>
    private readonly string _appSecret;

    /// <summary>
    /// The requested scopes.
    /// </summary>
    private readonly string[] _requestedScopes;

    #endregion

    /// <summary>
    /// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
    /// </summary>
    /// <param name="appId">The Facebook App Id</param>
    /// <param name="appSecret">The Facebook App Secret</param>
    public FacebookClient(string appId, string appSecret)
        : this(appId, appSecret, new[] { "email" }) { }

    /// <summary>
    /// Creates a new Facebook OAuth2 client.
    /// </summary>
    /// <param name="appId">The Facebook App Id</param>
    /// <param name="appSecret">The Facebook App Secret</param>
    /// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
    public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
        : base("facebook")
    {
        if (string.IsNullOrWhiteSpace(appId))
            throw new ArgumentNullException("appId");

        if (string.IsNullOrWhiteSpace(appSecret))
            throw new ArgumentNullException("appSecret");

        if (requestedScopes == null)
            throw new ArgumentNullException("requestedScopes");

        if (requestedScopes.Length == 0)
            throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");

        _appId = appId;
        _appSecret = appSecret;
        _requestedScopes = requestedScopes;
    }

    protected override Uri GetServiceLoginUrl(Uri returnUrl)
    {
        var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);

        return BuildUri(AuthorizationEndpoint, new NameValueCollection
                {
                    { "client_id", _appId },
                    { "scope", string.Join(" ", _requestedScopes) },
                    { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
                    { "state", state },
                });
    }

    protected override IDictionary<string, string> GetUserData(string accessToken)
    {
        var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });

        var webRequest = (HttpWebRequest)WebRequest.Create(uri);

        using (var webResponse = webRequest.GetResponse())
        using (var stream = webResponse.GetResponseStream())
        {
            if (stream == null)
                return null;

            using (var textReader = new StreamReader(stream))
            {
                var json = textReader.ReadToEnd();
                var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
                var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());

                data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));

                return data;
            }
        }
    }

    protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
    {
        var uri = BuildUri(TokenEndpoint, new NameValueCollection
                {
                    { "code", authorizationCode },
                    { "client_id", _appId },
                    { "client_secret", _appSecret },
                    { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
                });

        var webRequest = (HttpWebRequest)WebRequest.Create(uri);
        string accessToken = null;
        HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

        // handle response from FB 
        // this will not be a url with params like the first request to get the 'code'
        Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);

        using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
        {
            var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
            var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));

            Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
            accessToken = desirializedJsonObject["access_token"].ToString();
        }
        return accessToken;
    }

    private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
    {
        var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
        var qs = String.Join("&", keyValuePairs);

        var builder = new UriBuilder(baseUri) { Query = qs };
        return builder.Uri;
    }

    /// <summary>
    /// Facebook works best when return data be packed into a "state" parameter.
    /// This should be called before verifying the request, so that the url is rewritten to support this.
    /// </summary>
    public static void RewriteRequest()
    {
        var ctx = HttpContext.Current;

        var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
        if (stateString == null || !stateString.Contains("__provider__=facebook"))
            return;

        var q = HttpUtility.ParseQueryString(stateString);
        q.Add(ctx.Request.QueryString);
        q.Remove("state");

        ctx.RewritePath(ctx.Request.Path + "?" + q);
    }
}

Шаг 2. Добавьте одну ссылку на System.Web.Extensions

Шаг 3. В FacebookProviderAuthorizer.cs (проект Nopcommerce) найдите Свойство FacebookClient частное FacebookClient _facebookApplication;

Это должно относиться к вашему новому файлу, только что добавленному.

Шаг 4. Теперь установите точку останова в методе с именем VerifyAuthentication в файле FacebookProviderAuthorizer.cs .

AuthResult.IsSuccessful должен быть истинным, поскольку он успешно проанализировал токен.

Спасибо всем. Пожалуйста, как если бы решения работали для вас.

Согласно предыдущему ответу я получил свое решение. В MVC4 все записывают свои AppID а также SecurityCode, Из-за изменения API GRAPH на Facebook эти предыдущие ссылки не работают. Следовательно, каждый должен изменить RegisterFacebookClient учебный класс. Но этот класс является закрытым классом в библиотеке.Net, поэтому никто не может его расширить или перезаписать. В результате нам нужно использовать wrapper учебный класс. Я пишу этот ответ, потому что все предыдущие поставщики ответов упустили одну вещь, за которую я сильно пострадал. Потому что они не упомянули систему деформации класса в AuthConfig Учебный класс. Так что для решения этой проблемы требуется слишком много. Поэтому я иду шаг за шагом, как показано ниже. Давайте рассмотрим мой класс Wrapper FacebookClientV2Dot3 поэтому мой класс будет

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using DotNetOpenAuth.AspNet.Clients;
using Newtonsoft.Json;

public class FacebookClientV2Dot3 : OAuth2Client
{
    #region Constants and Fields

    /// <summary>
    /// The authorization endpoint.
    /// </summary>
    private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";

    /// <summary>
    /// The token endpoint.
    /// </summary>
    private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";

    /// <summary>
    /// The user info endpoint.
    /// </summary>
    private const string UserInfoEndpoint = "https://graph.facebook.com/me";

    /// <summary>
    /// The app id.
    /// </summary>
    private readonly string _appId;

    /// <summary>
    /// The app secret.
    /// </summary>
    private readonly string _appSecret;

    /// <summary>
    /// The requested scopes.
    /// </summary>
    private readonly string[] _requestedScopes;

    #endregion

    /// <summary>
    /// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
    /// </summary>
    /// <param name="appId">The Facebook App Id</param>
    /// <param name="appSecret">The Facebook App Secret</param>
    public FacebookClient(string appId, string appSecret)
        : this(appId, appSecret, new[] { "email" }) { }

    /// <summary>
    /// Creates a new Facebook OAuth2 client.
    /// </summary>
    /// <param name="appId">The Facebook App Id</param>
    /// <param name="appSecret">The Facebook App Secret</param>
    /// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
    public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
        : base("facebook")
    {
        if (string.IsNullOrWhiteSpace(appId))
            throw new ArgumentNullException("appId");

        if (string.IsNullOrWhiteSpace(appSecret))
            throw new ArgumentNullException("appSecret");

        if (requestedScopes == null)
            throw new ArgumentNullException("requestedScopes");

        if (requestedScopes.Length == 0)
            throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");

        _appId = appId;
        _appSecret = appSecret;
        _requestedScopes = requestedScopes;
    }

    protected override Uri GetServiceLoginUrl(Uri returnUrl)
    {
        var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);

        return BuildUri(AuthorizationEndpoint, new NameValueCollection
                {
                    { "client_id", _appId },
                    { "scope", string.Join(" ", _requestedScopes) },
                    { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
                    { "state", state },
                });
    }

    protected override IDictionary<string, string> GetUserData(string accessToken)
    {
        var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });

        var webRequest = (HttpWebRequest)WebRequest.Create(uri);

        using (var webResponse = webRequest.GetResponse())
        using (var stream = webResponse.GetResponseStream())
        {
            if (stream == null)
                return null;

            using (var textReader = new StreamReader(stream))
            {
                var json = textReader.ReadToEnd();
                var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
                var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());

                data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));

                return data;
            }
        }
    }

    protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
    {
        var uri = BuildUri(TokenEndpoint, new NameValueCollection
                {
                    { "code", authorizationCode },
                    { "client_id", _appId },
                    { "client_secret", _appSecret },
                    { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
                });

        var webRequest = (HttpWebRequest)WebRequest.Create(uri);
        string accessToken = null;
        HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

        // handle response from FB 
        // this will not be a url with params like the first request to get the 'code'
        Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);

        using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
        {
            var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
            var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));

            Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
            accessToken = desirializedJsonObject["access_token"].ToString();
        }
        return accessToken;
    }

    private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
    {
        var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
        var qs = String.Join("&", keyValuePairs);

        var builder = new UriBuilder(baseUri) { Query = qs };
        return builder.Uri;
    }

    /// <summary>
    /// Facebook works best when return data be packed into a "state" parameter.
    /// This should be called before verifying the request, so that the url is rewritten to support this.
    /// </summary>
    public static void RewriteRequest()
    {
        var ctx = HttpContext.Current;

        var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
        if (stateString == null || !stateString.Contains("__provider__=facebook"))
            return;

        var q = HttpUtility.ParseQueryString(stateString);
        q.Add(ctx.Request.QueryString);
        q.Remove("state");

        ctx.RewritePath(ctx.Request.Path + "?" + q);
    }
}

Посмотрите, здесь у меня все ссылки API заменены ссылками на новые версии.

Теперь вам нужно изменить ваш

AuthConfig

Просто используйте класс-оболочку вместо RegisterFacebookClient, Полностью заблокируйте эту часть кода. И добавить это...

OAuthWebSecurity.RegisterClient(new FacebookClientV2Dot3("AppID", "HassedPassword"));

Тогда все успехи. Ваш логин на Facebook вернется в прежнее состояние.

Однако вы можете столкнуться с новой проблемой, касающейся этого нового API, а не предыдущего API, проблема в том, что IP Whitelisting, Понравилось это изображение. Надеюсь, вам ничего не понадобится, кроме этого. Удачного кодирования.

Основываясь на сообщении Стива, я создал "FriendlyFacebookClient" для использования вместо FacebookClient, скопировал некоторые внутренние методы и заменил QueryAccessToken следующим:

  protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
        {
            UriBuilder builder = new UriBuilder("https://graph.facebook.com/oauth/access_token");
            AppendQueryArgs(builder, (IEnumerable<KeyValuePair<string, string>>)new Dictionary<string, string>()
              {
                { "client_id", this.appId},
                { "redirect_uri", FriendlyFacebookClient.NormalizeHexEncoding(returnUrl.AbsoluteUri)},
                { "client_secret",  this.appSecret },
                { "code",  authorizationCode },
                { "scope", "email" }
              });

            using (WebClient webClient = new WebClient())
            {
                var response = webClient.DownloadString(builder.Uri);
                var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
                return data["access_token"];
            }
        }

Я решу свою проблему и сделаю то же самое, что и @Adam, описанный в его ответе. Согласно ответам @Adam, @SteveTerry и @ Adeem, я изменяю свой код и создаю собственный класс FacebookClient с другим именем. и заменить оригинальной ссылкой FacebookClient в nopCommerce.

public class FacebookOAuth2Client : OAuth2Client
    {
        #region Constants and Fields

        /// <summary>
        /// The authorization endpoint.
        /// </summary>
        private const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";

        /// <summary>
        /// The token endpoint.
        /// </summary>
        private const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";

        /// <summary>
        /// The user info endpoint.
        /// </summary>
        private const string UserInfoEndpoint = "https://graph.facebook.com/me";

        /// <summary>
        /// The app id.
        /// </summary>
        private readonly string _appId;

        /// <summary>
        /// The app secret.
        /// </summary>
        private readonly string _appSecret;

        /// <summary>
        /// The requested scopes.
        /// </summary>
        private readonly string[] _requestedScopes;

        #endregion

        /// <summary>
        /// Creates a new Facebook OAuth2 client, requesting the default "email" scope.
        /// </summary>
        /// <param name="appId">The Facebook App Id</param>
        /// <param name="appSecret">The Facebook App Secret</param>
        public FacebookClient(string appId, string appSecret)
            : this(appId, appSecret, new[] { "email" }) { }

        /// <summary>
        /// Creates a new Facebook OAuth2 client.
        /// </summary>
        /// <param name="appId">The Facebook App Id</param>
        /// <param name="appSecret">The Facebook App Secret</param>
        /// <param name="requestedScopes">One or more requested scopes, passed without the base URI.</param>
        public FacebookClient(string appId, string appSecret, params string[] requestedScopes)
            : base("facebook")
        {
            if (string.IsNullOrWhiteSpace(appId))
                throw new ArgumentNullException("appId");

            if (string.IsNullOrWhiteSpace(appSecret))
                throw new ArgumentNullException("appSecret");

            if (requestedScopes == null)
                throw new ArgumentNullException("requestedScopes");

            if (requestedScopes.Length == 0)
                throw new ArgumentException("One or more scopes must be requested.", "requestedScopes");

            _appId = appId;
            _appSecret = appSecret;
            _requestedScopes = requestedScopes;
        }

        protected override Uri GetServiceLoginUrl(Uri returnUrl)
        {
            var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1);

            return BuildUri(AuthorizationEndpoint, new NameValueCollection
                {
                    { "client_id", _appId },
                    { "scope", string.Join(" ", _requestedScopes) },
                    { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
                    { "state", state },
                });
        }

        protected override IDictionary<string, string> GetUserData(string accessToken)
        {
            var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } });

            var webRequest = (HttpWebRequest)WebRequest.Create(uri);

            using (var webResponse = webRequest.GetResponse())
            using (var stream = webResponse.GetResponseStream())
            {
                if (stream == null)
                    return null;

                using (var textReader = new StreamReader(stream))
                {
                    var json = textReader.ReadToEnd();
                    var extraData = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
                    var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString());

                    data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"]));

                    return data;
                }
            }
        }

        protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
        {
            var uri = BuildUri(TokenEndpoint, new NameValueCollection
                {
                    { "code", authorizationCode },
                    { "client_id", _appId },
                    { "client_secret", _appSecret },
                    { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) },
                });

            var webRequest = (HttpWebRequest)WebRequest.Create(uri);
            string accessToken = null;            
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

            // handle response from FB 
            // this will not be a url with params like the first request to get the 'code'
            Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet);

            using (StreamReader sr = new StreamReader(response.GetResponseStream(), rEncoding))
            {
                var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
                var jsonObject = serializer.DeserializeObject(sr.ReadToEnd());
                var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject));

                Dictionary<string, object> desirializedJsonObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(jConvert.ToString());
                accessToken = desirializedJsonObject["access_token"].ToString();
            }
            return accessToken;
        }

        private static Uri BuildUri(string baseUri, NameValueCollection queryParameters)
        {
            var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k]));
            var qs = String.Join("&", keyValuePairs);

            var builder = new UriBuilder(baseUri) { Query = qs };
            return builder.Uri;
        }

        /// <summary>
        /// Facebook works best when return data be packed into a "state" parameter.
        /// This should be called before verifying the request, so that the url is rewritten to support this.
        /// </summary>
        public static void RewriteRequest()
        {
            var ctx = HttpContext.Current;

            var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]);
            if (stateString == null || !stateString.Contains("__provider__=facebook"))
                return;

            var q = HttpUtility.ParseQueryString(stateString);
            q.Add(ctx.Request.QueryString);
            q.Remove("state");

            ctx.RewritePath(ctx.Request.Path + "?" + q);
        }
    }

в этом коде у меня нет ссылок на JsonHelper, поэтому я использую простой jsonConvert. Еще раз спасибо @Adam, @SteveTerry и @ Adeem за помощь.

По предложению @SteveTerry Нам нужно обновить функцию QueryAccessToken в классе FacebookClient. к сожалению, "FacebookClient" является закрытым классом, поэтому мы не можем наследовать и переопределять. Поэтому какой бы путь вы ни выбрали, решать только вам. Вот как должен выглядеть конечный результат:

Старый код этой функции был:

protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) {
// Note: Facebook doesn't like us to url-encode the redirect_uri value
var builder = new UriBuilder(TokenEndpoint);
builder.AppendQueryArgs(
    new Dictionary<string, string> {
        { "client_id", this.appId },
        { "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
        { "client_secret", this.appSecret },
        { "code", authorizationCode },
        { "scope", "email" },
    });

using (webclient client = new webclient()) {
    string data = client.downloadstring(builder.uri);
    if (string.isnullorempty(data)) {
        return null;
    }

    var parsedquerystring = httputility.parsequerystring(data);
    return parsedquerystring["access_token"];
}

}

И для поддержки новой версии fb api это должно быть так:

/// <summary>
/// Contains access_token of a Facebook user.
/// </summary>
[DataContract]
[EditorBrowsable(EditorBrowsableState.Never)]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Facebook", Justification = "Brand name")]
public class FacebookAccessTokenData
{
    #region Public Properties

    /// <summary>
    /// 
    /// </summary>
    [DataMember(Name = "access_token")]
    public string AccessToken { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [DataMember(Name = "token_type")]
    public string TokenType { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [DataMember(Name = "expires_in")]
    public string ExpiresIn { get; set; }

    #endregion
}


/// <summary>
/// Obtains an access token given an authorization code and callback URL.
/// </summary>
/// <param name="returnUrl">
/// The return url.
/// </param>
/// <param name="authorizationCode">
/// The authorization code.
/// </param>
/// <returns>
/// The access token.
/// </returns>
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) {
    // Note: Facebook doesn't like us to url-encode the redirect_uri value
    var builder = new UriBuilder(TokenEndpoint);
    builder.AppendQueryArgs(
        new Dictionary<string, string> {
            { "client_id", this.appId },
            { "redirect_uri", NormalizeHexEncoding(returnUrl.AbsoluteUri) },
            { "client_secret", this.appSecret },
            { "code", authorizationCode },
            { "scope", "email" },
        });

    FacebookAccessTokenData graphData;
    var request = WebRequest.Create(builder.Uri);
    using (var response = request.GetResponse())
    {
        using (var responseStream = response.GetResponseStream())
        {
            graphData = JsonHelper.Deserialize<FacebookAccessTokenData>(responseStream);
        }
    }

    return graphData.AccessToken;
}
Другие вопросы по тегам