c# asp - Warum gibt der neue fb api 2.4 bei MVC 5 mit Identity und oauth 2 null Emails zurück?




net login (6)

Um dies zu beheben, müssen Sie Facebook SDK von NuGet-Paketen installieren.

In Startdatei

 app.UseFacebookAuthentication(new FacebookAuthenticationOptions
            {
                AppId = "XXXXXXXXXX",
                AppSecret = "XXXXXXXXXX",
                Scope = { "email" },
                Provider = new FacebookAuthenticationProvider
                {
                    OnAuthenticated = context =>
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
                        return Task.FromResult(true);
                    }
                }
            });

In Controller oder Helfer

var identity = AuthenticationManager.GetExternalIdentity(DefaultAuthenticationTypes.ExternalCookie);
var accessToken = identity.FindFirstValue("FacebookAccessToken");
var fb = new FacebookClient(accessToken);
dynamic myInfo = fb.Get("/me?fields=email,first_name,last_name,gender"); // specify the email field

Mit diesem können Sie die EmailId, First-Nachname, Geschlecht.

Sie können auch Ihre zusätzlichen erforderlichen Eigenschaften in dieser Abfragezeichenfolge hinzufügen.

Hoffe, das wird jemandem helfen.

Alles funktionierte perfekt, bis es von fb auf 2.4 aktualisiert wurde (ich hatte 2.3 in meinem vorherigen Projekt).

Heute, wenn ich eine neue Anwendung auf fb-Entwicklern hinzufügen, bekomme ich es mit API 2.4.

Das Problem: Jetzt bekomme ich null Email von fb ( loginInfo.email = null ).

Natürlich habe ich überprüft, dass die Benutzer-E-Mail im öffentlichen Status auf fb-Profil ist,

und ich ging über das loginInfo Objekt, fand aber keine andere E-Mail-Adresse.

und ich google das, aber habe keine Antwort gefunden.

bitte jede Hilfe .. Ich bin irgendwie verloren ..

Vielen Dank,

Mein ursprünglicher Code (der auf 2.3 api funktioniert hat):

In der AccountController.cs:

//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    if (loginInfo == null)
    {
        return RedirectToAction("Login");
    }
    //A way to get fb details about the log-in user: 
    //var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:first_name");  <--worked only on 2.3
    //var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:name"); <--works on 2.4 api

    // Sign in the user with this external login provider if the user already has a login
    var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
    switch (result)
    {
        case SignInStatus.Success:
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
        case SignInStatus.Failure:
        default:
            // If the user does not have an account, then prompt the user to create an account
            ViewBag.ReturnUrl = returnUrl;
            ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
            return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });  //<---DOESN'T WORK. loginInfo.Email IS NULL
    }
}

In der Startup.Auth.cs:

    Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions fbOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
    {
        AppId = System.Configuration.ConfigurationManager.AppSettings.Get("FacebookAppId"),
        AppSecret = System.Configuration.ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
    };
    fbOptions.Scope.Add("email");
    fbOptions.Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
        {
            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);
                string claimValue = claim.Value.ToString();
                if (!context.Identity.HasClaim(claimType, claimValue))
                    context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));

            }
            return System.Threading.Tasks.Task.FromResult(0);
        }
    };
    fbOptions.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
    app.UseFacebookAuthentication(fbOptions);

Lesen Sie das changelog , das ist von Entwurf. Sie müssen explizit die Felder und Kanten anfordern, die in der Antwort neu abgestimmt werden sollen:

Deklarative Felder Um die Leistung in mobilen Netzwerken zu verbessern, müssen Knoten und Kanten in Version 2.4 explizit die Felder anfordern, die Sie für Ihre GET Anforderungen benötigen. Beispiel: GET /v2.4/me/feed enthält GET /v2.4/me/feed keine Likes und Kommentare mehr, aber GET /v2.4/me/feed?fields=comments,likes gibt die Daten zurück. Weitere Informationen finden Sie in der docs zum Anfordern bestimmter Felder.


Ich möchte nur die Antwort von Mike auf diese Zeile hinzufügen

