web-dev-qa-db-de.com

Wie greife ich mit ASP.NET Identity (OWIN) auf private Facebook-Informationen zu?

Ich entwickle eine Website in ASP.NET MVC 5 (derzeit mit RC1-Version). Die Website verwendet Facebook zur Benutzerauthentifizierung und zum Abrufen der anfänglichen Profildaten.

Für das Authentifizierungssystem verwende ich die neue OWIN-basierte ASP.NET Identity Engine ( http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication) -in-mvc-5.aspx ), da dies die Authentifizierung bei externen Anbietern erheblich vereinfacht.

Das Problem ist, dass ich, sobald sich ein Benutzer zum ersten Mal anmeldet, seine E-Mail-Adresse aus dem Facebook-Profil erhalten möchte, diese Daten jedoch nicht in den generierten Ansprüchen enthalten sind. Also habe ich über diese Alternativen nachgedacht, um die Adresse zu erhalten:

  1. Weisen Sie das ASP.NET-Identitätsmodul an, die E-Mail-Adresse in den Datensatz aufzunehmen, der von Facebook abgerufen und dann in Ansprüche konvertiert wird. Ich weiß nicht, ob das überhaupt möglich ist.

  2. Verwenden Sie die Facebook-Grafik-API ( https://developers.facebook.com/docs/getting-started/graphapi ), um die E-Mail-Adresse mithilfe der Facebook-Benutzer-ID abzurufen (die in den Anspruchsdaten enthalten ist) ). Dies funktioniert jedoch nicht, wenn der Benutzer seine E-Mail-Adresse als privat festgelegt hat.

  3. Verwenden Sie die Facebook-Grafik-API, geben Sie jedoch "Ich" anstelle der Facebook-Benutzer-ID an ( https://developers.facebook.com/docs/reference/api/user ). Es ist jedoch ein Zugriffstoken erforderlich, und ich weiß nicht, wie (oder ob es überhaupt möglich ist) ich das Zugriffstoken abrufen kann, mit dem ASP.NET die Benutzerdaten abruft.

Die Frage ist also:

  1. Wie kann ich die ASP.NET Identity Engine anweisen, zusätzliche Informationen von Facebook abzurufen und diese in die Anspruchsdaten aufzunehmen?

  2. Oder wie kann ich das generierte Zugriffstoken abrufen, damit ich Facebook selbst fragen kann?

Vielen Dank!

Hinweis: Für das Authentifizierungssystem verwendet meine Anwendung Code, der auf dem Beispielprojekt basiert, das in dieser SO answer: https://stackoverflow.com/a/18423474/4574 verlinkt ist

31
Konamiman

Um zusätzliche Informationen von Facebook abzurufen, können Sie Bereiche angeben, die Sie beim Konfigurieren der Facebook-Authentifizierungsoptionen einbeziehen möchten. Um die abgerufenen zusätzlichen Informationen abzurufen, können Sie die OnAuthenticated-Methode des Anbieters wie folgt implementieren:

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
    Provider = new FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
            {
                // All data from facebook in this object. 
                var rawUserObjectFromFacebookAsJson = context.User;

                // Only some of the basic details from facebook 
                // like id, username, email etc are added as claims.
                // But you can retrieve any other details from this
                // raw Json object from facebook and add it as claims here.
                // Subsequently adding a claim here will also send this claim
                // as part of the cookie set on the browser so you can retrieve
                // on every successive request. 
                context.Identity.AddClaim(...);

                return Task.FromResult(0);
            }
    }
};

//Way to specify additional scopes
facebookOptions.Scope.Add("...");

app.UseFacebookAuthentication(facebookOptions);

Gemäß dem Code hier sehe ich, dass die E-Mail bereits abgerufen und als Anspruch hier hinzugefügt wurde, wenn Facebook gesendet hat. Kannst du es nicht sehen?

24
Praburaj

Erstellen Sie ein neues Microsoft.Owin.Security.Facebook.AuthenticationOptions-Objekt in Startup.ConfigureAuth (StartupAuth.cs), und übergeben Sie ihm die FacebookAppId, FacebookAppSecret und einen neuen AuthenticationProvider. Mit einem Lambda-Ausdruck übergeben Sie der OnAuthenticated-Methode Code, um der Identität Claims hinzuzufügen, die die Werte enthalten, die Sie aus context.Identity extrahieren. Dies schließt standardmäßig access_token ein. Sie müssen dem Bereich eine E-Mail hinzufügen. Andere Benutzereigenschaften sind über context.User verfügbar (siehe Link unten zum Beispiel).

