ASP.NET MVC5 OWIN Facebook身份validation突然不起作用

2017年更新!

我发布原始问题时遇到的问题与Facebook迫使每个人版本2.3的API所做的最近更改没有任何关系。 有关特定问题的解决scheme,请参阅下面的sammy34的答案 。 / oauth / access_token端点的2.3版现在返回JSON而不是表单编码的值

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

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

现在,突然之间,Facebook身份validation刚刚停止工作。 谷歌authentication仍然很好。

问题描述:我们点击链接使用Facebook连接,我们被redirect到Facebook,如果我们不能让我们的Facebook应用程序访问我们的个人资料,我们会被提示。 当我们点击“确定”时,我们被redirect回到我们的网站,而不是login,我们只是在login屏幕上结束。

我已经在debugging模式下完成了这个过程,并且按照上面提到的教程,我的帐户控制器中有了这个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,这将导致用户被redirect回login名。

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

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

这个URL不是一个实际的文件,很可能是由这个OWIN框架中内置的一些控制器/处理程序来处理的。 最有可能的是,它使用给定的代码连接到Facebook来查询关于正在尝试login的用户的信息。 现在问题是,我们不是这样做,而是redirect到:

 /Account/ExternalLoginCallback?error=access_denied 

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

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

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

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

任何想法欢迎!

更新!

好的,这让我疯狂! 我现在已经手动完成了执行身份validation所需的步骤,而且当我这样做的时候一切正常。 为什么在使用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实现显然有些问题。

更新2017年4月22日: Microsoft.Owin。*软件包的版本3.1.0现在可用。 如果您在2017年3月27日Facebook的API更改后遇到问题,请先尝试更新的NuGet包。 在我的情况下,他们解决了这个问题(在我们的生产系统上正常工作)。

原始答案:

就我而言,我在2017年3月28日醒来,发现我们的应用程序的Facebook身份validation突然停止工作。 我们没有改变任何应用程序代码。

事实certificate,Facebook在2017年3月27日将graphicsAPI从2.2版本强制升级到2.3版本。这些版本的API之间的差异之一似乎是Facebook端点/oauth/access_token不再响应一个表单编码的内容主体,但用JSON代替。

现在,在Owin中间件中,我们发现protected override FacebookAuthenticationHandler.AuthenticateCoreAsync()方法protected override FacebookAuthenticationHandler.AuthenticateCoreAsync() ,它将响应的主体parsing为表单,随后使用parsing后的表单中的access_token 。 不用说,parsing的表单是空的,所以access_token也是空的,导致进一步向下的access_denied错误。

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

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

基本上,处理程序只需创build一个新的HttpResponseMessage,它包含来自Facebook JSON响应的等效的表单编码信息。 请注意,这段代码使用stream行的Json.Net包。

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

希望能拯救别人今天醒来也有类似的问题!

另外,如果有人有一个更清洁的解决scheme,我很想知道!

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

好的,我已经有了解决这个问题的方法。

这是我以前在我的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处理程序中查询电子邮件? 是的,没错。 由于某种原因,这工作几个星期完美无瑕。

我的解决scheme是简单地取消注释x.Scope.Add(“email”); 以确保在对Facebook的初始请求中存在scope = emailvariables。

现在一切都像它一样工作!

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

我在Google身份validation中遇到了同样的问题。 以下工作适用于: Google OAuth 2.0的更改以及Google中间件3.0.0 RC版本中的更新

最后的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库无法parsing新的响应。

tldr:Microsoft WebPages OAuth库已经过期(至less对于FB),您可能需要另外寻找解决scheme

我也有这个问题,但这不是由范围设置造成的。 花了我很长时间来弄清楚,但最终我提出的是通过设置OwinStartup.Configuration(IAppBuilder app)的以下设置自定义logging器。

 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 – authentication失败
System.Net.Http.HttpRequestException:发送请求时发生错误。 —> System.Net.WebException:远程名称不能
解决:'graph.facebook.com'在
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
在System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)—结束内部exception堆栈跟踪—在
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务
任务)在
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()at
Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext()

基于上述调用堆栈,我发现我的Azure虚拟机无法parsinggraph.facebook.com。 我所要做的只是解决这个问题,就是运行“ipconfig / registerdns”,我都修好了……

上述解决scheme不适合我。 最后,这似乎与会议有关。 通过在上次调用中“唤醒”会话,它将不再从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一样,我有第三方authentication工作很长时间然后突然停止。 我相信这是因为我在设置Session使用Azure上的Rediscaching时在代码中所做的更改。

我也有这个问题。 包括这个,我已经阅读了两天的许多文章和主题。 他们都没有为我工作。 Facebooklogin在部署之前已经完美运行,但之后并没有发生。 我发现,我的VPS互联网连接出现问题,所以我在我的VPS上尝试了NextVPN。 但是当我试图通过我的电脑login时,NextVPN的内部ProxiFire中没有任何新的连接。 最后,在我的VPS上安装OpenVPN解决了这个问题。

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

我一直在为解决scheme工作三天。 我刚刚在github上find了它( 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; } 

这让我疯狂。 所有的工作,直到我部署到我的分期环境。 我正在使用Nuget的Microsoft.Owin.Security.Facebook版本3.0.1。 更新它从Nuget的预发行版本3.1.0,我不再有访问被拒绝的错误…

即使我做了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