facebookOptions.Scope.Add("email");

muss nachher noch hinzugefügt werden

var facebookOptions = new FacebookAuthenticationOptions()
{
    AppId = "*",
    AppSecret = "*",
    BackchannelHttpHandler = new FacebookBackChannelHandler(),
    UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name,location"
}

Und wenn Sie Ihren Facebook-Account bereits ohne E-Mail-Erlaubnis auf Ihrer Entwickler-Website registriert haben. Nachdem Sie den Code geändert und es erneut versucht haben, erhalten Sie die E-Mail immer noch nicht, da die E-Mail-Berechtigung für Ihre Dev-Website nicht erteilt wurde. Ich gehe zu https://www.facebook.com/settings?tab=applications , entferne meine Facebook-App und führe den Vorgang erneut durch.



Aus einem Katana-Thread entnahm ich folgendes:

Ändern Sie die FacebookAuthenticationOptions , um BackchannelHttpHandler und UserInformationEndpoint wie unten dargestellt UserInformationEndpoint . Stellen Sie sicher, dass Sie die Namen der Felder angeben, die Sie für Ihre Implementierung benötigen und benötigen.

var facebookOptions = new FacebookAuthenticationOptions()
{
    AppId = "*",
    AppSecret = "*",
    BackchannelHttpHandler = new FacebookBackChannelHandler(),
    UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name"
}

Erstellen Sie dann einen benutzerdefinierten FacebookBackChannelHandler , der die Anfragen an Facebook abfängt und die fehlerhafte URL nach Bedarf korrigiert.

UPDATE: Der FacebookBackChannelHandler wird basierend auf einem Update vom 27.03.2017 auf die FB api aktualisiert.

public class FacebookBackChannelHandler : HttpClientHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        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 content = await result.Content.ReadAsStringAsync();
        var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);

        var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty);
        outgoingQueryString.Add("access_token", facebookOauthResponse.access_token);
        outgoingQueryString.Add("expires_in", facebookOauthResponse.expires_in + string.Empty);
        outgoingQueryString.Add("token_type", facebookOauthResponse.token_type);
        var postdata = outgoingQueryString.ToString();

        var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(postdata)
        };

        return modifiedResult;
    }
}

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

Eine sinnvolle Ergänzung wäre, nach der Version 3.0.1 der Bibliothek zu suchen und eine Ausnahme auszulösen, wenn sie sich ändert. Auf diese Weise wissen Sie, ob jemand das NuGet-Paket aktualisiert oder aktualisiert, nachdem eine Lösung für dieses Problem veröffentlicht wurde.

