asp.net-mvc MVC 5 Owin Facebook Auth 导致空引用异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19564479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 03:15:24  来源:igfitidea点击:

MVC 5 Owin Facebook Auth results in Null Reference Exception

asp.net-mvcfacebookasp.net-mvc-5owinasp.net-identity

提问by Gracie

I'm trying to setup integrated OWIN Facebook authentication in a new MVC 5 project in Visual Studio 2013. I have configured apps and keys as per this tutorial:

我正在尝试在 Visual Studio 2013 的新 MVC 5 项目中设置集成的 OWIN Facebook 身份验证。我已经按照本教程配置了应用程序和密钥:

http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on

http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on

However, I'm getting a NullReferenceException thrown from this call in the AccountController:

但是,我在 AccountController 中的这个调用中抛出了 NullReferenceException:

    [AllowAnonymous]
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
    {
        var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();

I already checked the response in Fiddler and am getting what appears to be a success response from Facebook, but still get this error. The response looks like this:

我已经检查了 Fiddler 中的响应,并且从 Facebook 得到了看似成功的响应,但仍然收到此错误。响应如下所示:

{"id":"xxx","name":"xxx","first_name":"xxx","last_name":"xxx","link":
"https:\/\/www.facebook.com\/profile.php?id=xxx","location":{"id":"xxx","name":"xxx"},
"gender":"xxx","timezone":1,"locale":"en_GB","verified":true,"updated_time":"2013-10-23T10:42:23+0000"}

I get this when debugging in http as well as https. I'm guessing this is a framework bug but have so far drawn a blank diagnosing this through reflector.

我在 http 和 https 中调试时得到这个。我猜这是一个框架错误,但到目前为止已经通过反射器进行了诊断。

采纳答案by Hongye Sun

This probably is a bug in identity OWIN extension code. I can't repro the issue as my facebook payload always returns a username field in json, which is missing from your fb response. I am not quite sure why it's not there.

这可能是身份 OWIN 扩展代码中的一个错误。我无法重现该问题,因为我的 Facebook 负载总是在 json 中返回一个用户名字段,而您的 fb 响应中缺少该字段。我不太确定为什么它不在那里。

The code in identity owin extension method doesn't have a null check for the identity's name claim which is same as the username field. We have filed a bug for it internally.

身份 owin 扩展方法中的代码没有对与用户名字段相同的身份名称声明进行空检查。我们已经在内部为它提交了一个错误。

In order to workaround this issue, could you try replacing your ExternalLoginCallback method with following code:

为了解决这个问题,您可以尝试用以下代码替换您的 ExternalLoginCallback 方法:

   [AllowAnonymous]
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
    {
        var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);
        if (result == null || result.Identity == null)
        {
            return RedirectToAction("Login");
        }

        var idClaim = result.Identity.FindFirst(ClaimTypes.NameIdentifier);
        if (idClaim == null)
        {
            return RedirectToAction("Login");
        }

        var login = new UserLoginInfo(idClaim.Issuer, idClaim.Value);
        var name = result.Identity.Name == null ? "" : result.Identity.Name.Replace(" ", "");

        // Sign in the user with this external login provider if the user already has a login
        var user = await UserManager.FindAsync(login);
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            // If the user does not have an account, then prompt the user to create an account
            ViewBag.ReturnUrl = returnUrl;
            ViewBag.LoginProvider = login.LoginProvider;
            return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = name });
        }
    }

The code will set default user name as empty when there is no username back from facebook/google.

当 facebook/google 没有用户名时,代码会将默认用户名设置为空。

回答by Bryan Knox

Hongye Sun did all the heavy lifting in his answer above.

孙红叶在上面的回答中做了所有繁重的工作。

Here's some code that can be added to your controller class and be called in place of the troublesome AuthenticationManager.GetExternalLoginInfoAsync().

这里有一些代码可以添加到您的控制器类中并被调用以代替麻烦的 AuthenticationManager.GetExternalLoginInfoAsync()。

private async Task<ExternalLoginInfo> AuthenticationManager_GetExternalLoginInfoAsync_Workaround()
{
    ExternalLoginInfo loginInfo = null;

    var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);

    if (result != null && result.Identity != null)
    {
        var idClaim = result.Identity.FindFirst(ClaimTypes.NameIdentifier);
        if (idClaim != null)
        {
            loginInfo = new ExternalLoginInfo()
            {
                DefaultUserName = result.Identity.Name == null ? "" : result.Identity.Name.Replace(" ", ""),
                Login = new UserLoginInfo(idClaim.Issuer, idClaim.Value)
            };
        }
    }
    return loginInfo;
}