StartUp.Auth.cs

// Facebook : Create New App
// https://dev.Twitter.com/apps
if (ConfigurationManager.AppSettings.Get("FacebookAppId").Length > 0)
{
    var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
    {
        AppId = ConfigurationManager.AppSettings.Get("FacebookAppId"),
        AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
        Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
        {
            OnAuthenticated = (context) =>
                {
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
                    context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:email", context.Email, XmlSchemaString, "Facebook"));
                    return Task.FromResult(0);
                }
        }

    };
    facebookOptions.Scope.Add("email");
    app.UseFacebookAuthentication(facebookOptions);
}

In AccountController extrahiere ich die ClaimsIdentity mithilfe des externen Cookies aus dem AuthenticationManager. Ich füge es dann der Identität hinzu, die mit dem Anwendungs-Cookie erstellt wurde. Ich habe alle Behauptungen, die mit "... schemas.xmlsoap.org/ws/2005/05/identity/claims" beginnen, ignoriert, da die Anmeldung offenbar unterbrochen wurde.

AccountController.cs

private async Task SignInAsync(CustomUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

// Extracted the part that has been changed in SignInAsync for clarity.
    await SetExternalProperties(identity);

    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

private async Task SetExternalProperties(ClaimsIdentity identity)
{
    // get external claims captured in Startup.ConfigureAuth
    ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

    if (ext != null)
    {
        var ignoreClaim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
        // add external claims to identity
        foreach (var c in ext.Claims)
        {
            if (!c.Type.StartsWith(ignoreClaim))
                if (!identity.HasClaim(c.Type, c.Value))
                    identity.AddClaim(c);
        } 
    }
}

Und zum Schluss möchte ich die Werte anzeigen, die nicht von LOCAL AUTHORITY stammen. Ich habe eine Teilansicht _ExternalUserPropertiesListPartial erstellt, die auf der Seite / Account/Manage angezeigt wird. Ich erhalte die Ansprüche, die ich zuvor gespeichert habe, von AuthenticationManager.User.Claims und übergebe sie dann an die Ansicht.

AccountController.cs

[ChildActionOnly]
public ActionResult ExternalUserPropertiesList()
{
    var extList = GetExternalProperties();
    return (ActionResult)PartialView("_ExternalUserPropertiesListPartial", extList);
}

private List<ExtPropertyViewModel> GetExternalProperties()
{
    var claimlist = from claims in AuthenticationManager.User.Claims
                    where claims.Issuer != "LOCAL AUTHORITY"
                    select new ExtPropertyViewModel
                    {
                        Issuer = claims.Issuer,
                        Type = claims.Type,
                        Value = claims.Value
                    };

    return claimlist.ToList<ExtPropertyViewModel>();
}

Und um genau zu sein, die Aussicht:

_ExternalUserPropertiesListPartial.cshtml

@model IEnumerable<MySample.Models.ExtPropertyViewModel>

@if (Model != null)
{
    <legend>External User Properties</legend>
    <table class="table">
        <tbody>
            @foreach (var claim in Model)
            {
                <tr>
                    <td>@claim.Issuer</td>
                    <td>@claim.Type</td>
                    <td>@claim.Value</td>
                </tr>
            }
        </tbody>
    </table>
}

Das Arbeitsbeispiel und der vollständige Code befinden sich auf GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample

Feedback, Korrekturen oder Verbesserungen sind jederzeit willkommen.

29
John Palmer

Es funktionierte für mich mit nur diesem in Startup.Auth:

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()        {
    AppId = "*",
    AppSecret = "**"
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);

Und dann in der Methode ExternalLoginCallback oder ExternalLoginConfirmation erhalten Sie die E-Mail mit:

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var email = ext.Claims.First(x => x.Type.Contains("emailaddress")).Value;
22
Julien

Sie müssen eine Instanz von FacebookAuthenticationOptions erstellen und Provider konfigurieren. Das Provider enthält ein Ereignis namens OnAuthenticated, das beim Anmelden ausgelöst wird.

var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
    Provider = new FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
        {
            context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook"));

            return Task.FromResult(0);
        }
    },

    // You can store these on AppSettings
    AppId = ConfigurationManager.AppSettings["facebook:AppId"],
    AppSecret = ConfigurationManager.AppSettings["facebook:AppSecret"]
};

