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


67

更新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-和-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实现显然存在问题。


1
我正在处理同一问题-昨天开始发生。我有一个MVC5应用程序,使用此处描述的过程:stackoverflow.com/questions/18942196/…和此处:stackoverflow.com/questions/20928939/…。在我的本地开发环境上运行良好,在azure平台上出现access_denied和FB错误交替出现的错误“应用程序配置不允许使用URL 。:应用程序的设置不允许使用一个或多个给定的URL ...”
GojiraDeMonstah 2014年

1
好,我不是唯一的一个:-)如果我找到解决这个问题的方法,我一定会通知您!
HaukurHaf 2014年

我遇到了同样的问题,将example.com/signin-facebook用作Facebook上的重定向URL解决了该问题
Majid

按照您的URL测试方法,我注意到我的应用程序密码已更改!干杯。
蒂姆·泰勒

Answers:


112

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中间件中,我们找到了method 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
我也有这样的早晨-谢谢,很高兴我找到了您的帖子!:)
好撒玛利亚人

1
同样的问题在这里,感谢您的帖子。Azure移动服务也受到影响。social.msdn.microsoft.com/Forums/azure/zh-CN/…– MarcHägele
2017年


1
如果您认为我第一次尝试创建Facebook登录实际上是在3月28日,那感觉就像是头顶上的乌云...
Giannis Paraskevopoulos


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


1
仍然对我不起作用,我更新到最新版本3.1.0-rc1,并确保所有DLL都已
aspnet /

1
无法从ExternalLoginCallback操作方法获取登录信息。尝试HttpContext.GetOwinContext()。Authentication.GetExternalLoginInfoAsync()返回null。
Danyl

2
已发布3.1.0的稳定版本:nuget.org/packages/Microsoft.Owin.Security.Facebook 此线程也有助于解决问题:github.com/aspnet/AspNetKatana/issues/38
Good Samaritan

2
非常感谢您在经过6个小时的调查后节省了我的时间!
Marzouk

1
你是摇滚明星。谢谢!
坚不可摧的

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”);的注释。行,以确保在对Facebook的初始请求中出现了scope = email变量。

现在一切正常。

我不明白为什么这比以前更有效。我能提出的唯一解释是,Facebook最终改变了某些东西。


这段代码有错误,上面的代码缺少等待操作符
DevWithSigns

1
谢谢!奇怪的是,我需要进行更改才能从Azure托管的应用程序(以及Valid OAuth redirect URIs在我的Facebook应用程序设置中为某些值设置)中获取FB登录才能正常工作,而在我的开发环境中,无需进行这些修改即可正常工作。
加里·查普曼

2
@HaukurHaf,现在是否有可能再次失败?不管我做什么,我都无法获得,context.Email因为它始终为null(登录有效,但上下文仅包含ID和用户名)
zed

1
我将Owin升级到3.0.1-RC1并设置FacebookAuthenticationOptions { UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name"Startup.Auth; 我被授权并收到电子邮件:)不需要其他BackchannelHttpHandler
ignacy130

@zed-这几乎对我有用。我必须添加一个检查,context.Email != null如果是的话,请忽略它。
dwilliss '18


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),您可能不得不寻找其他解决方案


您应该切换到Microsoft.Owin.Security.Facebook 3.1.0-RC1。我还使用了DonNetOpenAuth库,直到Facebook迫使我们至少切换到图形API v2.3。因此,我们将解决方案从MVC 4升级到MVC 5,并切换到OWIN及其实现。祝您好运
Danyl

2

上述解决方案对我不起作用。最后,它似乎与本届会议有关。通过“唤醒”上一次调用中的会话,它将不再从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缓存时代码中所做的更改。


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:发送请求时发生错误。---> System.Net.WebException:无法
解析远程名称: System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar

上System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)处的“ graph.facebook.com” )-内部异常堆栈跟踪的结尾--在
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务
任务)在

Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext() 上System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()处的System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)

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


您为什么不插入LoggerFactry来帮助他人?
Marco Alves

@MarcoAlves-我的记录器实现非常自定义,其他任何人都无法使用。我只是做了一点挖掘,并且有一个DiagnosticsLoggerFactory(msdn.microsoft.com/en-us/library/…)可能会为您工作。
jt000 2014年

谢谢。:我的问题是这样,现在是解决stackoverflow.com/questions/24894789/...
马可·阿尔维斯

我在根本原因稍有不同的情况下遇到了相同的问题:服务器上未安装用于签名Facebook SSL证书的X509证书,因此当我在浏览器中导航至facebook.com时,出现了证书错误。解决此问题可以解决登录问题。
Martin Liversage 2015年

1

我已经解决了三天。我刚刚在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;
}

真的很感谢你们:我从来没有见过这么大的答案在我的有生之年(
亚瑟·莫拉迪

0

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



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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.