回答by Daniil T.

I had the same problem. I solve my problem just added app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);to the Startup.Auth.cs. I didn't have that in my Startup.Auth.cs so

我有同样的问题。我解决了刚刚添加app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);到 Startup.Auth.cs 的问题。我的 Startup.Auth.cs 中没有,所以

var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);

var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);

always thrown me an Object reference not set to an instance of an object error. I figure that out by analyzing VS 2013 default template for MVC 5. So if you need more info on code structure or example take a look on VS 2013 MVC5 template.

总是向我抛出一个未设置为对象错误实例的对象引用。我通过分析 MVC 5 的 VS 2013 默认模板来解决这个问题。因此,如果您需要有关代码结构或示例的更多信息,请查看 VS 2013 MVC5 模板。

回答by user3012760

I came across this post a few days ago but unfortunately none of the above solutions worked for me. so here is how I managed to fix it and get the email from Facebook.

几天前我看到了这篇文章,但不幸的是,上述解决方案都不适合我。所以这是我设法修复它并从 Facebook 获取电子邮件的方法。

  • Update following NuGet Pacakges
    • Microsoft.Owinto version 3.1.0-rc1
    • Microsoft.Owin.Securityto version 3.1.0-rc1
    • Microsoft.Owin.Security.Cookiesto version 3.1.0-rc1
    • Microsoft.Owin.Security.OAuthto version 3.1.0-rc1
    • Microsoft.Owin.Security.Facebookto version 3.1.0-rc1
  • 更新以下 NuGet 包
    • Microsoft.Owin版本 3.1.0-rc1
    • Microsoft.Owin.Security版本 3.1.0-rc1
    • Microsoft.Owin.Security.Cookies版本 3.1.0-rc1
    • Microsoft.Owin.Security.OAuth版本 3.1.0-rc1
    • Microsoft.Owin.Security.Facebook版本 3.1.0-rc1

Then add the following code to the Identity Startupclass

然后在Identity Startup类中添加以下代码

var facebookOptions = new FacebookAuthenticationOptions()
        {
            AppId = "your app id",
            AppSecret = "your app secret",
            BackchannelHttpHandler = new FacebookBackChannelHandler(),
            UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name",
            Scope = { "email" }
        };

        app.UseFacebookAuthentication(facebookOptions);

This is the definition class for FacebookBackChannelHandler():

这是 的定义类FacebookBackChannelHandler()

using System;
using System.Net.Http;

