2017年更新!
我发布原始问题时遇到的问题与Facebook最近在强迫每个人使用其API版本2.3时所做的更改无关。有关该特定问题的解决方案,请参阅 sammy34&#39>以下答案。 / 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应用访问我们的个人资料,我们会被提示。当我们点击" OK"我们被重定向回我们的网站,但我们只是登录屏幕而不是登录。
我已经在调试模式下完成了这个过程,并且根据上面提到的教程,我已经在我的帐户控制器中获得了这个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流量。我发现的是点击&#34; OK&#34;在Facebook权限对话框中,Facebook使用以下网址重定向回我们的应用程序:
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的实施显然有问题。
答案 0 :(得分:109)
2017年4月22日更新:Microsoft.Owin。*软件包3.1.0版现已推出。如果您在2017年3月27日Facebook更改后遇到问题,请先尝试更新的NuGet软件包。在我的情况下,他们解决了问题(在我们的生产系统上正常工作)。
原始回答:
在我的情况下,我在2017年3月28日醒来发现我们的应用程序的Facebook身份验证突然停止工作。我们在应用代码中没有任何改变。
事实证明,Facebook进行了强制升级&#34;他们的图API在2017年3月27日从版本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 :)。
希望拯救别人今天醒来时遇到类似的问题!
另外,如果有人对此有更清洁的解决方案,我很乐意知道!
答案 1 :(得分:27)
昨天注意到这个问题。 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
答案 2 :(得分:9)
好的,我已经解决了这个问题。
这是我之前在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”);确保scope = email变量存在于Facebook的初始请求中。
现在一切都像它一样!
我无法理解为什么它会像以前一样有效。我能想到的唯一解释是Facebook改变了一些东西。
答案 3 :(得分:4)
我在使用Google身份验证时遇到了同样的问题。以下内容适用于我:Changes to Google OAuth 2.0 and updates in Google middleware for 3.0.0 RC release
答案 4 :(得分:3)
最后一次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),您可能需要找到另一种解决方案
答案 5 :(得分:2)
我也有这个问题,但它不是由范围设置引起的。花了很长时间才弄明白,但最终让我了解的是通过在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:发送时发生错误 请求。 ---&GT; System.Net.WebException:远程名称不能为 解决:'graph.facebook.com'在 System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
在System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult) ar)---内部异常堆栈跟踪结束---在
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”并且我已经修好了......
答案 6 :(得分:2)
上述解决方案对我没有用。最后,它似乎与会议有关。通过&#34;醒来&#34;在上一次调用中的会话,它将不再从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一样,我让第三方认证工作很长时间然后突然停止了。我相信这是因为我在设置会话以在Azure上使用Redis缓存时在我的代码中所做的更改。
答案 7 :(得分:1)
我一直致力于解决方案三天。我刚刚在github上发现了它({{3}})
> pyomo solve Pyomo_concrete_v1.py --solver=glpk
获得价值
x[1]=0
x[2]=0.25
OBJ=0.75
答案 8 :(得分:0)
检查您的应用程序是否有外部互联网连接。如果没有,请修复您的外部互联网连接。我的问题是我使用的EC2 AWS实例突然停止连接到互联网。我花了一段时间才意识到这就是问题。
答案 9 :(得分:0)
这让我疯了。一切正常,直到我部署到我的临时环境。我正在使用Nuget的Microsoft.Owin.Security.Facebook 3.0.1版。将其更新为Nuget的预发布版本3.1.0,我不再获得访问被拒绝错误...
答案 10 :(得分: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