web-dev-qa-db-fra.com

Comment accéder aux informations privées de Facebook en utilisant l'identité ASP.NET (OWIN)?

Je développe un site Web en 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 moteur d'identité ASP.NET basé sur OWIN ( http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication -in-mvc-5.aspx ), car il simplifie considérablement le processus d'authentification auprès de 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. Demandez au moteur d'identité ASP.NET d'inclure l'adresse e-mail dans l'ensemble de données récupérées sur Facebook, puis converties en revendications. Je ne sais pas si c'est possible du tout.

  2. Utilisez l'API graphique Facebook ( https://developers.facebook.com/docs/getting-started/graphapi ) pour récupérer l'adresse e-mail en utilisant l'ID utilisateur Facebook (qui est inclus dans les données de réclamation ). Mais cela ne fonctionnera pas si l'utilisateur a défini son adresse e-mail comme privée.

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

La question est donc:

  1. Comment puis-je demander au moteur d'identité ASP.NET de récupérer des informations supplémentaires sur Facebook et de les inclure dans les données de réclamation?

  2. Ou bien, comment puis-je récupérer le jeton d'accès généré afin de pouvoir demander moi-même à Facebook?

Je vous remercie!

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

31
Konamiman

Pour récupérer des informations supplémentaires sur Facebook, vous pouvez spécifier les étendues que vous souhaitez inclure lorsque vous configurez les options d'authentification Facebook. L'obtention des informations supplémentaires récupérées peut être obtenue 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é comme réclamation ici si Facebook a envoyé. N'êtes-vous pas en mesure de le voir?

24
Praburaj

Créez un nouvel objet Microsoft.Owin.Security.Facebook.AuthenticationOptions dans Startup.ConfigureAuth (StartupAuth.cs), en lui passant le FacebookAppId, FacebookAppSecret et un nouveau AuthenticationProvider. Vous allez utiliser une expression lambda pour transmettre à la méthode OnAuthenticated du code pour ajouter des revendications à l'identité qui contiennent les valeurs que vous extrayez de context.Identity. Cela inclura access_token par défaut. Vous devez ajouter e-mail à la portée. D'autres propriétés utilisateur sont disponibles dans context.User (voir le lien en bas par exemple).

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

Dans AccountController, j'extrais 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 commençant par "... schemas.xmlsoap.org/ws/2005/05/identity/claims" car il semblait rompre 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 toutes les valeurs qui ne proviennent pas de l'Autorité locale. J'ai créé une vue partielle _ExternalUserPropertiesListPartial qui apparaît sur la page / Account/Manage . J'obtiens les revendications que j'ai précédemment stockées d'AuthenticationManager.User.Claims, puis je les transmets à 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 se trouvent sur GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample

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

29
John Palmer

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

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

Et puis 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
Julien

Vous devez créer une instance de FacebookAuthenticationOptions et configurer Provider. 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'ajouter à Claims de l'utilisateur actuellement connecté.

Pour accéder à cette valeur ultérieurement, vous devez procéder comme suit:

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 en faire hériter tous vos Controllers.

Le code BaseController serait:

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 ne seront pas disponibles, pour obtenir l'e-mail dont vous avez besoin, vous aurez besoin de l'email Scope.

facebookOptions.Scope.Add("email");

Accédez ensuite à l'événement OnAuthenticated comme

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

Voici quelques étapes qui vous aideront. Je suis en train de rédiger un article 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
pranav rastogi

mes quelques cents à toutes les réponses ... si vous voulez toujours demander à Facebook vous-même, il est logique de jeter un œil sur le package Facebook déjà existant. Il fournit de grandes fonctionnalités qui ont déjà été implémentées, vous n'avez donc pas à le réimplémenter vous-même ... un exemple comment l'utiliser dans l'application ASP.NET MVC, vous pouvez trouver ici .

0
Mr. Pumpkin