Search code examples
facebookasp.net-mvc-4facebook-graph-apifacebook-appsfacebook-authentication

External login is not working for facebook in MVC 4 webapplication


I have noticed that after expiration of Facebook GRAPH API on March 27, 2017 my Facebook application is not providing the access to my web application. Mainly OAuthWebSecurity is failed to fetch the login state from the facebook graph API, I have gone through the bug report in the developers group. But they didn't provide a solution. I got a solution which is provided for NopCommerce in this link. However I didn't get any perfect solution for MVC 4.

I have gone through this link where they system is approached in MVC 5 however I need the solution in MVC4 And mention that OWIN is not supported for MVC4 in VS 2012 So how do I solve this problem. I have been undergoing this problem since last 1 week.

Mainly the exception is happening in this piece of code

AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

It's returning IsSuccessful as false and rest of the parameters are null as well.

I have studied that Facebook have changed the the return type of the API string to JSON So how to develop my code to get those JSON exactly. thank you.

Update: This Question is still unanswered. Anyone here to help me.


Solution

  • According to Previous Answer I got to give you a solution. In MVC4 everyone write down their AppID and SecurityCode. Due to change of facebook GRAPH API those previous links are broken. Consequently everyone need to change the RegisterFacebookClient calss. But this class is a sealed class in the .Net library, so anyone can't extend or overwrite it. As a result we need to use a wrapper class. Let us consider my Wrapper class is FacebookClientV2Dot3 therefore my class will be

    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);
        }
    }
    

    Look here you I have replaces all the API links by newer version links.

    Now you need to modify your

    AuthConfig

    Just use a wrapper class

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

    Then all success. You facebook login will be back in previous state.

    However you can face a new issue regarding this new API rather than previous API, the problem is that IP Whitelisting. Like this image. Hope you will need nothing but this. Happy coding.