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

Kon*_*man 31 asp.net-mvc facebook-graph-api owin asp.net-mvc-5 asp.net-identity

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

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

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

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

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

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

所以问题是:

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

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

谢谢!

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

Joh*_*mer 29

在Startup.ConfigureAuth(StartupAuth.cs)中创建一个新的Microsoft.Owin.Security.Facebook.AuthenticationOptions对象,并将其传递给FacebookAppId,FacebookAppSecret和一个新的AuthenticationProvider.您将使用lambda表达式向OnAuthenticated方法传递一些代码,以将声明添加到包含您从context.Identity中提取的值的标识.默认情况下,这将包括access_token.您必须向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);
}
Run Code Online (Sandbox Code Playgroud)

在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);
        } 
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,我想显示来自LOCAL AUTHORITY的任何值.我创建了一个部分视图_ExternalUserPropertiesListPartial,显示在/ Account/Manage页面上.我从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>();
}
Run Code Online (Sandbox Code Playgroud)

只是为了彻底,观点:

_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>
}
Run Code Online (Sandbox Code Playgroud)

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

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


Pra*_*raj 24

要从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);
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢!诀窍是调用`facebookOptions.Scope.Add("email")`,然后正如你所说的那样,电子邮件数据会自动添加为声明,而不必解析json数据.但是我现在遇到了一个新问题:在使用代码时,在回调中调用`AuthenticationManager.GetExternalIdentity`将返回null而不是`ClaimsIdentity`的实例.你知道会发生什么吗?(是的,我正在为facebookOptions对象添加正确的应用ID和秘密) (3认同)
  • 尝试在 options.Scope.Add("..") 调用中添加范围 'user_birthday' 并检查您是否获得信息。可以在此处查看配置文件范围信息:https://developers.facebook.com/docs/reference/login/extended-profile-properties/。此处的 Facebook 高级范围信息:https://developers.facebook.com/docs/reference/login/ (2认同)

Jul*_*ien 22

这对我有用Startup.Auth:

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

然后在方法中ExternalLoginCallback或者ExternalLoginConfirmation您收到电子邮件:

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var email = ext.Claims.First(x => x.Type.Contains("emailaddress")).Value;
Run Code Online (Sandbox Code Playgroud)

  • 你可以将它用于第二个代码片段:var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); string email = loginInfo.Email; (3认同)

Bru*_*oLM 5

您需要创建一个实例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);
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我正在访问access_tokenby context.AccessToken并将其添加到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;
Run Code Online (Sandbox Code Playgroud)

为了简化所有这些,您可以创建一个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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

string accessToken = FacebookAccessToken;
Run Code Online (Sandbox Code Playgroud)

可以检索其他一些值

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"));
Run Code Online (Sandbox Code Playgroud)

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

facebookOptions.Scope.Add("email");
Run Code Online (Sandbox Code Playgroud)

然后访问OnAuthenticated事件为

context.User.Value<string>("email");
Run Code Online (Sandbox Code Playgroud)