27

我正在尝试在 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 中调试时得到了这个。我猜这是一个框架错误,但到目前为止还没有通过反射器来诊断它。

4

11 回答 11

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 没有返回用户名时,代码会将默认用户名设置为空。

于 2013-10-24T17:57:30.340 回答
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;
}
于 2013-12-30T20:59:48.973 回答
4

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

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

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

于 2014-10-20T17:03:10.470 回答
3

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

  • 更新以下 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

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);
    }
}
于 2017-03-31T12:02:48.057 回答
2

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

DotNetOpenAuth 中的 NullReferenceException

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

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

如果是这样,这是否意味着 OSS 被埋没在封闭的代码中?

希望看到您的堆栈跟踪。

于 2013-12-02T05:55:48.127 回答
2

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

于 2014-07-06T10:06:00.077 回答
1

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

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

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

于 2014-08-30T00:41:54.387 回答
0

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

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

于 2015-01-10T14:58:48.420 回答
0

我也一样。

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

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

// This must come later
app.UseGoogleAuthentication(
            "[ClientId]",
            "[ClientSecret]");
于 2015-12-07T04:07:46.720 回答
0

我想我会为 Visual Studio 2015 模板/WebAPI 2 的最新样板代码添加一些注释。我在使用 google 身份验证时遇到了这个问题,但认为它类似于 facebook 和其他社交登录。我有最新的 Owin,我的其他 nuget 包是最新的。事实证明,使用最新的开箱即用 web api 2 模板,我只需要专门请求将“电子邮件”从 google 包含回来。如果没有这一行,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)登录,您将被重定向回您的重定向页面。它会有一个类似这样的 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

抓住令牌(上面的粗体)并将其用作不记名令牌。

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

4) 现在由于您没有注册(但您确实有一个不记名令牌),您可以调用 POST api/Account/RegisterExternal

在此处输入图像描述

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

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

于 2016-03-02T08:42:18.183 回答
0

当我查看 fiddler 时,我没有启用 Google+ API 并返回 access_denied。启用 Google+ API 解决了这个问题。

于 2016-06-01T18:03:21.153 回答