Why does the new fb api 2.4 return null email address on MVC 5 with Identity and oauth 2?

Everything worked perfectly until fb updated it to 2.4 (I had 2.3 in my previous project).

Today, when I add a new application to fb developers, I get it with api 2.4.

Problem: now I get an empty email from fb ( loginInfo.email = null ).

Of course, I checked that the user's email is in the open state in the fb profile,

and I looked at the loginInfo object but could not find another email address.

and i google but did not find the answer.

please help .. I kind of lost ..

Thank,

My source code (which worked on 2.3 api):

In AccountController.cs:

 // // GET: /Account/ExternalLoginCallback [AllowAnonymous] public async Task<ActionResult> ExternalLoginCallback(string returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } //A way to get fb details about the log-in user: //var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:first_name"); <--worked only on 2.3 //var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:name"); <--works on 2.4 api // Sign in the user with this external login provider if the user already has a login var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false); switch (result) { case SignInStatus.Success: return RedirectToLocal(returnUrl); case SignInStatus.LockedOut: return View("Lockout"); case SignInStatus.RequiresVerification: return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false }); case SignInStatus.Failure: default: // If the user does not have an account, then prompt the user to create an account ViewBag.ReturnUrl = returnUrl; ViewBag.LoginProvider = loginInfo.Login.LoginProvider; return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email }); //<---DOESN'T WORK. loginInfo.Email IS NULL } } 

In Startup.Auth.cs:

  Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions fbOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions() { AppId = System.Configuration.ConfigurationManager.AppSettings.Get("FacebookAppId"), AppSecret = System.Configuration.ConfigurationManager.AppSettings.Get("FacebookAppSecret"), }; fbOptions.Scope.Add("email"); fbOptions.Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider() { OnAuthenticated = (context) => { context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken)); foreach (var claim in context.User) { var claimType = string.Format("urn:facebook:{0}", claim.Key); string claimValue = claim.Value.ToString(); if (!context.Identity.HasClaim(claimType, claimValue)) context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook")); } return System.Threading.Tasks.Task.FromResult(0); } }; fbOptions.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie; app.UseFacebookAuthentication(fbOptions); 
+43
c # facebook asp.net-mvc-5 facebook-graph-api asp.net-identity-2
Aug 17 '15 at 20:45
source share
6 answers

Taken from Katana Thread I came up with the following:

Modify FacebookAuthenticationOptions to include BackchannelHttpHandler and UserInformationEndpoint , as shown below. Be sure to include the names of the fields you want and their need.

 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 requests on Facebook and correct the invalid URL as needed.

UPDATE: FacebookBackChannelHandler updated based on the March 27, 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 library version 3.0.1 and throw an exception if and when it changes. This way you will find out if someone is updating or updating the NuGet package after fixing the fix for this problem.

(Updated for build, working in C # 5 without a new nameof function)

+80
Sep 17 '15 at 17:09
source share

To solve this problem, you need to install the 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 the controller or assistant

 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 EmailId, first-person name, gender.

You can also add additional required properties to this query string.

Hope this helps someone.

+19
Nov 23 '15 at 10:19
source share

For me, this problem was resolved by updating 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); 
+18
May 7 '17 at 16:47
source share

I just want to add to Mike the answer that this line

 facebookOptions.Scope.Add("email"); 

still need to add after

 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,location" } 

And if you already registered your facebook account on your dev website without "email permission". After changing the code and retrying, you still won’t get the email address because email permission is not granted to your dev website. The way I do is https://www.facebook.com/settings?tab=applications , uninstall my facebook application and execute the process again.

+12
Dec 04 '15 at 3:34
source share
+3
Aug 25 '16 at 13:37
source share

Read changelog , this is by design. You need to explicitly request the fields and edges that you want to reconfigure in the response:

Declarative fields In order to try to improve performance on mobile networks, Nodes and Edges in version 2.4 require that you explicitly request the field (s) that is needed for your GET requests. For example, GET /v2.4/me/feed no longer includes preferences and comments by default, but GET /v2.4/me/feed?fields=comments,likes will return data. For more details, see the docs on how to request specific fields.

+1
Aug 21 '15 at 12:51 on
source share



All Articles