【问题标题】:ASP.NET MVC5 OWIN Facebook authentication suddenly not workingASP.NET MVC5 OWIN Facebook 身份验证突然不起作用
【发布时间】:2014-04-17 08:24:22
【问题描述】:

2017 年更新!

我发布原始问题时遇到的问题与 Facebook 最近在强制所有人使用其 API 2.3 版时所做的更改无关。有关该特定问题的解决方案,请参阅 sammy34 的回答如下。 /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 身份验证刚刚停止工作。 Google 身份验证仍然很有效。

问题描述:我们单击链接以使用 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 的实现显然有问题。

【问题讨论】:

  • 我正在处理同样的问题 - 昨天开始发生。我有一个使用此处描述的过程的 MVC5 应用程序:stackoverflow.com/questions/18942196/… 和此处:stackoverflow.com/questions/20928939/…。在我的本地开发环境中运行良好,在 azure 平台上出现交替错误 access_denied 和 FB 错误“应用程序配置不允许给定 URL。:应用程序的设置不允许一个或多个给定 URL...”
  • 好,我不是唯一一个 :-) 如果我找到解决此问题的方法,我一定会通知您!
  • 我也遇到了同样的问题,将example.com/signin-facebook 设置为 facebook 上的重定向 url 解决了这个问题
  • 按照您的 URL 测试方法,我注意到我的应用程序密码已更改!干杯。

标签: facebook asp.net-mvc-5 owin


【解决方案1】:

2017 年 4 月 22 日更新:Microsoft.Owin.* 软件包的 3.1.0 版现已推出。如果在 2017 年 3 月 27 日 Facebook 的 API 更改后遇到问题,请先尝试更新的 NuGet 包。就我而言,他们解决了问题(在我们的生产系统上运行良好)。

原答案:

就我而言,我在 2017 年 3 月 28 日醒来时发现我们应用的 Facebook 身份验证突然停止工作。我们没有更改应用代码中的任何内容。

事实证明,Facebook 于 2017 年 3 月 27 日将其图形 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 :))。

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

另外,如果有人对此有更清洁的解决方案,我很想知道!

【讨论】:

【解决方案2】:

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

【讨论】:

【解决方案3】:

好的,我已经找到了解决问题的方法。

这是我之前在 Startup.Auth.cs 文件中的代码:

var x = new FacebookAuthenticationOptions();
            //x.Scope.Add("email");
            x.AppId = "1442725269277224";
            x.AppSecret = "<secret>";
            x.Provider = new FacebookAuthenticationProvider()
            {
                OnAuthenticated = async context =>
                {
                        //Get the access token from FB and store it in the database and
                    //use FacebookC# SDK to get more information about the user
                    context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken",context.AccessToken));
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:name", context.Name));
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email));
                }
            };
            x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
            app.UseFacebookAuthentication(x);

注意

x.Scope.Add("email")

行已被注释掉,但我稍后仍在 OnAuthenticated 处理程序中查询电子邮件?是的,没错。出于某种原因,这在几周内完美运行。

我的解决方案是简单地取消注释 x.Scope.Add("email");行以确保在对 Facebook 的初始请求中存在 scope=email 变量。

现在一切正常!

我不明白为什么以前会这样。我能想出的唯一解释是 Facebook 改变了一些东西。

