Подтвердить что ты не робот

MVC 5 Owin Facebook Auth приводит к исключению ссылки на Null

Я пытаюсь настроить интегрированную аутентификацию OWIN Facebook в новом проекте MVC 5 в Visual Studio 2013. Я настроил приложения и ключи в соответствии с этим руководством:

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

Однако я получаю исключение NullReferenceException, вызванное этим вызовом в AccountController:

    [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. Я предполагаю, что это ошибка структуры, но до сих пор нарисовал пустую диагностику этого через отражатель.

4b9b3361

Ответ 1

Вероятно, это ошибка в идентификационном коде OWIN. Я не могу воспроизвести проблему, так как моя полезная нагрузка facebook всегда возвращает поле имени пользователя в json, которое отсутствует в вашем ответе fb. Я не совсем уверен, почему этого не произошло.

Код в методе расширения 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.

Ответ 2

Hongye Sun сделал все тяжелый подъем в своем ответе выше.

Вот некоторый код, который можно добавить в класс контроллера и вызываться вместо проблемного 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;
}

Ответ 3

У меня была та же проблема. Я только что решил решить проблему app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); к Startup.Auth.cs. У меня не было этого в моем Startup.Auth.cs, поэтому

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

всегда меня бросает ссылку на объект, не установленную на экземпляр ошибки объекта. Я вычисляю это, анализируя шаблон VS 2013 по умолчанию для MVC 5. Поэтому, если вам нужна дополнительная информация о структуре кода или примере, посмотрите шаблон VS 2013 MVC5.

Ответ 4

Если ваша трассировка стека содержит DotNetOpenAuth.AspNet, то такая же ошибка, которая, по-видимому, существовала в течение двух лет в DotNetOpenAuth/DotNetOpenId.

NullReferenceException в DotNetOpenAuth

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

Владелец этих библиотек указывает, что MS отказалась от них, хотя он выглядит с вашего дефекта, как будто они, вероятно, действительно перемещены в код MS.

Если это так, означает ли это, что OSS попал в закрытый код?

Хотелось бы увидеть трассировку стека.

Ответ 5

Я столкнулся с той же проблемой, когда я проверял библиотеки, я использовал Microsoft ASP.NET Identity Owin 1.0.0 Я обновил его до Microsoft ASP.NET Identity Owin 2.0.1 используя команду PM > Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1 Это устранило проблему.

Ответ 6

Я столкнулся с этим сообщением несколько дней назад, но, к сожалению, ни один из вышеперечисленных решений не работал у меня. поэтому вот как мне удалось это исправить и получить электронную почту от 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 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);
    }
}

Ответ 7

Я начал получать это в последнем шаблоне VS 2013.3 и понял, что аутентификация не очень хорошо сочетается с FormsAuthentication, которую я излишне портировал из одного из моих других проектов. Вот что я сделал, чтобы исправить это:

добавлен <system.web><authentication mode="None" />...

добавил <system.webServer><modules><remove name="FormsAuthentication" /></modules>...

Ответ 8

У меня была точно такая же проблема, следуя тому же учебнику. Я решил это, выполнив следующие два шага: 1 > Меню Visual Studio- > Инструменты- > Диспетчер пакетов библиотеки- > Управление пакетами NuGet для решения..., затем установите пакет: Microsoft.Owin.Host.SystemWeb 2 > В том же окне нажмите "Обновить" (левая панель), а затем обновите все пакеты.

Надеюсь, что этот ответ поможет другим людям, которые имеют одинаковую проблему.

Ответ 9

Я получал то же самое.

Я заметил, что мои провайдеры были настроены до того, как был вызван UseExternalSignInCookie, поэтому я просто убедился, что UseExternalSignInCookie вызывается до того, как мои провайдеры настроены и все работает:

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

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

Ответ 10

Я думал, что брошу некоторые примечания для шаблонов Visual Studio 2015/последнего кода котельной плиты для WebAPI 2. Я получал эту проблему с проверкой подлинности Google, но фигурировал ее аналогично facebook и другим социальным входам. У меня был последний Owin, а мои другие пакеты nuget были обновлены. Оказывается с последними готовыми веб-шаблонами 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) вернет null из этого раздела RegisterExternal:

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

Так как это одна из немногих статей, которые приходят для этой ошибки, я решил, что помету свои заметки на своем решении для потомков. (особенно процесс проверки почтальона!)

Итак, чтобы все работало в тестировании: 1) Вызвать URL-адрес api/Account/ExternalLogins следующим образом:

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 из ответа и назовите его. Вы должны получить подсказку/страницу входа в google. (Или я предполагаю, что facebook или твиттер, если это то, что вы создали.)

3) Войдите в систему, и вы перенаправитесь на свою страницу переадресации. У него будет URL-адрес примерно так:

http://localhost:59137/#access_token= d5asC1arCUXaLEMgBS8PT_uwZcTJqC1UZbXblNZ3hMOh3TSKtEXYeKtyKBTv3WmLcaLGGomSvpRSFMfXPxpPvNRgjUVWAiqxtKfv3qWHNqfIMeu5j0eZrJDRAMTrYFgflSbEopAe909a31I4mQnJuvaiITHYPrLmqkm6J88HAVx8F981_q_tflu4A72k3KaB-m2wd0-p1jdQnNMlixM2Wfloh_niUTBIOYUPc1SkKWcZxuI6dzN2Z0PmWHDwzJI8nM8vOuzybJIsxLOyTY1VfzSQ5Qzcll3HhifLPkyZxvXDQ5LHqW1v0_AztsUWkEhW_AJzmw2IaOcTtHCmkmWm1K444okNtOsYfs6HFui0NeY & token_type= носителем & expires_in = 1209600 & состояние = 3FSOd3_n_sEL4QtiELWPG5B2_H3wRjVb75uDjQS16gk1

возьмите маркер (выделено жирным шрифтом) и используйте его как токен-носитель.

Пример почтового менеджера GET api/Account/UserInfo

4) Теперь, поскольку вы не зарегистрированы (но у вас есть токен-носитель), вы можете вызвать POST api/Account/RegisterExternal

введите описание изображения здесь

5) Ответ будет ОК, и если вы посмотрите в своих таблицах AspnetUser, вы увидите, что у вас есть новая запись AspnetUsers и новая запись AspNetUserLogins для Google в качестве поставщика.

Я надеюсь, что это поможет любому, кто пытается заставить этот материал работать!

Ответ 11

Я не включил API Google+ и вернулся с access_denied, когда посмотрел на скрипача. Проблема с API Google+ устранена.