ASP.NET MVC5 OWIN Facebook аутентификация внезапно не работает

Обновление 2017 года!

Проблема, возникшая у меня при публикации исходного вопроса, не имеет ничего общего с недавними изменениями, внесенными Facebook, когда они вынудили всех к версии 2.3 своего API. Для решения этой конкретной проблемы см. Ответ sammy34 ниже . Версия 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 получило доступ к нашему профилю. Когда мы нажимаем «ОК», мы перенаправляемся обратно на наш сайт, но вместо того, чтобы войти, мы просто попадаем на экран входа в систему.

Я прошел этот процесс в режиме отладки, и у меня есть этот 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.

64 голоса | спросил HaukurHaf 13 MaramThu, 13 Mar 2014 01:40:05 +04002014-03-13T01:40:05+04:0001 2014, 01:40:05

11 ответов


0

Хорошо, у меня есть решение проблемы.

Это код, который у меня был ранее в моем файле Startup.Auth.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.Add ("email"); чтобы убедиться, что переменная scope = email присутствовала в первоначальном запросе к Facebook.

Теперь все работает как было!

Я не могу понять, почему это работало раньше, как это было. Единственное объяснение, которое я могу придумать, это то, что Facebook что-то изменил с их стороны.

ответил HaukurHaf 14 MaramFri, 14 Mar 2014 02:34:24 +04002014-03-14T02:34:24+04:0002 2014, 02:34:24
0

Обновление от 22 апреля 2017 года: версия 3.1.0 пакетов Microsoft.Owin. * теперь доступна. Если у вас возникли проблемы после изменения API Facebook с 27 марта 2017 года, попробуйте сначала обновить обновленные пакеты NuGet. В моем случае они решили проблему (отлично работают на наших производственных системах).

Оригинальный ответ:

В моем случае я проснулся 28 марта 2017 года и обнаружил, что аутентификация Facebook нашего приложения внезапно перестала работать. Мы ничего не изменили в коде приложения.

Оказывается, что 27 марта 2017 года Facebook «принудительно обновил» свой API-интерфейс для графов с версии 2.2 до 2.3. Одним из отличий этих версий API-а является то, что конечная точка Facebook /oauth/access_token больше не отвечает телом с закодированной формой, а вместо этого JSON.

Теперь в промежуточном программном обеспечении Owin мы находим метод protected override FacebookAuthenticationHandler.AuthenticateCoreAsync(), который анализирует тело ответа как форму и впоследствии использует access_token из проанализированной формы. Излишне говорить, что проанализированная форма пуста, поэтому access_token также пусто, вызывая access_denied ошибка дальше по цепочке.

Чтобы быстро это исправить, мы создали класс-оболочку для ответа Oauth Facebook

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, содержащее эквивалентную закодированную информацию из ответа JSON Facebook. Обратите внимание, что этот код использует популярный пакет Json.Net.

С помощью этого пользовательского обработчика проблемы, похоже, решены (хотя мы еще не развернули его в prod:)).

Надеюсь, это спасет кого-то другого, проснувшегося сегодня от подобных проблем!

Кроме того, если у кого-то есть более чистое решение для этого, я хотел бы знать!

ответил sammy34 28 MaramTue, 28 Mar 2017 10:37:50 +03002017-03-28T10:37:50+03:0010 2017, 10:37:50
0

Заметил эту проблему вчера. Facebook больше не поддерживает Microsoft.Owin.Security.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

ответил JayPi 29 MaramWed, 29 Mar 2017 00:49:29 +03002017-03-29T00:49:29+03:0012 2017, 00:49:29
0

У меня была такая же проблема с аутентификацией Google. У меня сработало следующее: Изменения в Google OAuth 2.0 и обновления в промежуточном ПО Google для версии 3.0.0 RC

ответил Maarten Docter 28 AM00000010000000231 2014, 01:16:02
0

