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