【讨论】:

  • 这段代码有错误,上面的代码缺少等待操作符
  • 谢谢!奇怪的是,我需要进行更改才能让 FB 登录从 Azure 托管的应用程序中工作(以及在我的 Facebook 应用程序设置中为 Valid OAuth redirect URIs 设置一些值),而在我的开发环境中,如果没有这些修改,一切都可以正常工作。
  • @HaukurHaf,现在这有可能再次失败吗?无论我做什么,我都无法获得context.Email,因为它始终为空(登录有效,但上下文只有 id 和用户名)
  • 我将Owin升级到3.0.1-RC1并在Startup.Auth中设置了FacebookAuthenticationOptions { UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name";我已获得授权并收到电子邮件 :) 不需要额外的 BackchannelHttpHandler
  • @zed - 这几乎对我有用。我必须为context.Email != null 添加一个检查,如果是则忽略它。
【解决方案4】:

我在使用 Google 身份验证时遇到了同样的问题。以下对我有用:Changes to Google OAuth 2.0 and updates in Google middleware for 3.0.0 RC release

【讨论】:

  • 非常感谢,工作就像一个魅力。要为其他人总结,请访问 console.developer.google.com 并选择您的应用程序,然后转到 APIs & Auth > APIs > 并启用“Google+ API”。如果您在 2014 年 9 月 1 日之前配置该应用程序,那么应用程序将被禁用,这就是问题所在。
【解决方案5】:

上一次 Facebook 升级是在 2015-02-09 (https://www.nuget.org/packages/Microsoft.AspNet.WebPages.OAuth/)

当时 API 的最新版本是 2.2 版。 2.2 版于 2017 年 3 月 25 日到期,恰好是问题开始的时候。 (https://developers.facebook.com/docs/apps/changelog)

我猜 Facebook 可能会自动升级 API,现在 MS OAUTH 库无法解析新的响应。

tldr:Microsoft WebPages OAuth 库已过时(至少对于 FB 而言),您可能需要寻找其他解决方案

【讨论】:

  • 你应该切换到Microsoft.Owin.Security.Facebook 3.1.0-RC1。我还使用了 DonNetOpenAuth 库,直到 Facebook 强制我们至少切换到图形 API v2.3。所以我们将我们的解决方案从 MVC 4 升级到 MVC 5 并切换到 OWIN 和它的实现。祝你好运
【解决方案6】:

上述解决方案对我不起作用。最后,它似乎与 Session 有关。通过在上一次调用中“唤醒”会话,它将不再从 GetExternalLoginInfoAsync() 中返回 null

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult ExternalLogin(string provider, string returnUrl)
    {
        Session["WAKEUP"] = "NOW!";
        // Request a redirect to the external login provider
        return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
    }

像 OP 一样,我让第 3 方身份验证工作了很长时间,然后突然停止了。我相信这是由于我在设置 Session 以在 Azure 上使用 Redis 缓存时对代码所做的更改。

【讨论】:

    【解决方案7】:

    我也有这个问题,但不是由范围设置引起的。我花了很长时间才弄清楚这一点,但最终让我明白的是通过在OwinStartup.Configuration(IAppBuilder app) 中设置以下内容来设置自定义记录器。

    app.SetLoggerFactory(new LoggerFactory()); 
    // Note: LoggerFactory is my own custom ILoggerFactory
    

    这输出了以下内容:

    2014-05-31 21:14:48,508 [8] 错误
    Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
    [(null)] - 0x00000000 - 身份验证失败
    System.Net.Http.HttpRequestException:发送时出错 请求。 ---> System.Net.WebException:远程名称无法
    解决:'graph.facebook.com' at
    System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
    在 System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) --- 内部异常堆栈跟踪结束 --- at
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务
    任务)在
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 在
    Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext()

    基于上述调用堆栈,我发现我的 Azure VM 无法解析 graph.facebook.com。我所要做的就是运行“ipconfig /registerdns”来解决这个问题,我已经完全解决了......

    【讨论】:

    • 你为什么不插入你的 LoggerFactry 来帮助别人?
    • @MarcoAlves - 我的记录器实现非常定制,对其他人不起作用。我只是做了一点挖掘,有一个 DiagnosticsLoggerFactory (msdn.microsoft.com/en-us/library/…) 可能对你有用。
    • 谢谢。我的问题是这样的,现在解决了:stackoverflow.com/questions/24894789/…
    • 我遇到了同样的问题,但根本原因略有不同:用于签署 Facebook SSL 证书的 X509 证书未安装在服务器上,因此当我在浏览器中导航到 facebook.com 时,我得到了一个证书错误。修复此问题解决了登录问题。
    【解决方案8】:

    我已经研究了三天的解决方案。而且我刚刚在github上找到了(https://github.com/aspnet/AspNetKatana/issues/38#issuecomment-290400987)

    var facebookOptions = new FacebookAuthenticationOptions()
    {
        AppId = "xxxxx",
        AppSecret = "xxxxx",
    };
    
    // Set requested scope
    facebookOptions.Scope.Add("email");
    facebookOptions.Scope.Add("public_profile");
    
    // Set requested fields
    facebookOptions.Fields.Add("email");
    facebookOptions.Fields.Add("first_name");
    facebookOptions.Fields.Add("last_name");
    
    facebookOptions.Provider = new FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
            {
                // Attach the access token if you need it later on for calls on behalf of the user
                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);
                    var claimType = string.Format("{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 Task.FromResult(0);
           }
    };
    
    app.UseFacebookAuthentication(facebookOptions);
    

    获取价值

    var info = await AuthenticationManager.GetExternalLoginInfoAsync();
    
    if (info != null)
    {
        var firstName = info.ExternalIdentity.Claims.First(c => c.Type == "first_name").Value;
        var lastName = info.ExternalIdentity.Claims.First(c => c.Type == "last_name").Value;
    }
    

    【讨论】:

    • 我这辈子从未见过这么棒的答案(:真的谢谢。
    【解决方案9】:

    检查您的应用程序是否获得了外部互联网连接。如果没有,请修复您的外部互联网连接。我的问题是我使用的 EC2 AWS 实例突然停止连接到互联网。我花了一段时间才意识到这是问题所在。

    【讨论】:

      【解决方案10】:

      这让我发疯了。一切都在工作,直到我部署到我的登台环境。我使用的是 Nuget 的 Microsoft.Owin.Security.Facebook 3.0.1 版。将其从 Nuget 更新为 3.1.0 版,我不再收到拒绝访问错误...

      【讨论】:

      【解决方案11】:

      即使我按照 sammy34 所说的做了一切,但它对我不起作用。我与 HaukurHaf 处于同一点:当我在浏览器上手动进行 apirequest 时,它运行良好,但如果我使用我的 mvc 应用程序,GetExternalLoginInfoAsync() 总是返回 null

      所以我在 sammy34 的代码上更改了一些行,例如此评论:https://stackoverflow.com/a/43148543/7776015

      替换:

      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 result = await base.SendAsync(request, cancellationToken);
      if (!request.RequestUri.AbsolutePath.Contains("access_token"))
      return result;
      

      并将这一行添加到我的FacebookAuthenticationOptions

      UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name,picture"
      

      现在它可以工作了。(字段和参数可选)

      注意:我没有更新Microsoft.Owin.Security.Facebook

      【讨论】:

        猜你喜欢
        • 2015-06-26
        • 1970-01-01
        • 2015-09-17
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多