Authentification ASP.NET MVC5 OWIN Facebook ne fonctionne pas soudainement

Mise à jour 2017!

Le problème que j’avais quand j’ai posté la question initiale n’a rien à voir avec les récents changements effectués par Facebook quand ils ont forcé tout le monde à la version 2.3 de leur API. Pour une solution à ce problème spécifique, voir la réponse de sammy34 ci-dessous . La version 2.3 du noeud final / oauth / access_token renvoie maintenant JSON à la place des valeurs encodées dans un formulaire

Pour des raisons historiques, voici ma question / question initiale:

J’ai une application Web MVC5 qui utilise la prise en charge intégrée pour l’authentification via Facebook et Google. Lorsque nous avons créé cette application il y a quelques mois, nous avons suivi ce tutoriel: http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and -google-oauth2-and-openid-sign-on et tout a bien fonctionné.

Maintenant, tout à coup, l’authentification Facebook a cessé de fonctionner. L’authentification Google fonctionne toujours très bien.

Description du problème: Nous cliquons sur le lien pour nous connecter via Facebook, nous sums redirigés vers Facebook où nous sums invités à autoriser notre application Facebook à accéder à notre profil. Lorsque nous cliquons sur “OK”, nous sums redirigés vers notre site, mais au lieu d’être connectés, nous nous retrouvons simplement à l’écran de connexion.

Je suis passé par ce processus en mode débogage et j’ai ce ActionResult dans mon contrôleur de compte conformément au tutoriel mentionné ci-dessus:

// GET: /Account/ExternalLoginCallback [AllowAnonymous] public async Task ExternalLoginCallback(ssortingng returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } ............ 

Lorsque vous parcourez le code et que vous revenez de Facebook, l’object loginInfo est toujours NULL, ce qui provoque la redirection de l’utilisateur vers la connexion.

Afin de comprendre ce qui se passe réellement dans les coulisses, j’ai installé Fiddler et surveillé le trafic HTTP. Ce que j’ai découvert, c’est qu’en cliquant sur “OK” dans la boîte de dialog de permission de Facebook, Facebook redirige vers notre application avec cette URL:

 https://localhost/signin-facebook?code= 

Cette URL n’est pas un fichier réel et probablement manipulée par un contrôleur / gestionnaire intégré à ce framework OWIN. Très probablement, il se connecte à Facebook en utilisant le code donné pour interroger des informations sur l’utilisateur qui tente de se connecter. Maintenant, le problème est que, au lieu de le faire, nous sums redirigés vers:

 /Account/ExternalLoginCallback?error=access_denied 

Je suis sûr que c’est quelque chose que fait Facebook, c’est-à-dire qu’au lieu de nous fournir les données utilisateur, il nous redirige avec ce message d’erreur.

Cela provoque le AuthenticationManager.GetExternalLoginInfoAsync(); échouer et toujours retourner NULL.

Je suis complètement à court d’idées. Pour autant que nous sachions, nous n’avons rien changé de notre côté.

J’ai essayé de créer une nouvelle application Facebook, j’ai essayé de suivre à nouveau le tutoriel mais j’ai toujours le même problème.

Toutes les idées sont les bienvenues!

Mettre à jour!

OK, ça me rend fou! J’ai maintenant parcouru manuellement les étapes requirejses pour effectuer l’authentification et tout fonctionne parfaitement lorsque je le fais. Pourquoi est-ce que cela ne fonctionne pas lorsque vous utilisez le matériel MVC5 Owin?

C’est ce que j’ai fait:

  // 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= Now, I grabbed the code I got and used it in the URL below: // Step 2 - opened this URL in a browser, and successfully resortingeved an access token https://graph.facebook.com/oauth/access_token?client_id=619359858118523&redirect_uri=https://localhost/signin-facebook&client_secret=&code= // 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= 

Pas d’erreurs, tout fonctionne bien! Alors pourquoi est-ce que ça ne marche pas quand on utilise le matériel MVC5 Owin? Il y a évidemment quelque chose qui ne va pas dans l’implémentation d’OWin.

Mise à jour du 22 avril 2017: la version 3.1.0 des packages Microsoft.Owin. * Est désormais disponible. Si vous rencontrez des problèmes après les modifications de l’API de Facebook à partir du 27 mars 2017, essayez d’abord les packages NuGet mis à jour. Dans mon cas, ils ont résolu le problème (fonctionne bien sur nos systèmes de production).

Réponse originale:

Dans mon cas, je me suis réveillé le 28 mars 2017 pour découvrir que l’authentification Facebook de notre application avait soudainement cessé de fonctionner. Nous n’avions rien changé dans le code de l’application.

Il s’avère que Facebook a effectué une “mise à niveau forcée” de leur API graphique de la version 2.2 à la version 2.3 le 27 mars 2017. L’une des différences entre ces versions de l’API semble être que le noeud final Facebook /oauth/access_token ne répond plus un corps de contenu codé par formulaire, mais avec JSON à la place.