Последнее обновление 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 WebPages устарела (по крайней мере, для FB), и вам, вероятно, придется найти другое решение

ответил RedTornado 28 MarpmTue, 28 Mar 2017 21:30:32 +03002017-03-28T21:30:32+03:0009 2017, 21:30:32
0

Вышеуказанные решения не сработали для меня. В конце концов, это было похоже на сессию. «Пробуждая» сеанс в предыдущем вызове, он больше не будет возвращать ноль из 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, у меня долгое время нормально работала сторонняя аутентификация, а потом внезапно она прекратилась. Я полагаю, это произошло из-за изменений, внесенных в мой код при настройке сеанса для использования Redis Cache в Azure.

ответил Joel Gallagher 28 52014vEurope/Moscow11bEurope/MoscowFri, 28 Nov 2014 02:50:52 +0300 2014, 02:50:52
0

У меня тоже была эта проблема, но она не была вызвана настройкой области действия. Мне потребовалось много времени, чтобы понять это, но в конечном итоге я понял, что установил собственный регистратор, установив следующее в OwinStartup.Configuration(IAppBuilder app)

app.SetLoggerFactory(new LoggerFactory()); 
// Note: LoggerFactory is my own custom ILoggerFactory

Это вывело следующее:

  

2014-05-31 21: 14: 48,508 [8] ОШИБКА
  Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
  [(null)] - 0x00000000 - Ошибка аутентификации
  System.Net.Http.HttpRequestException: при отправке произошла ошибка   запрос. --- > System.Net.WebException: удаленное имя не может
  быть решенным: 'graph.facebook.com' на
  System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult)
  в System.Net.Http.HttpClientHandler.GetResponseCallback (IAsyncResult   ar) --- конец трассировки стека внутренних исключений --- в
  System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Task
  задача) на
  System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task   задача) в System.Runtime.CompilerServices.TaskAwaiter`1.GetResult ()   на авто   Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext ()

На основании описанного выше стека вызовов я обнаружил, что моей виртуальной машине Azure не удалось разрешить graph.facebook.com. Все, что мне нужно было сделать, чтобы это исправить - запустить ipconfig /registerdns, и я все исправил ...

ответил jt000 1 J0000006Europe/Moscow 2014, 06:04:21
0

Я работал над решением в течение трех дней. А я только что нашел его на github САШ /AspNetKatana /вопросы /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;
}
ответил affair 30 MarpmThu, 30 Mar 2017 19:09:36 +03002017-03-30T19:09:36+03:0007 2017, 19:09:36
0

Убедитесь, что вы получаете внешнее подключение к Интернету из своего приложения. Если нет, исправьте внешнее подключение к Интернету. Моя проблема заключалась в том, что я использовал экземпляр ECS AWS, который внезапно перестал подключаться к Интернету. Мне потребовалось некоторое время, чтобы понять, что это была проблема.

ответил Ronen Festinger 19 Maypm16 2016, 23:05:04
0

Это сводило меня с ума. Все работало, пока я не развернулся в своей промежуточной среде. Я использовал Microsoft.Owin.Security.Facebook версии 3.0.1 от Nuget. Обновил его до предварительной версии 3.1.0 от Nuget, и я больше не получаю сообщение об ошибке отказа в доступе ...

ответил Ewert 2 PMpSun, 02 Apr 2017 12:12:42 +030012Sunday 2017, 12:12:42
0

Несмотря на то, что я сделал все, что сказал sammy34 , у меня это не сработало. Я был в одной точке с HaukurHaf : когда я делаю apirequest вручную в браузере, он отлично работает, но если я использую мое приложение mvc, GetExternalLoginInfoAsync() всегда возвращает ноль .

Поэтому я изменил несколько строк в кодах sammy34 , как в этом комментарии: https: //stackoverflow .com /а /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

ответил gld-R 6 PMpThu, 06 Apr 2017 20:50:08 +030050Thursday 2017, 20:50:08

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132