ASP.NET MVC5 OWIN Facebook 身份验证突然不起作用
Posted
技术标签:
【中文标题】ASP.NET MVC5 OWIN Facebook 身份验证突然不起作用【英文标题】:ASP.NET MVC5 OWIN Facebook authentication suddenly not working 【发布时间】: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 应用程序:***.com/questions/18942196/… 和此处:***.com/questions/20928939/…。在我的本地开发环境中运行良好,在 azure 平台上出现交替错误 access_denied 和 FB 错误“应用程序配置不允许给定 URL。:应用程序的设置不允许一个或多个给定 URL...” 好,我不是唯一一个 :-) 如果我找到解决此问题的方法,我一定会通知您! 我也遇到了同样的问题,将example.com/signin-facebook 设置为 facebook 上的重定向 url 解决了这个问题 按照您的 URL 测试方法,我注意到我的应用程序密码已更改!干杯。 【参考方案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 :))。
希望能拯救今天醒来时遇到类似问题的其他人!
另外,如果有人对此有更清洁的解决方案,我很想知道!
【讨论】:
我也有这样的早晨 - 谢谢,很高兴找到你的帖子! :) 同样的问题,谢谢你的帖子。 Azure 移动服务也受到影响。 social.msdn.microsoft.com/Forums/azure/en-US/… 这已在 3.1.0-RC1 github.com/aspnet/AspNetKatana/issues/38 中修复 如果您认为我第一次尝试创建 Facebook 登录实际上是在 3 月 28 日,那感觉就像是一片乌云…… 3.1.0稳定版已经发布:nuget.org/packages/Microsoft.Owin.Security.Facebook【参考方案2】:昨天注意到这个问题。 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
【讨论】:
还是不行,我更新到最新版本 3.1.0-rc1 并确保所有 DLL 都更新了,github.com/aspnet/AspNetKatana/issues/38 无法从 ExternalLoginCallback 操作方法获取登录信息。尝试 HttpContext.GetOwinContext().Authentication.GetExternalLoginInfoAsync() 返回 null。 3.1.0 稳定版已经发布:nuget.org/packages/Microsoft.Owin.Security.Facebook 本帖也有助于排查问题:github.com/aspnet/AspNetKatana/issues/38 非常感谢您在 6 小时的调查后节省了我的时间! 你是个摇滚明星。谢谢!【参考方案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/…) 可能对你有用。 谢谢。我的问题是这样的,现在解决了:***.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 版,我不再收到拒绝访问错误...
【讨论】:
这成功了(此类问题最好由框架解决)-nuget.org/packages/Microsoft.Owin.Security.Facebook/3.1.0-rc1【参考方案11】:即使我按照 sammy34 所说的做了一切,但它对我不起作用。我与 HaukurHaf 处于同一点:当我在浏览器上手动进行 apirequest 时,它运行良好,但如果我使用我的 mvc 应用程序,GetExternalLoginInfoAsync()
总是返回 null。
所以我在 sammy34 的代码上更改了一些行,例如此评论:https://***.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
【讨论】:
以上是关于ASP.NET MVC5 OWIN Facebook 身份验证突然不起作用的主要内容,如果未能解决你的问题,请参考以下文章