首页 文章

如何使用ASP.NET身份(OWIN)访问Facebook私人信息?

提问于
浏览
31

我正在ASP.NET MVC 5中开发一个网站(目前使用RC1版本) . 该网站将使用Facebook进行用户身份验证和检索初始 Profiles 数据 .

对于身份验证系统,我使用新的基于OWIN的ASP.NET身份引擎(http://blogs.msdn.com/b/webdev/archive/2013/07/03/understanding-owin-forms-authentication-in-mvc-5.aspx),因为它极大地简化了与外部提供程序进行身份验证的过程 .

问题是,一旦用户首次登录,我想从Facebook Profiles 中获取其电子邮件地址,但此数据不包含在生成的声明中 . 所以我考虑过这些替代方案来获取地址:

  • 指示ASP.NET标识引擎将电子邮件地址包含在从Facebook检索的数据集中,然后转换为声明 . 我不知道这是否可行 .

  • 使用Facebook图形API(https://developers.facebook.com/docs/getting-started/graphapi)通过使用Facebook用户ID(包含在声明数据中)来检索电子邮件地址 . 但是,如果用户将其电子邮件地址设置为私有,则此操作无效 .

  • 使用Facebook图形API,但指定"me"而不是Facebook用户ID(https://developers.facebook.com/docs/reference/api/user) . 但是需要一个访问令牌,我根本不可能检索ASP.NET用来获取用户数据的访问令牌 .

所以问题是:

  • 如何指示ASP.NET标识引擎从Facebook检索其他信息并将其包含在声明数据中?

  • 或者,我如何检索生成的访问令牌以便我自己询问Facebook?

谢谢!

注意:对于身份验证系统,我的应用程序使用基于此SO答案中链接的示例项目的代码:https://stackoverflow.com/a/18423474/4574

6 回答

  • 22

    它在_2863542中只对我有用:

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

    然后在方法 ExternalLoginCallbackExternalLoginConfirmation 中,您将收到以下电子邮件:

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

    要从Facebook检索其他信息,您可以指定在配置facebook身份验证选项时要包括的范围 . 获取检索到的其他信息可以通过实现提供程序的OnAuthenticated方法来实现,如下所示:

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

    根据代码here我看到电子邮件已经被检索并在Facebook发送时作为声明添加 . 你不能看到它吗?

  • 24

    在Startup.ConfigureAuth(StartupAuth.cs)中创建一个新的Microsoft.Owin.Security.Facebook.AuthenticationOptions对象,并将其传递给FacebookAppId,FacebookAppSecret和一个新的AuthenticationProvider . 您将使用lambda表达式向OnAuthenticated方法传递一些代码,以将声明添加到包含您从context.Identity中提取的值的标识 . 默认情况下,这将包括 access_token . 您必须将 email 添加到Scope . context.User中提供了其他用户属性(例如,请参见底部的链接) .

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

    在AccountController中,我使用外部cookie从AuthenticationManager中提取ClaimsIdentity . 然后我将其添加到使用应用程序cookie创建的标识中 . 我忽略了任何以“... schemas.xmlsoap.org/ws/2005/05/identity/claims”开头的声明,因为它似乎打破了登录 .

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

    最后,我想显示来自LOCAL AUTHORITY的任何值 . 我创建了一个出现在/Account/Manage page上的部分视图_ExternalUserPropertiesListPartial . 我从AuthenticationManager.User.Claims获取我之前存储的声明,然后将其传递给视图 .

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

    只是为了彻底,观点:

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

    工作示例和完整代码在GitHub上:https://github.com/johndpalm/IdentityUserPropertiesSample

    任何反馈,更正或改进将不胜感激 .

  • 2

    您需要创建 FacebookAuthenticationOptions 的实例并配置 Provider . Provider 包含一个名为 OnAuthenticated 的事件,在您登录时会触发该事件 .

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

    在上面的代码中,我通过 context.AccessToken 访问 access_token 并将其添加到当前登录用户的 Claims .

    要在以后访问此值,您需要执行以下操作:

    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;
    

    为了简化所有这些,您可以创建 BaseController 并从中继承所有 Controllers .

    BaseController 代码将是:

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

    然后,要获取代码上的访问令牌,您只需访问属性 FacebookAccessToken .

    string accessToken = FacebookAccessToken;
    

    可以检索其他一些值

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

    请注意不是所有字段都可用,以获取您需要 Scope 电子邮件所需的电子邮件 .

    facebookOptions.Scope.Add("email");
    

    然后在 OnAuthenticated 事件上访问

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

    以下是一些可以帮助您的步骤 . 我正在撰写博客文章,但需要一段时间...... - 在Fb提供程序中添加范围并添加从FB返回的数据作为声明

    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",
            });
    
    • 使用访问令牌并调用Facebook C#SDK获取用户的朋友列表
    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 + "
    Facebook id: " + friend.id + "

    "); }
  • 0

    我的几分钱给所有的答案......如果你还想自己问问Facebook,那么看看现有的Facebook包是否合理 . 它提供了一些已经实现的强大功能,因此您不必自己重新实现它...一些示例如何在ASP.NET MVC应用程序中使用它,您可以找到here .

相关问题