OAuthWebSecurity with Facebook not using email permission as expected

asp.net-mvc, c#, facebook, oauth

Solution

Firstly, the extraData parameter is not passed to facebook. It is for internal use only. See the following link on how this data can be used on your site:

http://blogs.msdn.com/b/pranav_rastogi/archive/2012/08/24/customizing-the-login-ui-when-using-oauth-openid.aspx

Now, to the meat:

In addition to the methods `RegisterFacebookClient`, `RegisterYahooClient` etc. in `OAuthWebSecurity`, there is also a generic method `RegisterClient`. This is the method we will be using for this solution.

This idea germinates from the code provided at: http://mvc4beginner.com/Sample-Code/Facebook-Twitter/MVC-4-oAuth-Facebook-Login-EMail-Problem-Solved.html

However, we will not be using the hacky approach provided by the solution. Instead, we will create a new class called `FacebookScopedClient` which will implement `IAuthenticationClient`. Then we will simply register the class using:

OAuthWebSecurity.RegisterClient(new FacebookScopedClient("your_app_id", "your_app_secret"), "Facebook", null);

in AuthConfig.cs

The code for the class is:

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

    public class FacebookScopedClient : IAuthenticationClient
        {
            private string appId;
            private string appSecret;

            private const string baseUrl = "https://www.facebook.com/dialog/oauth?client_id=";
            public const string graphApiToken = "https://graph.facebook.com/oauth/access_token?";
            public const string graphApiMe = "https://graph.facebook.com/me?";


            private static string GetHTML(string URL)
            {
                string connectionString = URL;

                try
                {
                    System.Net.HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(connectionString);
                    myRequest.Credentials = CredentialCache.DefaultCredentials;
                    //// Get the response
                    WebResponse webResponse = myRequest.GetResponse();
                    Stream respStream = webResponse.GetResponseStream();
                    ////
                    StreamReader ioStream = new StreamReader(respStream);
                    string pageContent = ioStream.ReadToEnd();
                    //// Close streams
                    ioStream.Close();
                    respStream.Close();
                    return pageContent;
                }
                catch (Exception)
                {
                }
                return null;
            }

            private  IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
            {

                string token = GetHTML(graphApiToken + "client_id=" + appId + "&redirect_uri=" + HttpUtility.UrlEncode(redirectURI) + "&client_secret=" + appSecret + "&code=" + accessCode);
                if (token == null || token == "")
                {
                    return null;
                }
                string data = GetHTML(graphApiMe + "fields=id,name,email,gender,link&access_token=" + token.Substring("access_token=", "&"));

                // this dictionary must contains
                Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
                return userData;
            }

            public FacebookScopedClient(string appId, string appSecret)
            {
                this.appId = appId;
                this.appSecret = appSecret;
            }

            public string ProviderName
            {
                get { return "Facebook"; }
            }

            public void RequestAuthentication(System.Web.HttpContextBase context, Uri returnUrl)
            {
                string url = baseUrl + appId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=email";
                context.Response.Redirect(url);
            }

            public AuthenticationResult VerifyAuthentication(System.Web.HttpContextBase context)
            {
                string code = context.Request.QueryString["code"];

                string rawUrl = context.Request.Url.OriginalString;
                //From this we need to remove code portion
                rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");

                IDictionary<string, string> userData = GetUserData(code, rawUrl);

                if (userData == null)
                    return new AuthenticationResult(false, ProviderName, null, null, null);

                string id = userData["id"];
                string username = userData["email"];
                userData.Remove("id");
                userData.Remove("email");

                AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
                return result;
            }
        }

now in the

public ActionResult ExternalLoginCallback(string returnUrl)

method in `AccountController`, `result.ExtraData` should have the email.

Edit: I missed some code in this post. I am adding it below:

public static class String
    {
        public static string Substring(this string str, string StartString, string EndString)
        {
            if (str.Contains(StartString))
            {
                int iStart = str.IndexOf(StartString) + StartString.Length;
                int iEnd = str.IndexOf(EndString, iStart);
                return str.Substring(iStart, (iEnd - iStart));
            }
            return null;
        }
    }

Cheers!

Problem

Using the new OAuthWebSecurity for authenticating with Facebook, I added the email permission on my Facebook application. Now, as I can read, I need to define a scope to be able to actually get the email in the result. So far without the scope I'm not getting the users' email and am not sure why as I can not see where to define the "scope". It's just a rip of the ASP.NET MVC 4 default authenticationcontrollers external login.

Original source

Related problems