c# - Por que o novo fb api 2.4 retorna um e-mail nulo no MVC 5 com Identity e oauth 2?




facebook facebook-graph-api (4)

Leia o changelog , isso é por design. Você precisa solicitar explicitamente os campos e as bordas que deseja reajustar na resposta:

Campos Declarativos Para tentar melhorar o desempenho em redes móveis, os Nós e Arestas na v2.4 exigem que você solicite explicitamente o (s) campo (s) necessários para suas solicitações GET . Por exemplo, GET /v2.4/me/feed não inclui mais curtidas e comentários por padrão, mas GET /v2.4/me/feed?fields=comments,likes , GET /v2.4/me/feed?fields=comments,likes retornará os dados. Para mais detalhes, consulte os docs sobre como solicitar campos específicos.

Tudo funcionou perfeitamente até que fb atualizou sua API para 2.4 (eu tinha 2.3 no meu projeto anterior).

Hoje, quando eu adiciono uma nova aplicação em desenvolvedores fb, eu a obtenho com a API 2.4.

O problema: Agora eu recebo e-mail nulo de fb ( loginInfo.email = null ).

É claro que verifiquei se o email do usuário está em status público no perfil do fb,

e eu fui até o objeto loginInfo mas não encontrei nenhum outro endereço de email.

e eu pesquisei isso, mas não encontrei nenhuma resposta.

por favor qualquer ajuda .. Eu meio que perdi ..

Obrigado,

Meu código original (que trabalhou em 2.3 api):

No 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
    }
}

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

Para mim, este problema foi resolvido atualizando para o Microsoft.Owin.Security.Facebook 3.1.0 e adicionando 'email' à coleção Fields :

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

Retirado de um Katana Thread eu inventei o seguinte:

Altere as FacebookAuthenticationOptions para incluir BackchannelHttpHandler e UserInformationEndpoint como BackchannelHttpHandler abaixo. Certifique-se de incluir os nomes dos campos que deseja e precisa para sua implementação.

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

Em seguida, crie um FacebookBackChannelHandler personalizado que interceptará as solicitações para o Facebook e corrija o URL malformado, conforme necessário.

ATUALIZAÇÃO: O FacebookBackChannelHandler é atualizado com base em uma atualização de 27 de março de 2017 para a API do FB.

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

Uma adição útil seria verificar a versão 3.0.1 da biblioteca e lançar uma exceção se e quando ela for alterada. Dessa forma, você saberá se alguém atualizar ou atualizar o pacote NuGet depois que uma correção para esse problema for liberada.

(Atualizado para construir, trabalhar em C # 5 sem novo nome de recurso)


Só quero adicionar na resposta de Mike que esta linha

facebookOptions.Scope.Add("email");

ainda precisa ser adicionado depois

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

E se você já registrou sua conta do facebook no seu site de desenvolvimento, sem "permissão de e-mail". Depois de alterar o código e tentar novamente, você ainda não receberá o e-mail porque a permissão de e-mail não é concedida ao seu site de desenvolvimento. A maneira que eu faço é ir para https://www.facebook.com/settings?tab=applications , remover meu aplicativo do Facebook e refazer o processo novamente.





asp.net-identity-2