public class FacebookBackChannelHandler : HttpClientHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        System.Threading.CancellationToken cancellationToken)
    {
        // Replace the RequestUri so it's not malformed
        if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
        {
            request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

回答by Rahul Patel

I faced the same problem, when I checked libraries, I was using Microsoft ASP.NET Identity Owin 1.0.0 I updated it to Microsoft ASP.NET Identity Owin 2.0.1 using command PM> Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1 This fixed the issue.

我遇到了同样的问题,当我检查库时,我使用的是 Microsoft ASP.NET Identity Owin 1.0.0 我使用命令 PM> Install-Package Microsoft.AspNet.Identity.Owin 将其更新为 Microsoft ASP.NET Identity Owin 2.0.1 -Version 2.0.1 这解决了这个问题。

回答by shannon

If your stack trace contains DotNetOpenAuth.AspNet then is the same bug as has apparently existed for two years in DotNetOpenAuth/DotNetOpenId.

如果您的堆栈跟踪包含 DotNetOpenAuth.AspNet,那么与在 DotNetOpenAuth/DotNetOpenId 中明显存在两年的错误相同。

NullReferenceException in DotNetOpenAuth

DotNetOpenAuth 中的 NullReferenceException

https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/317#issuecomment-29580565

https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/317#issuecomment-29580565

The owner of those libraries indicate MS has abandoned them, although it looks from your defect like they are probably actually moved into MS code.

这些库的所有者表示 MS 已经放弃了它们,尽管从您的缺陷来看,它们可能实际上已移入 MS 代码中。

If so, does that mean OSS got buried into closed code?

如果是这样,这是否意味着 OSS 被隐藏在封闭代码中?

Would love to see your stack trace.

很想看看你的堆栈跟踪。

回答by parliament

I started getting this in the latest VS 2013.3 template and realized the authentication wasn't playing nice with FormsAuthentication that I unnecessarily ported from one of my other projects. Here's what I did to fix it:

我开始在最新的 VS 2013.3 模板中获取此信息,并意识到身份验证与 FormsAuthentication 的配合不佳,我从其他项目中不必要地移植了它。这是我为修复它所做的:

added <system.web><authentication mode="None" />...

添加 <system.web><authentication mode="None" />...

added <system.webServer><modules><remove name="FormsAuthentication" /></modules>...

添加 <system.webServer><modules><remove name="FormsAuthentication" /></modules>...

回答by Leona

I had exactly the same problem by following the same tutorial. I solved it by doing the following two steps: 1> Visual Studio Menu->Tools->Library Package Manager->Manage NuGet Packages for Solution..., then install package: Microsoft.Owin.Host.SystemWeb 2> In the same window, click Update (left bar) and then update all the packages.

按照相同的教程,我遇到了完全相同的问题。我通过执行以下两个步骤来解决它:1> Visual Studio Menu->Tools->Library Package Manager->Manage NuGet Packages for Solution...,然后安装包:Microsoft.Owin.Host.SystemWeb 2> 在同一个窗口,单击更新(左栏),然后更新所有包。

Hope this answer will help other people who have the same problem.

希望这个答案能帮助其他有同样问题的人。

回答by joshcomley

I was getting the same.

我也一样。

I noticed that my providers were configured beforeUseExternalSignInCookiewas called, so I simply made sure UseExternalSignInCookieis called before my providers are configured and everything worked:

我注意到我的提供者是UseExternalSignInCookie被调用之前配置的,所以我只是确保UseExternalSignInCookie在我的提供者被配置之前被调用并且一切正常:

// This has to go first
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

// This must come later
app.UseGoogleAuthentication(
            "[ClientId]",
            "[ClientSecret]");

回答by Greg

I thought I'd throw on some notes for Visual Studio 2015 templates / the latest boiler plate code for WebAPI 2. I was getting this problem with google authentication but figure its similar to facebook and the other social logins. I had the latest Owin and my other nuget packages were up-to-date. Turns out with the latest out-of-the-box web api 2 templates, I just needed to specifically request the "email" be included back from google. Without this line, the api/Account/Register call would error.

我想我会为 Visual Studio 2015 模板/WebAPI 2 的最新样板代码添加一些注释。我在使用 google 身份验证时遇到了这个问题,但认为它类似于 facebook 和其他社交登录。我有最新的 Owin,我的其他 nuget 软件包也是最新的。事实证明,使用最新的开箱即用的 web api 2 模板,我只需要专门请求从谷歌返回“电子邮件”。如果没有这一行,api/Account/Register 调用就会出错。

And of course make sure your app is registered with google and your site is allowed to call it. (Lots of good examples showing those steps.) https://console.developers.google.com/apis

当然,请确保您的应用已在 google 上注册,并且您的网站可以调用它。(很多很好的例子展示了这些步骤。) https://console.developers.google.com/apis

Here's my adjustment in the App_Start\Startup.Auth.cs file:

这是我在 App_Start\Startup.Auth.cs 文件中的调整:

var googleOptions = new GoogleOAuth2AuthenticationOptions()
{
    ClientId = "xxx",
    ClientSecret = "xxx"
};
googleOptions.Scope.Add("email"); //!! Add this !!
app.UseGoogleAuthentication(googleOptions);

Until I added the .Add("email"), line, the api/Account/RegisterExternal WebAPI 2 call (AccountController.cs) would return null from this section of RegisterExternal:

在我添加 .Add("email"), 行之前,api/Account/RegisterExternal WebAPI 2 调用 (AccountController.cs) 将从 RegisterExternal 的这一部分返回 null:

var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null) //This would be true, and it would error.
{
      return InternalServerError();
}

Since this is one of the few articles that come up for this error, I figured I'd tag my notes on my solution for posterity. (especially the postman test process!)

由于这是针对此错误提出的为数不多的文章之一,因此我想我会在我的解决方案上标记我的笔记以供后代使用。(尤其是邮递员测试过程!)

So to make it all work in testing: 1) Call the api/Account/ExternalLogins URL like this:

