Facebook中的ASP.NET MVC5身份验证突然不起作用
2017年更新强> 我在发布原始问题时遇到的问题与Facebook最近的改变无关,Facebook强迫所有人使用2.3版API。有关该特定问题的解决方案,请参见下面sammy34的答案。/oauth/access_令牌端点的2.3版现在返回JSON,而不是表单编码的值 出于历史原因,我的原始问题如下: 我有一个MVC5 Web应用程序,它通过Facebook和Google使用内置的身份验证支持。几个月前,当我们构建这个应用程序时,我们遵循了这个教程:一切都很好 现在,Facebook认证突然停止了工作。谷歌认证仍然很有效 问题描述:我们点击链接使用Facebook进行连接,我们会被重定向到Facebook,如果我们不允许Facebook应用访问我们的个人资料,系统会提示我们。当我们点击“确定”时,我们会被重定向回我们的站点,但是我们没有登录,而是直接进入登录屏幕 我已经在调试模式下完成了这个过程,根据上面提到的教程,我在我的帐户控制器中得到了这个ActionResult:Facebook中的ASP.NET MVC5身份验证突然不起作用,facebook,asp.net-mvc-5,owin,Facebook,Asp.net Mvc 5,Owin,2017年更新 我在发布原始问题时遇到的问题与Facebook最近的改变无关,Facebook强迫所有人使用2.3版API。有关该特定问题的解决方案,请参见下面sammy34的答案。/oauth/access_令牌端点的2.3版现在返回JSON,而不是表单编码的值 出于历史原因,我的原始问题如下: 我有一个MVC5 Web应用程序,它通过Facebook和Google使用内置的身份验证支持。几个月前,当我们构建这个应用程序时,我们遵循了这个教程:一切都很好 现在,Facebook认证突然停止了工
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
............
我相信这是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>
//步骤1-将其粘贴到浏览器中,将返回一个代码
https://www.facebook.com/dialog/oauth?response_type=code&client_id=619359858118523&redirect_uri=https%3A%2F%2Flocalhost%2Fsignin-facebook&scope=&state=u9r1M4iri6td4yacego99etqw9naos06bzwiljrxrn1rh4eqhfuevaq52upnuif-lehgayywrsrrdlw6ghld8ifgx5s2iubhotytqcq9lx2nl091hpiw1n0jv23sc4wyfos2yu5smyw9mghceuinvtaeql2ql2qhbowr62ffu6pyla6m8pd3odiwbymoreMLu2qnpEk0GekbtTVWgQnKnH6t1UcC6KcNXYY
我被重定向回localhost(此时我已关闭该主机,以避免立即被重定向到别处)。我重定向到的URL是:
https://localhost/signin-facebook?code=
现在,我抓取了我得到的代码并在下面的URL中使用它:
//步骤2-在浏览器中打开此URL,并成功检索访问令牌
https://graph.facebook.com/oauth/access_token?client_id=619359858118523&redirect_uri=https://localhost/signin-facebook和客户端\u secret=&代码=
//第3步-现在我可以使用第2步中的访问令牌查询facebook图了!
https://graph.facebook.com/me?access_token=
没有错误,一切都很好!那么,为什么在使用MVC5 Owin的时候,这不起作用呢?OWin实现显然有问题。好的,我找到了解决问题的方法 这是我以前在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);
行已被注释掉,但我仍在稍后的OnAuthenticated处理程序中查询电子邮件?是的,没错。出于某种原因,这在几周内完美无瑕
我的解决方案是简单地取消对x.Scope.Add(“email”)的注释;行以确保scope=email变量出现在对Facebook的初始请求中
现在一切都和以前一样
我不明白为什么以前会这样。我能想到的唯一解释是Facebook改变了他们的某些方面。我也有这个问题,但这不是由范围设置引起的。我花了很长时间才弄明白这一点,但最终让我明白的是,在
OwinStartup.Configuration(IAppBuilder app)
中设置了一个自定义记录器
这将输出以下内容:
2014-05-31 21:14:48508[8]错误Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
[(null)]-0x00000000-身份验证失败
System.Net.Http.HttpRequestException:发送时出错 请求。-->System.Net.WebException:无法找到远程名称
解析:'graph.facebook.com'位于
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
在System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)---内部异常堆栈跟踪的结束---at
System.Runtime.CompilerServices.TaskWaiter.ThrowForNonSuccess(任务
任务)在
System.Runtime.CompilerServices.TaskWaiter.HandleNonSuccessAndDebuggerNotification(任务 任务)在System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()中 在
Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d_u0.MoveNext()
基于上述调用堆栈,我发现我的Azure VM无法解析graph.facebook.com。我所要做的就是运行“ipconfig/registerdns”来修复这个问题,我已经全部修复了…我在谷歌认证方面也遇到了同样的问题。以下方法对我有效:上述解决方案对我无效。最后,它似乎与会议有关。通过在上一次调用中“唤醒”会话,它将不再从getExternalLoginFoAsync()返回null 像OP一样,我让第三方auth工作了很长一段时间,然后突然停止了。我相信这是因为我在Azure上设置会话以使用Redis缓存时对代码进行了更改。
// 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>
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")
app.SetLoggerFactory(new LoggerFactory());
// Note: LoggerFactory is my own custom ILoggerFactory
[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 }));
}
public class FacebookOauthResponse
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
}
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;
}
}
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;
}
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;
UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name,picture"