Why new fb api 2.4 returns null email on MVC 5 with Identity and oauth 2?

Taken from a Katana Thread I devised the following:

Change the FacebookAuthenticationOptions to include BackchannelHttpHandler and UserInformationEndpoint as seen below. Make sure to include the names of the fields you want and need for your implementation.

var facebookOptions = new FacebookAuthenticationOptions()
{
    AppId = "*",
    AppSecret = "*",
    BackchannelHttpHandler = new FacebookBackChannelHandler(),
    UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name"
}

Then create a custom FacebookBackChannelHandler that will intercept the requests to Facebook and fix the malformed url as needed.

UPDATE: The FacebookBackChannelHandler is updated based on a 27 Mar 2017 update to the FB api.

public class FacebookBackChannelHandler : HttpClientHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
        {
            request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
        }

        var result = await base.SendAsync(request, cancellationToken);
        if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
        {
            return result;
        }

        var content = await result.Content.ReadAsStringAsync();
        var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);

        var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty);
        outgoingQueryString.Add("access_token", facebookOauthResponse.access_token);
        outgoingQueryString.Add("expires_in", facebookOauthResponse.expires_in + string.Empty);
        outgoingQueryString.Add("token_type", facebookOauthResponse.token_type);
        var postdata = outgoingQueryString.ToString();

        var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(postdata)
        };

        return modifiedResult;
    }
}

public class FacebookOauthResponse
{
    public string access_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
}

One useful addition would be to check for the version 3.0.1 of the library and throw an exception if and when it changes. That way you'll know if someone upgrades or updates the NuGet package after a fix for this problem has been released.

(Updated to build, work in C# 5 without new nameof feature)


For me this Issue was solved by upgrading to Microsoft.Owin.Security.Facebook 3.1.0 and adding 'email' to the Fields collection:

var options = new FacebookAuthenticationOptions
{
    AppId = "-------",
    AppSecret = "------",    
};
options.Scope.Add("public_profile");
options.Scope.Add("email");

//add this for facebook to actually return the email and name
options.Fields.Add("email");
options.Fields.Add("name");

app.UseFacebookAuthentication(options);

To resolve this you need to install Facebook SDK from NuGet packages.

In StartUp File

 app.UseFacebookAuthentication(new FacebookAuthenticationOptions
            {
                AppId = "XXXXXXXXXX",
                AppSecret = "XXXXXXXXXX",
                Scope = { "email" },
                Provider = new FacebookAuthenticationProvider
                {
                    OnAuthenticated = context =>
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
                        return Task.FromResult(true);
                    }
                }
            });

In Controller or Helper

var identity = AuthenticationManager.GetExternalIdentity(DefaultAuthenticationTypes.ExternalCookie);
var accessToken = identity.FindFirstValue("FacebookAccessToken");
var fb = new FacebookClient(accessToken);
dynamic myInfo = fb.Get("/me?fields=email,first_name,last_name,gender"); // specify the email field

With this you can get the EmailId,First-last Name, Gender.

You can also add your additional required properties in that query string.

Hope this will help someone.