为啥新的 fb api 2.4 在具有身份和 oauth 2 的 MVC 5 上返回空电子邮件?

Posted

技术标签:

【中文标题】为啥新的 fb api 2.4 在具有身份和 oauth 2 的 MVC 5 上返回空电子邮件?【英文标题】:Why new fb api 2.4 returns null email on MVC 5 with Identity and oauth 2?为什么新的 fb api 2.4 在具有身份和 oauth 2 的 MVC 5 上返回空电子邮件? 【发布时间】:2015-11-10 14:54:26 【问题描述】:

在 fb 将它的 api 升级到 2.4 之前,一切都正常运行(我之前有 2.3项目)。

今天,当我在 fb 开发人员上添加一个新应用程序时,我使用 api 2.4 获得它。

问题:现在我收到来自 fb (loginInfo.email = null) 的空电子邮件。

当然,我检查了用户电子邮件在 fb 个人资料中处于公开状态,

我检查了loginInfo 对象,但没有找到任何其他电子邮件地址。

我用谷歌搜索,但没有找到任何答案。

请帮忙..我有点迷路了..

谢谢,

我的原始代码(适用于 2.3 api):

在 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
    

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

【问题讨论】:

我刚刚设置了一个新的 Facebook 应用程序 - 为什么它说 API 版本 2.11?! 2017 年?! 【参考方案1】:

取自Katana Thread 我设计了以下内容:

FacebookAuthenticationOptions 更改为包括BackchannelHttpHandlerUserInformationEndpoint,如下所示。确保包含实施所需和需要的字段名称。

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"

然后创建一个自定义FacebookBackChannelHandler,它将拦截对 Facebook 的请求并根据需要修复格式错误的 url。

更新:FacebookBackChannelHandler 基于 2017 年 3 月 27 日对 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; 

一个有用的补充是检查库的 3.0.1 版本,并在它发生变化时抛出异常。这样,您将知道在发布此问题的修复程序后是否有人升级或更新了 NuGet 包。

(更新为构建,在 C# 5 中工作,没有新的 nameof 功能)

【讨论】:

tnx,听起来很有趣。我将在下一个项目中检查它。 不客气。我还花了一整天的时间来解决这个问题。发现解决方案后不发布是不道德的。 仍然没有收到电子邮件。 只想补充一点,在 Facebook API 2.7 和 Microsoft.Owin.Security.Facebook 3.0.1 中仍然需要这个解决方案 更新后 3.1.0-rc 邮箱总是空的,这段代码帮我修好了。谢谢!【参考方案2】:

对我来说,这个问题通过升级到Microsoft.Owin.Security.Facebook 3.1.0 并将“电子邮件”添加到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);

【讨论】:

比其他一些答案更简单,并且如上所述。我很惊讶通常包含的名称必须被特别指出。 这似乎是最简单和最好的解决方案。请务必添加范围和名称字段。此外,在添加电子邮件字段之前,我还返回了名称字段。如果两者都需要,请务必添加它。 他们登录后如何获取字段值? Options.Fields... 返回错误“FacebookAuthenticationOptions”不包含字段的定义... @niico 获取要显示的字段的定义,将您的 Microsoft.Owin.Security.Facebook 引用更新到版本 3.1.0(您可能在 3.0.1 上)【参考方案3】:

要解决此问题,您需要从 NuGet 包安装 Facebook SDK。

在启动文件中

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

在控制器或助手中

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

有了这个,你可以得到 EmailId、First-last Name、Gender。

您还可以在该查询字符串中添加其他必需的属性。

希望这会对某人有所帮助。

【讨论】:

谢谢!这也解决了我的问题。如果您还想从动态 myInfo 中获取值,那么有一种简单的方法可以做到这一点:myInfo.email(而不是电子邮件,它可以是性别、名字等)【参考方案4】:

只是想在迈克的回答中添加这一行

facebookOptions.Scope.Add("email");

后面还需要添加

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"

如果您已经在没有“电子邮件许可”的情况下将您的 Facebook 帐户注册到您的开发网站。更改代码并重试后,您仍然不会收到电子邮件,因为您的开发网站未授予电子邮件权限。我这样做的方法是转到https://www.facebook.com/settings?tab=applications,删除我的 facebook 应用程序,然后再次重做该过程。

【讨论】:

是的,如上所述。这是完整的解决方案。【参考方案5】: 将 Microsoft.Owin 升级到 3.0.1(安装包 Microsoft.Owin.Security.OAuth) 在 Startup.Auth.cs 添加 facebookOptions.UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email";

【讨论】:

【参考方案6】:

阅读changelog,这是设计使然。您需要在响应中明确请求要重新调整的字段和边:

声明性字段 为了尝试提高移动网络的性能, v2.4 中的节点和边缘要求您明确请求 GET 请求所需的字段。比如GET /v2.4/me/feed默认不再包含likes和cmets,而是 GET /v2.4/me/feed?fields=comments,likes 将返回数据。更多 有关如何请求特定字段的详细信息,请参阅docs。

【讨论】:

是的,我知道,但不知道如何在C#代码上修改外部facebook oauth-2中的GET前缀url。 对我有用的唯一方法是按照以下帖子安装“Facebook SDK for .NET”:first post,second post。但是,我希望我能在不安装任何额外软件包的情况下找到解决方案。 @Dudi,Mike Trionfo 的回答效果很好。我只是对其进行了修改,以不修复特定的 API 版本(UserInformationEndpoint = "https://graph.facebook.com/me?fields=id,name,email")。

以上是关于为啥新的 fb api 2.4 在具有身份和 oauth 2 的 MVC 5 上返回空电子邮件?的主要内容,如果未能解决你的问题,请参考以下文章

在新的 FB API 上为非应用用户访问好友列表

无法在新的 FB 图 API (2.5) 上获得喜欢的类别

使用Ionic登录fb的Rails API

如何在 Android 上获取 FB Chat 的 API 密钥和会话密钥和会话密钥

以管理员身份使用自定义令牌向 FB DB 发出 REST 请求

我们如何在 Laravel 中实现自定义的仅 API 身份验证