ASP.NETMVC5owinFacebook身份验证突然失效怎么办?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (130)

/ oauth / access_token端点的2.3版现在返回JSON而不是表单编码的值

由于历史原因,这是我的原始问题/问题:

我有一个MVC5 Web应用程序,它使用内置的支持通过Facebook和Google进行身份验证。几个月前我们构建这个应用程序时,我们遵循本教程:http : //www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and -google-oauth2-and-openid-sign-on,一切都很好。

现在,突然间,Facebook身份验证刚刚停止工作。谷歌认证仍然很好。

问题描述:我们点击链接使用Facebook进行连接,我们被重定向到Facebook,如果我们不允许我们的Facebook应用程序访问我们的个人资料,我们会被提示。当我们点击“确定”后,我们被重定向回到我们的网站,但不是登录,而是直接登录屏幕。

我已经在调试模式下完成了这个过程,并且根据上面提到的教程,我的帐户控制器中有了这个ActionResult:

// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    if (loginInfo == null)
    {
        return RedirectToAction("Login");
    }
    ............

在单步执行代码并从Facebook返回时,loginInfo对象始终为NULL,这会导致用户被重定向回登录名。

为了理解幕后实际发生的事情,我安装了Fiddler并监控HTTP流量。我发现的是,在Facebook权限对话框中单击“确定”后,Facebook将通过以下URL重定向到我们的应用程序:

https://localhost/signin-facebook?code=<access-token>

这个URL不是一个实际的文件,可能是由我猜测的这个OWIN框架中内置的一些控制器/处理程序处理的。最有可能的是,它使用给定的代码连接到Facebook,以查询关于试图登录的用户的信息。现在,问题是不是这样做,我们被重定向到:

/Account/ExternalLoginCallback?error=access_denied

我敢肯定的是Facebook正在做的事情,也就是不是给我们用户数据,而是用这个错误信息重新引导我们。

这会导致AuthenticationManager.GetExternalLoginInfoAsync();失败并始终返回NULL。

我完全没有想法。据我们所知,我们没有改变任何事情。

我试过创建一个新的Facebook应用程序,我试过再次按照教程,但我总是有同样的问题。

我现在手动完成了执行身份验证所需的步骤,当我这样做时,一切都很好。为什么在使用MVC5 Owin的时候这不起作用?

这就是我所做的:

    // Step 1 - Pasted this into a browser, this returns a code
    https://www.facebook.com/dialog/oauth?response_type=code&client_id=619359858118523&redirect_uri=https%3A%2F%2Flocalhost%2Fsignin-facebook&scope=&state=u9R1m4iRI6Td4yACEgO99ETQw9NAos06bZWilJxJrXRn1rh4KEQhfuEVAq52UPnUif-lEHgayyWrsrdlW6t3ghLD8iFGX5S2iUBHotyTqCCQ9lx2Nl091pHPIw1N0JV23sc4wYfOs2YU5smyw9MGhcEuinvTAEql2QhBowR62FfU6PY4lA6m8pD3odI5MwBYOMor3eMLu2qnpEk0GekbtTVWgQnKnH6t1UcC6KcNXYY

I was redirected back to localhost (which I had shut down at this point to avoid being redirected immediately away).  The URL I was redirected to is this:

https://localhost/signin-facebook?code=<code-received-removed-for-obvious-reasons>

Now, I grabbed the code I got and used it in the URL below:

// Step 2 - opened this URL in a browser, and successfully retrieved an access token
https://graph.facebook.com/oauth/access_token?client_id=619359858118523&redirect_uri=https://localhost/signin-facebook&client_secret=<client-secret>&code=<code-from-step-1>

// Step 3 - Now I'm able to query the facebook graph using the access token from step 2!

https://graph.facebook.com/me?access_token=<access-token-from-step-2>

没有错误,一切都很好!那么为什么在使用MVC5 Owin的东西时这种方式不起作用?OWin实施显然有些问题。

提问于
用户回答回答于

昨天注意到这个问题。Facebook不再支持Microsoft.Owin.Security.Facebook版本3.0.1。对我来说,它工作安装版本3.1.0。要更新到3.1.0,请Install-Package Microsoft.Owin.Security.Facebook在软件包管理器控制台中运行该命令:https : //www.nuget.org/packages/Microsoft.Owin.Security.Facebook

用户回答回答于

Microsoft.Owin。*软件包的3.1.0版现已发布。如果在Facebook的API更改后遇到问题,请先尝试更新的NuGet包。在我的情况下,他们解决了这个问题(在我们的生产系统上正常工作)。

事实证明,Facebook将他们的图形API从2.2版本强制升级到2.3版本。这些版本的API之间的差异之一似乎是Facebook端点/oauth/access_token不再以格式编码内容主体,但用JSON代替。

现在,在Owin中间件中,我们找到了protected override FacebookAuthenticationHandler.AuthenticateCoreAsync()将响应主体解析为表单并随后使用access_token解析表单的方法。不用说,解析后的表单是空的,所以access_token它也是空的,导致access_denied链中更多的错误。

为了快速解决这个问题,我们为Facebook Oauth响应创建了一个包装类

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

然后,在OwinStart中,我们添加了一个自定义的后通道处理程序...

        app.UseFacebookAuthentication(new FacebookAuthenticationOptions
        {
            AppId = "hidden",
            AppSecret = "hidden",
            BackchannelHttpHandler = new FacebookBackChannelHandler()
        });

.处理程序定义为:

public class FacebookBackChannelHandler : HttpClientHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var result = await base.SendAsync(request, cancellationToken);
        if (!request.RequestUri.AbsolutePath.Contains("access_token"))
            return result;

        // For the access token we need to now deal with the fact that the response is now in JSON format, not form values. Owin looks for form values.
        var content = await result.Content.ReadAsStringAsync();
        var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);

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

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

        return modifiedResult;
    }
}

基本上,处理程序只需创建一个新的HttpResponseMessage,其中包含来自Facebook JSON响应的等效表单编码信息。请注意,此代码使用流行的Json.Net软件包。

有了这个自定义处理程序,问题似乎得到解决(虽然我们还没有部署到prod )。

希望能拯救别人今天醒来时遇到类似问题!

扫码关注云+社区

领取腾讯云代金券