Проверка подлинности ASP.NET MVC5 OWIN в Facebook вдруг не работает
обновить 2017!
проблема, с которой я столкнулся, когда я опубликовал исходный вопрос, не имеет ничего общего с недавними изменениями Facebook, сделанными, когда они заставили всех перейти на версию 2.3 своего API. Решение этой конкретной проблемы см. В разделе ответ сэмми34 ниже. Версия 2.3 конечной точки/oauth / access_token теперь возвращает JSON вместо закодированных в форме значений
по историческим причинам, вот мой оригинал вопрос:
у меня есть веб-приложение MVC5, которое использует встроенную поддержку аутентификации через Facebook и Google. Когда мы создали это приложение несколько месяцев назад, мы следовали этому учебнику: http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on и все работало отлично.
теперь, внезапно, аутентификация Facebook просто перестала работать все вместе. Этот Аутентификация Google по-прежнему отлично работает.
описание проблемы: мы нажимаем ссылку для подключения с помощью Facebook, мы перенаправлены на Facebook, где нам будет предложено, если мы не хотим, чтобы наше приложение Facebook доступ к нашему профилю. Когда мы нажимаем "OK", мы перенаправляемся обратно на наш сайт, но вместо того, чтобы войти в систему, мы просто оказываемся на экране входа в систему.
Я прошел через этот процесс в режиме отладки, и у меня есть этот ActionResult в моем контроллере учетной записи в соответствии с учебник, упомянутый выше:
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
............
при переходе через код и при возвращении из Facebook объект loginInfo всегда имеет значение NULL,что приводит к перенаправлению пользователя обратно к логину.
чтобы понять, что на самом деле происходит за кулисами, я установил Fiddler и контролировал HTTP-трафик. Я обнаружил, что при нажатии кнопки "ОК" в диалоговом окне разрешения Facebook Facebook перенаправляет обратно в наше приложение с помощью этого URL:
https://localhost/signin-facebook?code=<access-token>
этот URL-адрес не является фактическим файлом и, вероятно, обрабатывается каким-то контроллером/обработчиком, встроенным в эту структуру OWIN, я предполагаю. Скорее всего, он подключается обратно к Facebook, используя данный код для запроса информации о пользователе, который пытается войти в систему. Теперь проблема в том, что вместо этого мы перенаправляемся на:
/Account/ExternalLoginCallback?error=access_denied
что я уверен, что это то, что делает Facebook, то есть вместо того, чтобы давать нам данные пользователя, он перенаправляет нас вернуться с этим сообщением об ошибке.
в этом случае AuthenticationManager.GetExternalLoginInfoAsync();
для сбоя и всегда возвращать NULL.
у меня совершенно нет идей. Насколько нам известно, мы ничего не меняли с нашей стороны.
Я попытался создать новое приложение Facebook, я попытался снова следовать учебнику, но у меня всегда есть одна и та же проблема.
любые идеи приветствуются!
обновление!
ОК, это сводит меня с ума! Теперь я вручную прошел шаги, необходимые для выполнения аутентификации, и все отлично работает, когда я это делаю. Почему это не работает при использовании материала Mvc5 Owin?
вот что я сделал:
// Step 1 - Pasted this into a browser, this returns a code
https://www.facebook.com/dialog/oauth?response_type=code&client_id=619359858118523&redirect_uri=https%3A%2F%2Flocalhost%2Fsignin-facebook&scope=&state=u9R1m4iRI6Td4yACEgO99ETQw9NAos06bZWilJxJrXRn1rh4KEQhfuEVAq52UPnUif-lEHgayyWrsrdlW6t3ghLD8iFGX5S2iUBHotyTqCCQ9lx2Nl091pHPIw1N0JV23sc4wYfOs2YU5smyw9MGhcEuinvTAEql2QhBowR62FfU6PY4lA6m8pD3odI5MwBYOMor3eMLu2qnpEk0GekbtTVWgQnKnH6t1UcC6KcNXYY
I was redirected back to localhost (which I had shut down at this point to avoid being redirected immediately away). The URL I was redirected to is this:
https://localhost/signin-facebook?code=<code-received-removed-for-obvious-reasons>
Now, I grabbed the code I got and used it in the URL below:
// Step 2 - opened this URL in a browser, and successfully retrieved an access token
https://graph.facebook.com/oauth/access_token?client_id=619359858118523&redirect_uri=https://localhost/signin-facebook&client_secret=<client-secret>&code=<code-from-step-1>
// Step 3 - Now I'm able to query the facebook graph using the access token from step 2!
https://graph.facebook.com/me?access_token=<access-token-from-step-2>
нет ошибок, все работает отлично! Тогда почему, черт возьми, это не работает при использовании материала Mvc5 Owin? Очевидно, что-то не так с реализацией OWin.
12 ответов:
обновление 22 апреля 2017: версия 3.1.0 от Microsoft.Долг.* пакеты теперь доступны. Если у вас возникли проблемы после изменения API Facebook с 27 марта 2017 года, сначала попробуйте обновленные пакеты NuGet. В моем случае они решили проблему (отлично работают на наших производственных системах).
оригинальный ответ:
в моем случае я проснулся 28 марта 2017 года, чтобы обнаружить, что аутентификация Facebook нашего приложения внезапно перестал работать. Мы ничего не изменили в коде приложения.
оказывается, что Facebook сделал "принудительное обновление" своего API graph с версии 2.2 до 2.3 27 марта 2017 года. Одним из отличий в этих версиях API, по-видимому, является то, что конечная точка Facebook
/oauth/access_token
отвечает больше не с закодированным в форме телом контента, а с JSON вместо этого.теперь, в промежуточном программном обеспечении Owin, мы находим метод
protected override FacebookAuthenticationHandler.AuthenticateCoreAsync()
, который анализирует тело ответа как форму и впоследствии используетaccess_token
из разобранной форме. Излишне говорить, что разбираемая форма пуста, поэтомуaccess_token
также пуст, вызываяaccess_denied
ошибка дальше по цепочке.чтобы исправить это быстро, мы создали класс-оболочку для ответа Facebook Oauth
public class FacebookOauthResponse { public string access_token { get; set; } public string token_type { get; set; } public int expires_in { get; set; } }
затем в OwinStart мы добавили пользовательский обработчик обратного канала...
app.UseFacebookAuthentication(new FacebookAuthenticationOptions { AppId = "hidden", AppSecret = "hidden", BackchannelHttpHandler = new FacebookBackChannelHandler() });
...где обработчик определяется как:
public class FacebookBackChannelHandler : HttpClientHandler { protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { var result = await base.SendAsync(request, cancellationToken); if (!request.RequestUri.AbsolutePath.Contains("access_token")) return result; // For the access token we need to now deal with the fact that the response is now in JSON format, not form values. Owin looks for form values. var content = await result.Content.ReadAsStringAsync(); var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content); var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty); outgoingQueryString.Add(nameof(facebookOauthResponse.access_token), facebookOauthResponse.access_token); outgoingQueryString.Add(nameof(facebookOauthResponse.expires_in), facebookOauthResponse.expires_in + string.Empty); outgoingQueryString.Add(nameof(facebookOauthResponse.token_type), facebookOauthResponse.token_type); var postdata = outgoingQueryString.ToString(); var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(postdata) }; return modifiedResult; } }
в принципе, обработчик просто создает новый HttpResponseMessage, содержащий эквивалентную закодированную в форме информацию из ответа Facebook JSON. Обратите внимание, что этот код использует популярные Json.Net пакет.
С помощью этого пользовательского обработчика проблемы, похоже, решены (хотя нам еще предстоит развернуть prod :)).
надеюсь, что это спасает кого-то еще просыпаться сегодня с подобными проблемами!
кроме того, если у кого-то есть более чистое решение для этого, я хотел бы знать!
заметил эту проблему. Facebook не поддерживает Microsoft.Долг.Безопасность.Facebook версии 3.0.1 больше нет. Для меня это сработало, чтобы установить версию 3.1.0. Для обновления до версии 3.1.0 выполните команду
Install-Package Microsoft.Owin.Security.Facebook
в консоли диспетчера пакетов:https://www.nuget.org/packages/Microsoft.Owin.Security.Facebook
Ок у меня есть решение проблемы.
это код, который я имел ранее в мой стартап.Автор.cs файл:
var x = new FacebookAuthenticationOptions(); //x.Scope.Add("email"); x.AppId = "1442725269277224"; x.AppSecret = "<secret>"; x.Provider = new FacebookAuthenticationProvider() { OnAuthenticated = async context => { //Get the access token from FB and store it in the database and //use FacebookC# SDK to get more information about the user context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken",context.AccessToken)); context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:name", context.Name)); context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email)); } }; x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie; app.UseFacebookAuthentication(x);
обратите внимание, как
x.Scope.Add("email")
строка была закомментирована, но все же я запрашиваю электронную почту позже в обработчике OnAuthenticated? Да, это так. По какой-то причине это работало безупречно в течение нескольких недель.
мое решение состояло в том, чтобы просто раскомментировать X.Scope.Добавить ("электронная почта"); строка, чтобы убедиться, что область=электронная почта переменная присутствовала в первоначальном запросе к Facebook.
теперь все работает как было!
Я не могу понять, почему это работало раньше, как это было. Единственное объяснение, которое я могу придумать, это то, что Facebook изменил что-то на своем конце.
У меня была такая же проблема с аутентификацией Google. Для меня сработало следующее:изменения в Google OAuth 2.0 и обновления в Google middleware для 3.0.0 RC release
последнее обновление Facebook было на 2015-02-09 (https://www.nuget.org/packages/Microsoft.AspNet.WebPages.OAuth/)
последней версией API на тот момент была версия 2.2. Срок действия версии 2.2 истек 25 марта 2017 года, что совпало с началом проблемы. (https://developers.facebook.com/docs/apps/changelog)
Я предполагаю, что Facebook, вероятно, автоматически обновил API, и теперь библиотека MS OAUTH не может чтобы разобрать новый ответ.
tldr: библиотека OAuth веб-страниц Microsoft устарела (по крайней мере для FB), и вам, вероятно, придется найти другое решение
у меня тоже была эта проблема, но она не была вызвана настройкой области. Мне потребовалось много времени, чтобы понять это, но то, что наконец-то просветил по настройки регистратора, установив следующий в
OwinStartup.Configuration(IAppBuilder app)
.app.SetLoggerFactory(new LoggerFactory()); // Note: LoggerFactory is my own custom ILoggerFactory
это выводило следующее:
2014-05-31 21:14:48,508 [8] Ошибка
Microsoft.Долг.Безопасность.Cookies.CookieAuthenticationMiddleware
[(null)] - 0x00000000 - ошибка аутентификации
Системы.Чистая.Протоколу HTTP.HttpRequestException: произошла ошибка при отправке запрос. --- >System. Net. WebException: удаленное имя не может
будьте решительны: 'graph.facebook.com-в
Системы.Чистая.Класса HttpWebRequest.Метода endgetresponse(объекта iasyncresult asyncresult, к)
в системе.Чистая.Протоколу HTTP.HttpClientHandler.GetResponseCallback(Объекта Iasyncresult Арканзас) --- конец внутреннее исключение трассировки стека - в
Система.Во время выполнения.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Task
задача) в
Система.Во время выполнения.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Задача задача) в системе.Во время выполнения.CompilerServices.TaskAwaiter`1.Метод getresult() в
Microsoft.Долг.Безопасность.Фейсбук.FacebookAuthenticationHandler.Д__0.MoveNext ()на основе вышеприведенного стека вызовов я обнаружил, что моя виртуальная машина Azure не смогла разрешить graph.facebook.com. все, что мне нужно было сделать, чтобы исправить это, было запустить "ipconfig /registerdns", и я все исправил...
вышеуказанные решения не работают для меня. В конце концов, это, казалось, было связано с сессией. При "пробуждении" сеанса в предыдущем вызове он больше не будет возвращать null из GetExternalLoginInfoAsync()
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult ExternalLogin(string provider, string returnUrl) { Session["WAKEUP"] = "NOW!"; // Request a redirect to the external login provider return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); }
Как и OP, у меня был 3rd party auth, работающий нормально в течение длительного времени, а затем внезапно он остановился. Я верю, что это произошло из-за изменений, внесенных в мой код, когда я настроил сеанс для использования кэша Redis в Azure.
Я работаю над решением в течение трех дней. И я только что нашел его на github(https://github.com/aspnet/AspNetKatana/issues/38#issuecomment-290400987)
var facebookOptions = new FacebookAuthenticationOptions() { AppId = "xxxxx", AppSecret = "xxxxx", }; // Set requested scope facebookOptions.Scope.Add("email"); facebookOptions.Scope.Add("public_profile"); // Set requested fields facebookOptions.Fields.Add("email"); facebookOptions.Fields.Add("first_name"); facebookOptions.Fields.Add("last_name"); facebookOptions.Provider = new FacebookAuthenticationProvider() { OnAuthenticated = (context) => { // Attach the access token if you need it later on for calls on behalf of the user context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken)); foreach (var claim in context.User) { //var claimType = string.Format("urn:facebook:{0}", claim.Key); var claimType = string.Format("{0}", claim.Key); string claimValue = claim.Value.ToString(); if (!context.Identity.HasClaim(claimType, claimValue)) context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook")); } return Task.FromResult(0); } }; app.UseFacebookAuthentication(facebookOptions);
и получить значения
var info = await AuthenticationManager.GetExternalLoginInfoAsync(); if (info != null) { var firstName = info.ExternalIdentity.Claims.First(c => c.Type == "first_name").Value; var lastName = info.ExternalIdentity.Claims.First(c => c.Type == "last_name").Value; }
У меня тоже эта проблема. Я прочитал много статей и тем в течение двух дней, включая это. Никто из них не работал на меня. Вход в систему Facebook отлично работал до развертывания, но после этого этого не произошло. Я узнал, что что-то не так с моим подключением к интернету VPS, поэтому я попробовал NextVPN на моем VPS. Но когда я попытался войти через свой компьютер, не было никакого нового соединения во внутреннем ProxiFire NextVPN. Наконец, установка OpenVPN на моем VPS решила проблему.
проверьте, что вы получаете внешнее подключение к интернету из вашего приложения. Если нет, исправьте внешнее подключение к интернету. Моя проблема заключалась в том, что я использовал экземпляр EC2 AWS, который внезапно перестал подключаться к интернету. Мне потребовалось некоторое время, чтобы понять, в чем проблема.
это сводило меня с ума. Все работало, пока я не развернулся в своей промежуточной среде. Я использовал Microsoft.Долг.Безопасность.Facebook версии 3.0.1 от Nuget. Обновил его до предварительной версии 3.1.0 от Nuget, и я больше не получал ошибку отказа в доступе...
хоть я и сделал все что sammy34 сказал, это не сработало для меня. Я был в одной точке с HaukurHaf: когда я делаю apirequest вручную в браузере, он отлично работает, но если я использую свое приложение mvc,
GetExternalLoginInfoAsync()
всегда возвращает null.поэтому я изменил некоторые строки на sammy34'коды как на этот комментарий: https://stackoverflow.com/a/43148543/7776015
заменить:
if (!request.RequestUri.AbsolutePath.Contains("/oauth")) { request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token")); } var result = await base.SendAsync(request, cancellationToken); if (!request.RequestUri.AbsolutePath.Contains("/oauth")) { return result; }
вместо:
var result = await base.SendAsync(request, cancellationToken); if (!request.RequestUri.AbsolutePath.Contains("access_token")) return result;
и добавил эту строку в моем
FacebookAuthenticationOptions
:UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name,picture"
и теперь он работает.(поля и что параметры необязательные)
примечание: Я не обновлял
Microsoft.Owin.Security.Facebook