因此,要使其全部用于测试: 1) 像这样调用 api/Account/ExternalLogins URL:

http://localhost:59137/api/Account/ExternalLogins?returnUrl=%2F&generateState=true

You should get a response like this:

你应该得到这样的回应:

<ArrayOfExternalLoginViewModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/TCG_DL_API.Models">
<ExternalLoginViewModel>
<Name>Google</Name>
<State>1phegLF241xeSfd8gZAsCXiBAp3l5bMygg2VSeRXAHk1</State>
<Url>
/api/Account/ExternalLogin?provider=Google&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A59137%2F&state=1phegLF241xeSfd8gZAsCXiBAp3l5bMygg2VSeRXAHk1
</Url>
</ExternalLoginViewModel>
</ArrayOfExternalLoginViewModel>

2) Then get the Url from the response, and call it. You should get the google login prompt/page. (Or I assume the facebook or twitter one, if that's what you set up.)

2)然后从响应中获取Url,并调用它。你应该得到谷歌登录提示/页面。(或者我假设 facebook 或 twitter 一个,如果那是你设置的。)

3) Login, and you'll get redirected back to your redirect page. It'll have a URL like something like this:

3) 登录,您将被重定向回重定向页面。它会有一个像这样的 URL:

http://localhost:59137/#access_token=d5asC1arCUXaLEMgBS8PT_uwZcTJqC1UZbXblNZ3hMOh3TSKtEXYeKtyKBTv3WmLcaLGGomSvpRSFMfXPxpPvNRgjUVWAiqxtKfv3qWHNqfIMeu5j0eZrJDRAMTrYFgflSbEopAe909a31I4mQnJuvaiITHYPrLmqkm6J88HAVx8F981_q_tflu4A72k3KaB-m2wd0-p1jdQnNMlixM2Wfloh_niUTBIOYUPc1SkKWcZxuI6dzN2Z0PmWHDwzJI8nM8vOuzybJIsxLOyTY1VfzSQ5Qzcll3HhifLPkyZxvXDQ5LHqW1v0_AztsUWkEhW_AJzmw2IaOcTtHCmkmWm1K444okNtOsYfs6HFui0NeY&token_type=bearer&expires_in=1209600&state=3FSOd3_n_sEL4QtiELWPG5B2_H3wRjVb75uDjQS16gk1

HTTP://本地主机:59137 /#=的access_token d5asC1arCUXaLEMgBS8PT_uwZcTJqC1UZbXblNZ3hMOh3TSKtEXYeKtyKBTv3WmLcaLGGomSvpRSFMfXPxpPvNRgjUVWAiqxtKfv3qWHNqfIMeu5j0eZrJDRAMTrYFgflSbEopAe909a31I4mQnJuvaiITHYPrLmqkm6J88HAVx8F981_q_tflu4A72k3KaB-m2wd0-p1jdQnNMlixM2Wfloh_niUTBIOYUPc1SkKWcZxuI6dzN2Z0PmWHDwzJI8nM8vOuzybJIsxLOyTY1VfzSQ5Qzcll3HhifLPkyZxvXDQ5LHqW1v0_AztsUWkEhW_AJzmw2IaOcTtHCmkmWm1K444okNtOsYfs6HFui0NeY&token_type=承载&expires_in = 1209600&状态= 3FSOd3_n_sEL4QtiELWPG5B2_H3wRjVb75uDjQS16gk1

grab the token (bold above) and use it as the bearer token.

获取令牌(上面粗体)并将其用作不记名令牌。

Postman example of GET api/Account/UserInfo

GET api/Account/UserInfo 的邮递员示例

4) Now since you aren't registered (but you do have a bearer token), you can call the POST api/Account/RegisterExternal

4) 现在因为你没有注册(但你有一个不记名令牌),你可以调用 POST api/Account/RegisterExternal

enter image description here

在此处输入图片说明

5) The response will be OK, and if you look in your AspnetUser tables, you'll see that you have a new AspnetUsers record and a new AspNetUserLogins record for google as the provider.

5) 响应会正常,如果您查看 AspnetUser 表,您会看到您有一个新的 AspnetUsers 记录和一个新的 AspNetUserLogins 记录,google 作为提供者。

I hope this helps with anyone trying to get this stuff to work!

我希望这对任何试图让这些东西起作用的人都有帮助!