Comment accéder à des informations privées Facebook en utilisant ASP.NET identité (OWIN)?

Je développe un site web dans ASP.NET MVC 5 (en utilisant la version RC1 actuellement). Le site utilisera Facebook pour l'authentification des utilisateurs et pour récupérer les données de profil initiales.

Pour le système d'authentification, j'utilise le nouveau basé sur OWIN ASP.NET moteur D'identité (http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc-5.aspx), car il simplifie grandement le processus d'authentification avec des fournisseurs externes.

Le problème est qu'une fois qu'un utilisateur se connecte pour la première fois, je veux obtenir son adresse e-mail à partir du profil Facebook, mais ces données ne sont pas incluses dans les revendications générées. J'ai donc pensé à ces alternatives pour obtenir l'adresse:

  1. Instruisez le ASP.NET moteur D'identité pour inclure l'adresse e-mail dans l'ensemble de données qui est récupéré à partir de Facebook, puis converti prétendre. Je ne sais pas si c'est possible.

  2. Utilisez le graphique Facebook API ( https://developers.facebook.com/docs/getting-started/graphapi ) à récupérer l'adresse e-mail en utilisant L'id utilisateur Facebook (qui est inclus dans les données sur les réclamations). Mais cela ne fonctionnera pas si l'Utilisateur a définissez son adresse e-mail comme privée.

  3. Utilisez L'API Facebook graph, mais en spécifiant "moi" au lieu de Id utilisateur Facebook ( https://developers.facebook.com/docs/reference/api/user ). le jeton d'accès est requis, et je ne sais pas comment (ou si c'est possible du tout) récupérer le jeton d'accès qui ASP.NET utilise pour obtenir les données utilisateur.

Donc la question Est:

  1. Comment puis-je instruire le ASP.NET moteur D'identité à récupérer informations supplémentaires de Facebook et l'inclure dans les réclamations les données?

  2. Ou alternativement, Comment puis-je récupérer le jeton d'accès généré afin que je peux demander à Facebook moi-même?

Merci!

Note: pour le système d'authentification mon l'application utilise du code basé sur l'exemple de projet lié dans cette réponse SO: https://stackoverflow.com/a/18423474/4574

31
demandé sur Community 2013-09-22 13:18:19

6 réponses

Pour récupérer des informations supplémentaires sur facebook, vous pouvez spécifier les étendues que vous souhaitez inclure lorsque vous configurez le facebook des options d'authentification. Obtenir les informations supplémentaires récupérées peut être réalisé en implémentant la méthode onauthenticated du fournisseur comme ceci:

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

Par le code ici je vois que l'e-mail est déjà récupéré et ajouté en tant que réclamation ici si facebook a envoyé. N'êtes-vous pas capable de le voir?

24
répondu Praburaj 2014-01-18 09:14:00

Créer un nouveau Microsoft.Owin.Sécurité.Facebook.Objet AuthenticationOptions au démarrage.ConfigureAuth (StartupAuth.cs), en passant le FacebookAppId, FacebookAppSecret, et un nouveau AuthenticationProvider. Vous utiliserez une expression lambda pour transmettre à la méthode OnAuthenticated du code pour ajouter des revendications à l'identité qui contiennent les valeurs que vous extrayez du contexte.Identité. Cela inclura access_token par défaut. Vous devez ajouter email à la Portée. Autres propriétés utilisateur sont disponibles à partir du contexte.Utilisateur (voir le lien en bas par exemple).

Démarrage.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);
}

Dans AccountController, j'extrait le ClaimsIdentity du AuthenticationManager en utilisant le cookie externe. Je l'ajoute ensuite à l'identité créée à l'aide du cookie d'application. J'ai ignoré toutes les revendications qui commencent par "...schemas.xmlsoap.org/ws/2005/05/identity/claims " comme il semblait casser la connexion.

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

Et enfin, je veux afficher quelles que soient les valeurs ne sont pas de L'autorité locale. J'ai créé une vue partielle _ExternalUserPropertiesListPartial qui apparaît sur la page /Account/Manage . Je reçois les revendications que j'ai précédemment stockées à partir D'AuthenticationManager.Utilisateur.Revendications, puis passez - le à la vue.

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

Et juste pour être complet, la vue:

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

L'exemple de travail et le code complet sont sur GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample

Et toute rétroaction, correction ou amélioration serait appréciée.

29
répondu John Palmer 2013-10-23 18:28:10

Cela a fonctionné pour moi avec juste ceci dans Startup.Auth:

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

Et ensuite, dans la méthode ExternalLoginCallback ou ExternalLoginConfirmation vous obtenez l'e-mail avec :

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var email = ext.Claims.First(x => x.Type.Contains("emailaddress")).Value;
22
répondu Julien 2013-12-05 23:40:58

, Vous devez créer une instance de FacebookAuthenticationOptions et configurethe Provider. Le Provider contient un événement appelé OnAuthenticated qui est déclenché lorsque vous vous connectez.

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

Dans le code ci-dessus, j'accède au access_token par context.AccessToken et l'ajoute à Claims de l'utilisateur loggedin actuel.

Pour accéder À cette valeur plus tard, vous devez faire ceci:

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;

Pour simplifier tout cela, vous pouvez créer un BaseController et de faire tous vos Controllers hériter d'elle.

Le code BaseController serait être:

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

Ensuite, pour obtenir le jeton d'accès sur votre code, il vous suffit d'accéder à la propriété FacebookAccessToken.

string accessToken = FacebookAccessToken;

Il est possible de récupérer d'autres valeurs comme

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"));

Notez que tous les champs seront disponibles pour obtenir l'e-mail, vous devez exiger du Scope email.

facebookOptions.Scope.Add("email");

Puis accéder à l'événement OnAuthenticated comme

context.User.Value<string>("email");
5
répondu BrunoLM 2013-12-19 21:27:56

Voici quelques étapes qui vous aideront. Je suis en train d'écrire un billet de blog, mais cela prendra un certain temps... - Ajouter des étendues dans le fournisseur Fb et ajouter les données renvoyées par FB en tant que revendications

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",
        });         
  • Utilisez le jeton D'accès et appelez Facebook C # SDK pour obtenir la liste des amis de l'utilisateur

        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
répondu pranav rastogi 2013-10-27 00:32:56

Mes quelques cents à toutes les réponses... Facebook Facebook Si vous voulez toujours demander à Facebook vous-même, il est logique de jeter un oeil sur le paquet Facebook déjà existant. Il fournit d'excellentes fonctionnalités déjà implémentées, de sorte que vous n'avez pas à les ré-implémenter vous-même... un exemple comment l'utiliser à l'intérieur ASP.NET application MVC vous pouvez trouver ici .

0
répondu Mr. Pumpkin 2014-03-28 09:28:09