Maintenant, dans le middleware Owin, nous trouvons que la méthode protected override FacebookAuthenticationHandler.AuthenticateCoreAsync() , qui parsing le corps de la réponse en tant que formulaire et utilise ensuite le access_token du formulaire analysé. Inutile de dire que le formulaire analysé est vide, de sorte que access_token est également vide, provoquant une erreur access_denied plus loin dans la chaîne.

Pour résoudre ce problème rapidement, nous avons créé une classe wrapper pour la réponse Facebook Oauth

 public class FacebookOauthResponse { public ssortingng access_token { get; set; } public ssortingng token_type { get; set; } public int expires_in { get; set; } } 

Ensuite, dans OwinStart, nous avons ajouté un gestionnaire de canal arrière personnalisé …

  app.UseFacebookAuthentication(new FacebookAuthenticationOptions { AppId = "hidden", AppSecret = "hidden", BackchannelHttpHandler = new FacebookBackChannelHandler() }); 

… où le gestionnaire est défini comme:

 public class FacebookBackChannelHandler : HttpClientHandler { protected override async System.Threading.Tasks.Task 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.ReadAsSsortingngAsync(); var facebookOauthResponse = JsonConvert.DeserializeObject(content); var outgoingQuerySsortingng = HttpUtility.ParseQuerySsortingng(ssortingng.Empty); outgoingQuerySsortingng.Add(nameof(facebookOauthResponse.access_token), facebookOauthResponse.access_token); outgoingQuerySsortingng.Add(nameof(facebookOauthResponse.expires_in), facebookOauthResponse.expires_in + ssortingng.Empty); outgoingQuerySsortingng.Add(nameof(facebookOauthResponse.token_type), facebookOauthResponse.token_type); var postdata = outgoingQuerySsortingng.ToSsortingng(); var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK) { Content = new SsortingngContent(postdata) }; return modifiedResult; } } 

Fondamentalement, le gestionnaire crée simplement un nouveau HttpResponseMessage contenant les informations encodées en forme équivalentes provenant de la réponse JSON de Facebook. Notez que ce code utilise le package Json.Net populaire.

Avec ce gestionnaire personnalisé, les problèmes semblent être résolus (bien que nous soyons encore à déployer pour prod :)).

Espérons que cela sauve quelqu’un d’autre qui se réveille aujourd’hui avec des problèmes similaires!

En outre, si quelqu’un a une solution plus propre à cela, j’aimerais en savoir plus!

Noté ce problème hier. Facebook ne prend plus en charge Microsoft.Owin.Security.Facebook version 3.0.1. Pour moi, cela a fonctionné pour installer la version 3.1.0. Pour mettre à jour vers la version 3.1.0, exécutez la commande Install-Package Microsoft.Owin.Security.Facebook dans la console Gestionnaire de packages: https://www.nuget.org/packages/Microsoft.Owin.Security.Facebook

Ok, j’ai une solution au problème.

C’est le code que j’avais précédemment dans mon fichier Startup.Auth.cs:

 var x = new FacebookAuthenticationOptions(); //x.Scope.Add("email"); x.AppId = "1442725269277224"; x.AppSecret = ""; 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); 

Notez comment le

 x.Scope.Add("email") 

ligne a été commentée, mais je demande toujours le courrier électronique plus tard dans le gestionnaire OnAuthenticated? Oui, c’est vrai. Pour une raison quelconque, cela a parfaitement fonctionné pendant quelques semaines.

Ma solution consistait simplement à décommenter le x.Scope.Add (“email”); line pour vous assurer que la variable scope = email était présente dans la demande initiale adressée à Facebook.

Maintenant tout fonctionne comme il l’a fait!

Je ne peux pas comprendre pourquoi cela a fonctionné avant comme c’était. La seule explication que je puisse trouver est que Facebook a changé quelque chose.

J’ai eu ce même problème avec l’authentification Google. Les points suivants ont fonctionné pour moi: Modifications apscopes à Google OAuth 2.0 et mises à jour du middleware Google pour la version 3.0.0 RC