app.UseFacebookAuthentication(facebookOptions);

Im obigen Code greife ich auf die access_token durch context.AccessToken und füge es zu Claims des aktuell angemeldeten Benutzers hinzu.

Um später auf diesen Wert zuzugreifen, müssen Sie dies tun:

var owinContext = HttpContext.GetOwinContext();
var authentication = owinContext.Authentication;
var user = autentication.User;
var claim = (user.Identity as ClaimsIdentity).FindFirst("urn:facebook:access_token");

string accessToken;
if (claim != null)
    accessToken = claim.Value;

Um all dies zu vereinfachen, können Sie ein BaseController erstellen und alle Ihre Controllers davon erben lassen.

Der BaseController Code wäre:

public class BaseController : Controller
{
    public IOwinContext CurrentOwinContext
    {
        get
        {
            return HttpContext.GetOwinContext();
        }
    }

    public IAuthenticationManager Authentication
    {
        get
        {
            return CurrentOwinContext.Authentication;
        }
    }

    public new ClaimsPrincipal User
    {
        get
        {
            return Authentication.User;
        }
    }

    public ClaimsIdentity Identity
    {
        get
        {
            return Authentication.User.Identity as ClaimsIdentity;
        }
    }

    public string FacebookAccessToken
    {
        get
        {
            var claim = Identity.FindFirst("urn:facebook:access_token");

            if (claim == null)
                return null;

            return claim.Value;
        }
    }
}

Um dann das Zugriffstoken für Ihren Code zu erhalten, müssen Sie nur auf die Eigenschaft FacebookAccessToken zugreifen.

string accessToken = FacebookAccessToken;

Es ist möglich, einige andere Werte als abzurufen

context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:username",
    context.User.Value<string>("username"), ClaimValueTypes.String, "Facebook"));

context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:name",
    context.User.Value<string>("name"), ClaimValueTypes.String, "Facebook"));

Beachten Sie, dass nicht alle Felder verfügbar sind. Um die E-Mail zu erhalten, benötigen Sie die E-Mail Scope.

facebookOptions.Scope.Add("email");

Greifen Sie dann auf das Ereignis OnAuthenticated als zu

context.User.Value<string>("email");
5
BrunoLM

Hier sind einige Schritte, die Ihnen helfen werden. Ich bin gerade dabei, einen Blog-Beitrag zu verfassen, aber es wird eine Weile dauern ... - Füge Bereiche im Fb-Anbieter hinzu und füge die von FB zurückgegebenen Daten als Ansprüche hinzu

app.UseFacebookAuthentication(new FacebookAuthenticationOptions()
        {
            AppId = "",
            AppSecret = "",
            //Scope = "email,user_about_me,user_hometown,friends_about_me,friends_photos",
            Provider = new FacebookAuthenticationProvider()
            {
                OnAuthenticated = async context =>
                {
                    foreach (var x in context.User)
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim(x.Key, x.Value.ToString()));
                    }
                    //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));
                }
            },
            SignInAsAuthenticationType = "External",
        });         
  • Verwenden Sie das Zugriffstoken und rufen Sie das Facebook C # SDK auf, um die Liste der Freunde für den Benutzer abzurufen

        var claimsIdentity = HttpContext.User.Identity as ClaimsIdentity;
        var access_token = claimsIdentity.FindAll("FacebookAccessToken").First().Value;
        var fb = new FacebookClient(access_token);
        dynamic myInfo = fb.Get("/me/friends");
        var friendsList = new List<FacebookViewModel>();
        foreach (dynamic friend in myInfo.data)
        {
            friendsList.Add(new FacebookViewModel() { Name = friend.name, ImageURL = @"https://graph.facebook.com/" + friend.id + "/picture?type=large" });
            //Response.Write("Name: " + friend.name + "<br/>Facebook id: " + friend.id + "<br/><br/>");
        }
    
2
pranav rastogi

mein paar zentiert sich auf alle antworten ... wenn du noch selbst facebook fragen willst, ist es sinnvoll, ein bereits vorhandenes facebook paket anzuschauen. Es bietet einige großartige Funktionen, die bereits implementiert wurden, sodass Sie sie nicht selbst erneut implementieren müssen. Ein Beispiel für die Verwendung innerhalb der ASP.NET MVC-Anwendung finden Sie hier .

0
Mr. Pumpkin