facebook-graph-api - Identity Server 3 Facebook 登录 获取电子邮件

标签 facebook-graph-api oauth-2.0 facebook-login identityserver3 katana

身份服务器已实现且运行良好。 Google 登录正在运行,并且正在返回包括电子邮件在内的多项声明。

Facebook 登录正常,我的应用程序已上线,并在新用户登录时请求电子邮件权限。

问题是我无法从 oauth 端点取回电子邮件,而且我似乎找不到 access_token 来手动请求用户信息。我所拥有的只是从 facebook 登录端点返回的“代码”。

这是 IdentityServer 设置。

var fb = new FacebookAuthenticationOptions
            {
                AuthenticationType = "Facebook",
                SignInAsAuthenticationType = signInAsType,
                AppId = ConfigurationManager.AppSettings["Facebook:AppId"],
                AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"]
            };

            fb.Scope.Add("email");

            app.UseFacebookAuthentication(fb);

当然,我已经自定义了 AuthenticateLocalAsync 方法,但我收到的声明仅包含名称。没有电子邮件 claim 。

深入研究身份服务器的源代码,我意识到发生了一些声明事情来转换 facebook 声明,因此我扩展了该类来调试它,看看它是否剥离了任何声明,但事实并非如此。

我还用 fiddler 观看了 http 调用,我只看到以下内容(抱歉,代码格式在 url 上效果不佳。我尝试将查询字符串参数格式化为自己的行,但没有成功)

  1. (facebook.com)

    /dialog/oauth ?response_type=代码 &client_id=xxx &redirect_uri=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook &范围=电子邮件 &state=xxx

  2. (facebook.com)

    /登录.php ?skip_api_login=1 &api_key=xxx &signed_next=1 &next=https%3A%2F%2Fwww.facebook.com%2Fv2.7%2Fdialog%2Foauth%3Fredirect_uri%3Dhttps%253A%252F%252Fidentity.[site].com%252Fid%252Fsignin-facebook%26state%3Dxxx%26scope% 3Demail%26response_type%3Dcode%26client_id%3Dxxx%26ret%3Dlogin%26logger_id%3Dxxx&cancel_url=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions% 2Berror%26error_reason%3Duser_denied%26state%3Dxxx%23_%3D_ &显示=页面 &locale=en_US &logger_id=xxx

  3. (facebook.com)

    POST/cookie/consent/?pv=1&dpr=1 HTTP/1.1

  4. (facebook.com)

    /登录.php ?login_attempt=1 &next=https%3A%2F%2Fwww.facebook.com%2Fv2.7%2Fdialog%2Foauth%3Fredirect_uri%3Dhttps%253A%252F%252Fidentity.[site].com%252Fid%252Fsignin-facebook%26state%3Dxxx%26scope% 3Demail%26response_type%3Dcode%26client_id%3Dxxx%26ret%3Dlogin%26logger_id%3Dxxx &lwv=100

  5. (facebook.com)

    /v2.7/dialog/oauth ?redirect_uri=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook &状态=xxx &范围=电子邮件 &response_type=代码 &client_id=xxx &ret=登录 &logger_id=xxx &hash=xxx

  6. (身份服务器)

    /id/登录-facebook ?代码=xxx &state=xxx

我在最后一次调用中看到了 code 参数,并认为也许我可以使用那里的代码从 facebook API https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow 获取 access_token

但是,当我尝试时,我从 API 收到一条消息,告诉我该代码已被使用。

我还尝试将 UserInformationEndpoint 更改为 FacebookAuthenticationOptions,通过将 ?fields=email 附加到默认端点位置的末尾来强制它请求电子邮件,但这会导致身份服务器吐出错误“There was an登录外部提供商时出错。错误消息为:access_denied”。

如果我可以更改中间件以使用response_type=id_token发送请求,我也许能够解决这一切,但我不知道如何做到这一点,也不知道如何在第一次返回时提取该访问 token 能够使用 Facebook C# sdk 的地方。

所以我想任何帮助或指导都会很棒。我花了无数的时间研究并试图解决这个问题。我需要做的就是通过 IdentityServer3 获取登录用户的电子邮件地址。听起来并不难,但我却卡住了。