(Aktualisiert, um in C # 5 zu arbeiten, ohne neuen Namen der Funktion)


Dein Missverständnis ist sehr häufig. Vielen Leuten wird beigebracht, dass Multithreading und Asynchronie dasselbe sind, aber sie sind es nicht.

Eine Analogie hilft normalerweise. Sie kochen in einem Restaurant. Eine Bestellung kommt für Eier und Toast.

  • Synchron: Sie kochen die Eier, dann kochen Sie den Toast.
  • Asynchron, single threaded: Sie starten das Eierkochen und stellen einen Timer ein. Sie starten das Toastkochen und stellen einen Timer ein. Während sie beide kochen, putzt du die Küche. Wenn die Timer losgehen, nehmen Sie die Eier von der Hitze und den Toast aus dem Toaster und servieren Sie ihnen.
  • Asynchron, Multithread: Sie stellen zwei weitere Köche ein, einen zum Eierkochen und einen zum Toastkochen. Jetzt haben Sie das Problem, die Köche so zu koordinieren, dass sie beim Teilen von Ressourcen nicht in der Küche miteinander in Konflikt geraten. Und du musst sie bezahlen.

Nun macht es Sinn, dass Multithreading nur eine Art von Asynchronität ist? Threading dreht sich um Arbeiter; Asynchronität geht um Aufgaben . In Multithread-Workflows weisen Sie Arbeitern Aufgaben zu. In asynchronen Single-Threaded-Workflows haben Sie ein Diagramm mit Aufgaben, bei denen einige Aufgaben von den Ergebnissen anderer abhängen. Wenn die einzelnen Aufgaben abgeschlossen sind, ruft sie den Code auf, der die nächste auszuführende Aufgabe aufgrund der Ergebnisse der gerade abgeschlossenen Aufgabe plant. Aber Sie benötigen (hoffentlich) nur einen Arbeiter, um alle Aufgaben auszuführen, nicht einen Arbeiter pro Aufgabe.

Es hilft zu erkennen, dass viele Aufgaben nicht prozessorgebunden sind. Für prozessorgebundene Tasks ist es sinnvoll, so viele Worker (Threads) einzustellen, wie Prozessoren vorhanden sind, jedem Worker eine Task zuzuweisen, jedem Worker einen Prozessor zuzuweisen und jedem Prozessor die Aufgabe zu übertragen, nur das Ergebnis zu berechnen so schnell wie möglich. Bei Aufgaben, die nicht auf einem Prozessor warten, müssen Sie keinen Mitarbeiter zuweisen. Sie warten nur auf die Nachricht, dass das Ergebnis verfügbar ist, und tun Sie etwas anderes, während Sie warten . Wenn diese Nachricht eintrifft, können Sie die Fortsetzung der abgeschlossenen Aufgabe als nächsten Schritt auf Ihrer To-Do-Liste planen.

Sehen wir uns Jon's Beispiel genauer an. Was geschieht?

  • Jemand ruft DisplayWebSiteLength auf. Wer? Uns ist es egal.
  • Er setzt ein Label, erstellt einen Client und fordert den Client auf, etwas zu holen. Der Client gibt ein Objekt zurück, das die Aufgabe darstellt, etwas abzurufen. Diese Aufgabe ist in Bearbeitung.
  • Ist es in einem anderen Thread in Bearbeitung? Wahrscheinlich nicht. Lesen Sie blog.stephencleary.com/2013/11/there-is-no-thread.html darüber, warum es keinen Thread gibt.
  • Jetzt erwarten wir die Aufgabe. Was geschieht? Wir prüfen, ob die Aufgabe zwischen dem Zeitpunkt, an dem wir sie erstellt haben, abgeschlossen ist und wir darauf gewartet haben. Wenn ja, dann holen wir das Ergebnis und laufen weiter. Nehmen wir an, es ist nicht abgeschlossen. Wir unterschreiben den Rest dieser Methode als Fortsetzung dieser Aufgabe und Rückkehr .
  • Jetzt ist die Kontrolle zum Anrufer zurückgekehrt. Was tut es? Was immer es will.
  • Angenommen, die Aufgabe ist abgeschlossen. Wie hat es das gemacht? Vielleicht lief es in einem anderen Thread, oder vielleicht der Aufrufer, den wir gerade zurückgegeben haben, damit er im aktuellen Thread vollständig ausgeführt werden konnte. Unabhängig davon haben wir jetzt eine abgeschlossene Aufgabe.
  • Die abgeschlossene Aufgabe fragt den richtigen Thread - wiederum wahrscheinlich der einzige Thread - um die Fortsetzung der Aufgabe auszuführen.
  • Die Kontrolle geht sofort wieder in die Methode über, die wir gerade an der Stelle des Wartens verlassen haben. Jetzt steht ein Ergebnis zur Verfügung, damit wir text zuweisen und den Rest der Methode ausführen können.

Es ist wie in meiner Analogie. Jemand bittet dich um ein Dokument. Sie verschicken das Dokument per Post und machen weiter. Wenn es in der Post ankommt, wird Ihnen signalisiert, und wenn Ihnen danach ist, erledigen Sie den Rest des Workflows - öffnen Sie den Umschlag, bezahlen Sie die Liefergebühren, was auch immer. Sie müssen keinen anderen Arbeiter einstellen, um das alles für Sie zu tun.





c# facebook facebook-graph-api asp.net-mvc-5 asp.net-identity-2