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 身份验证突然不起作用的主要内容,如果未能解决你的问题,请参考以下文章

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

Owin管道与asp.net管道模型

在 ASP.NET MVC5 中使用 Steam 登录

使用 OWIN 和异步方法的多语言网站

ASP.NET-入门

如何更新 ASP.NET Identity 中的声明?