MVC 5 Owin Facebook Auth导致Null参考异常

时间:2013-10-24 11:19:06

标签: asp.net-mvc facebook asp.net-mvc-5 owin asp.net-identity

我正在尝试在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

但是,我在AccountController中从此调用中抛出了NullReferenceException:

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

我已经检查了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"}

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

11 个答案:

答案 0 :(得分:26)

这可能是身份OWIN扩展代码中的错误。我无法重现这个问题,因为我的facebook有效负载总是在json中返回一个用户名字段,这在你的fb响应中是遗漏的。我不太确定为什么它不存在。

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

为了解决此问题,您是否可以尝试使用以下代码替换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 });
        }
    }

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

答案 1 :(得分:24)

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

这里有一些代码可以添加到您的控制器类中,并代替麻烦的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;
}

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:2)

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

NullReferenceException in DotNetOpenAuth

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

这些图书馆的所有者表示MS已经放弃了它们,尽管它从您的缺陷中看起来像它们可能实际上已经转移到MS代码中。

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

很想看到你的筹码追踪。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

几天前我发现了这篇文章,但不幸的是,以上解决方案都没有对我有用。所以这就是我如何设法解决它并从Facebook获取电子邮件。

  • 更新NuGet Pacakges
    • 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

然后将以下代码添加到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);

这是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);
    }
}

答案 6 :(得分:1)

我开始在最新的VS 2013.3模板中获得此功能,并意识到身份验证与FormsAuthentication不相称,我从其他项目中移植过。以下是我采取的措施:

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

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

答案 7 :(得分:0)

通过遵循相同的教程,我遇到了完全相同的问题。我通过以下两个步骤解决了这个问题: 1 GT; Visual Studio菜单 - &gt;工具 - &gt;库包管理器 - &gt;管理解决方案的NuGet包...,然后安装包:Microsoft.Owin.Host.SystemWeb 2 - ;在同一窗口中,单击“更新”(左侧栏),然后更新所有包。

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

答案 8 :(得分:0)

我也是这样。

我注意到在调用 UseExternalSignInCookie之前我的提供程序已经配置了,所以我确保在配置提供程序之前调用UseExternalSignInCookie并且一切正常:

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

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

答案 9 :(得分:0)

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

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

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

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

在我添加.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();
}

由于这是针对此错误的少数几篇文章之一,我想我会在我的解决方案上为后代标记我的注释。 (尤其是邮差测试过程!)

所以要使它在测试中全部工作: 1)像这样调用api / Account / ExternalLogins URL:

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

您应该得到这样的回复:

<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)然后从响应中获取Url并调用它。你应该得到谷歌登录提示/页面。 (或者我假设是facebook或twitter,如果这就是你设置的那个。)

3)登录,您将被重定向回您的重定向页面。它会有类似这样的网址:

http://localhost:59137/#access_token= d5asC1arCUXaLEMgBS8PT_uwZcTJqC1UZbXblNZ3hMOh3TSKtEXYeKtyKBTv3WmLcaLGGomSvpRSFMfXPxpPvNRgjUVWAiqxtKfv3qWHNqfIMeu5j0eZrJDRAMTrYFgflSbEopAe909a31I4mQnJuvaiITHYPrLmqkm6J88HAVx8F981_q_tflu4A72k3KaB-m2wd0-p1jdQnNMlixM2Wfloh_niUTBIOYUPc1SkKWcZxuI6dzN2Z0PmWHDwzJI8nM8vOuzybJIsxLOyTY1VfzSQ5Qzcll3HhifLPkyZxvXDQ5LHqW1v0_AztsUWkEhW_AJzmw2IaOcTtHCmkmWm1K444okNtOsYfs6HFui0NeY&安培; token_type =&承载放大器; expires_in = 1209600&安培;状态= 3FSOd3_n_sEL4QtiELWPG5B2_H3wRjVb75uDjQS16gk1

抓取令牌(上面加粗)并将其用作持票人令牌。

Postman example of GET api/Account/UserInfo

4)现在既然你没有注册(但你有一个持有人令牌),你可以调用POST api / Account / RegisterExternal

enter image description here

5)响应将是正常的,如果您查看AspnetUser表,您将看到您有一个新的AspnetUsers记录和谷歌作为提供商的新AspNetUserLogins记录。

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

答案 10 :(得分:0)

我没有启用Google+ API,当我查看fiddler时,又回来了access_denied。启用Google+ API可以解决问题。