La dernière mise à jour de Facebook a eu lieu le 2015-02-09 ( https://www.nuget.org/packages/Microsoft.AspNet.WebPages.OAuth/ )

La dernière version de l’API à ce moment-là était la version 2.2. La version 2.2 a expiré le 25 mars 2017, ce qui est une coïncidence lorsque le problème a commencé. ( https://developers.facebook.com/docs/apps/changelog )

Je suppose que Facebook a probablement mis à jour automatiquement l’API et que la bibliothèque MS OAUTH ne peut plus parsingr la nouvelle réponse.

tldr: La bibliothèque Microsoft WebPages OAuth est obsolète (pour FB au moins) et vous devrez probablement trouver une autre solution

J’ai eu ce problème aussi, mais cela n’a pas été causé par le réglage de la scope. Il m’a fallu beaucoup de temps pour comprendre cela, mais ce qui m’a finalement convaincu était de définir un enregistreur personnalisé en définissant les parameters suivants dans OwinStartup.Configuration(IAppBuilder app) .

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

Cela a généré ce qui suit:

2014-05-31 21: 14: 48,508 [8] ERREUR
Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware
[(null)] – 0x00000000 – L’authentification a échoué
System.Net.Http.HttpRequestException: une erreur s’est produite lors de l’envoi de la demande. —> System.Net.WebException: le nom distant n’a pas pu
être résolu: ‘graph.facebook.com’ à
System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback (IAsyncResult ar) — Fin de trace de la stack d’exceptions internes — at
System.Runtime.ComstackrServices.TaskAwaiter.ThrowForNonSuccess (tâche
tâche) à
System.Runtime.ComstackrServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (tâche de tâche) sur System.Runtime.ComstackrServices.TaskAwaiter`1.GetResult () à
Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.d__0.MoveNext ()

Sur la base de la stack d’appels ci-dessus, j’ai constaté que ma machine virtuelle Azure n’était pas en mesure de résoudre graph.facebook.com. Tout ce que j’avais à faire pour résoudre ce problème était de lancer “ipconfig / registerdns” et tout était corrigé …

Les solutions ci-dessus n’ont pas fonctionné pour moi. Au final, cela semblait être lié à la session. En “réveillant” la session lors de l’appel précédent, elle ne renverrait plus null à partir de GetExternalLoginInfoAsync ().

  [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult ExternalLogin(ssortingng provider, ssortingng returnUrl) { Session["WAKEUP"] = "NOW!"; // Request a redirect to the external login provider return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl })); } 

À l’instar de l’OP, la tierce partie fonctionnait correctement pendant longtemps, puis soudainement elle s’est arrêtée. Je pense que cela est dû aux modifications apscopes à mon code lorsque j’ai configuré la session pour utiliser Redis Cache sur Azure.

Je travaille sur la solution depuis trois jours. Et je viens de le trouver sur 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 = ssortingng.Format("urn:facebook:{0}", claim.Key); var claimType = ssortingng.Format("{0}", claim.Key); ssortingng claimValue = claim.Value.ToSsortingng(); if (!context.Identity.HasClaim(claimType, claimValue)) context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaSsortingng", "Facebook")); } return Task.FromResult(0); } }; app.UseFacebookAuthentication(facebookOptions); 

Et pour obtenir des valeurs

 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; } 

J’ai eu ce problème également. J’ai lu de nombreux articles et sujets pendant deux jours, y compris celui-ci. Aucun d’entre eux n’a travaillé pour moi. La connexion à Facebook avait parfaitement fonctionné avant le déploiement, mais ce n’est pas le cas après. J’ai découvert que quelque chose n’allait pas avec ma connexion Internet VPS, alors j’ai essayé NextVPN sur mon VPS. Mais lorsque j’ai tenté de me connecter via mon ordinateur, il n’y avait pas de nouvelle connexion dans ProxiFire interne de NextVPN. Enfin, l’installation d’OpenVPN sur mon VPS a résolu le problème.

Vérifiez que vous obtenez une connexion Internet extérieure à partir de votre application. Sinon, corrigez votre connexion Internet extérieure. Mon problème était que j’utilisais une instance AWS EC2 qui a soudainement cessé de se connecter à Internet. Cela m’a pris du temps pour réaliser que c’était le problème.

Cela m’a rendu fou. Tout fonctionnait jusqu’à ce que je sois déployé dans mon environnement de mise en scène. J’utilisais Microsoft.Owin.Security.Facebook version 3.0.1 de Nuget. Mis à jour à la version préliminaire 3.1.0 de Nuget et je n’ai plus l’erreur d’access refusé …

Même si j’ai fait tout ce que sammy34 a dit, cela n’a pas fonctionné pour moi. J’étais au même point avec HaukurHaf : Quand je fais de l’apirequest manuellement sur le navigateur, cela fonctionne parfaitement, mais si j’utilise mon application GetExternalLoginInfoAsync() , GetExternalLoginInfoAsync() retourne toujours null .

J’ai donc changé quelques lignes sur les codes de sammy34 comme sur ce commentaire: https://stackoverflow.com/a/43148543/7776015

Remplacé:

 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; } 

Au lieu de:

 var result = await base.SendAsync(request, cancellationToken); if (!request.RequestUri.AbsolutePath.Contains("access_token")) return result; 

Et ajouté cette ligne dans mon FacebookAuthenticationOptions :

 UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name,picture" 

et maintenant ça marche. (champs et parameters optionnels)

Remarque: je n’ai pas mis à jour Microsoft.Owin.Security.Facebook