最佳答案

我终于明白了这一点。答案与 Mitra 的评论有关,尽管这两个答案似乎都不符合要求,所以我在这里放了另一个答案。首先,您需要从 Facebook 的身份验证端点请求 access_token,而不是代码(授权代码)。为此,请像这样设置

        var fb = new FacebookAuthenticationOptions
        {
            AuthenticationType = "Facebook",
            SignInAsAuthenticationType = signInAsType,
            AppId = ConfigurationManager.AppSettings["Facebook:AppId"],
            AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"],
            Provider = new FacebookAuthenticationProvider()
            {
                OnAuthenticated = (context) =>
                {
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook"));
                    return Task.FromResult(0);
                }
            }

        };

        fb.Scope.Add("email");

        app.UseFacebookAuthentication(fb);

然后,您需要在登录后捕获响应。我正在使用以下 file来自IdentityServer3 Samples Repository ,它覆盖(读取,提供功能)从外部站点登录用户所需的方法。从这个回复中,我使用 C# Facebook SDK使用ExternalAuthenticationContext中新返回的access_token声明来请求我需要的字段并将它们添加到声明列表中。然后我可以使用该信息来创建/登录用户。

    public override async Task AuthenticateExternalAsync(ExternalAuthenticationContext ctx)
    {
        var externalUser = ctx.ExternalIdentity;
        var claimsList = ctx.ExternalIdentity.Claims.ToList();
        if (externalUser.Provider == "Facebook")
        {
            var extraClaims = GetAdditionalFacebookClaims(externalUser.Claims.First(claim => claim.Type == "urn:facebook:access_token"));
            claimsList.Add(new Claim("email", extraClaims.First(k => k.Key == "email").Value.ToString()));
            claimsList.Add(new Claim("given_name", extraClaims.First(k => k.Key == "first_name").Value.ToString()));
            claimsList.Add(new Claim("family_name", extraClaims.First(k => k.Key == "last_name").Value.ToString()));
        }

        if (externalUser == null)
        {
            throw new ArgumentNullException("externalUser");
        }

        var user = await userManager.FindAsync(new Microsoft.AspNet.Identity.UserLoginInfo(externalUser.Provider, externalUser.ProviderId));
        if (user == null)
        {
            ctx.AuthenticateResult = await ProcessNewExternalAccountAsync(externalUser.Provider, externalUser.ProviderId, claimsList);
        }
        else
        {
            ctx.AuthenticateResult = await ProcessExistingExternalAccountAsync(user.Id, externalUser.Provider, externalUser.ProviderId, claimsList);
        }
    }

就是这样!如果您对简化此过程有任何建议,请告诉我。我打算修改此代码以执行从 FacebookAuthenticationOptions 对 API 的调用,但 Events 属性显然不再存在。

编辑:GetAdditionalFacebookClaims 方法只是一种根据已提取的访问 token 创建新 FacebookClient 的方法,并在 Facebook API 中查询您需要的其他用户声明。例如,我的方法如下所示:

protected static JsonObject GetAdditionalFacebookClaims(Claim accessToken)
    {
        var fb = new FacebookClient(accessToken.Value);
        return fb.Get("me", new {fields = new[] {"email", "first_name", "last_name"}}) as JsonObject;
    }

关于facebook-graph-api - Identity Server 3 Facebook 登录 获取电子邮件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39172101/

相关文章:

python - 使用 google-api-python-client 授予 GAE 应用对 Google API 的访问权限

go - Gin Sessions 将状态和代码存储在 URL 中,我想更改它以使我的 URL 更清晰

ios - 如何在登录用户通过 Facebook 在 firebase for ios swift 登录后进行检查?

Facebook 触摸模式登录 "500 internal server error"

ios - Xcode 9.3 的 Facebook SDK 访问 token 问题

ios - 从 Facebook graph api 获取用户分享的视频数据

mysql - 存储 Facebook Access Token 的最佳字段类型

Facebook Messenger 机器人验证

java - 使用 RestFB 仅获取 Facebook 的最新帖子

python - Django AllAuth 给出 SSLError