WEB API 2:在oauth期间获取个人资料数据RegisterExternal(facebook)

时间:2015-02-22 02:19:15

标签: c# oauth asp.net-web-api2

在新用户调用后,开箱即用的ASP.NET WEB API oAuth实现:

GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true

用户被重定向到外部登录(在我的情况下是Facebook),导致他们用于注册的令牌(开箱即用的代码)

        // POST api/Account/RegisterExternal
        [OverrideAuthentication]
        [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
        [Route("RegisterExternal")]
        public async Task<IHttpActionResult> RegisterExternal([FromBody]RegisterExternalBindingModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

            if (externalLogin == null)
            {
                return InternalServerError();
            }

            IdentityUser user = new IdentityUser
            {
                UserName = model.UserName
            };
            user.Logins.Add(new IdentityUserLogin
            {
                LoginProvider = externalLogin.LoginProvider,
                ProviderKey = externalLogin.ProviderKey
            });
            IdentityResult result = await UserManager.CreateAsync(user);
            IHttpActionResult errorResult = GetErrorResult(result);

            if (errorResult != null)
            {
                return errorResult;
            }

            return Ok();
        }

在RegisterExternal期间我想使用Facebook上的数据填充另一个数据库(名字,姓氏,电子邮件,朋友,分机......)

我在注册时获得的持票人令牌可以简单地这样称呼:

var accessToken = "token from header";
var client = new FacebookClient(accessToken);

所以根据我的理解,我需要修改Startup.Auth并声明这些数据,因为我已经通过添加:

        var facebookProvider = new FacebookAuthenticationProvider()
        {
            OnAuthenticated = (context) =>
            {
                // Add the email id to the claim
                context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email));
                return Task.FromResult(0);
            }
        };
        var options = new FacebookAuthenticationOptions()
        {
            AppId = "xxxxxxxxxxxxxxxxx",
            AppSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            Provider = facebookProvider
        };
        options.Scope.Add("email");
        options.Scope.Add("user_friends");
        options.Scope.Add("public_profile");
        app.UseFacebookAuthentication(options);

但是我如何在RegisterExternal方法中获取数据?

3 个答案:

答案 0 :(得分:3)

我有同样的问题(我认为) - 问题是FB OAuth基础架构只填充基本数据而我想要更多。

在深入研究ASP.NET身份的源代码后,我发现了以下内容:

app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
    AppId = "",
    AppSecret = "",
    Scope = { "public_profile", "email", "user_birthday", "user_location" },
    Provider = new FacebookAuthProvider(),
    UserInformationEndpoint = "https://graph.facebook.com/v2.5/me?fields=id,name,email,first_name,last_name,location,birthday,picture",
});

这里的重要部分是自定义提供者:

private class FacebookAuthProvider : FacebookAuthenticationProvider
{
    /// <summary>
    /// Invoked whenever Facebook succesfully authenticates a user
    /// </summary>
    /// <param name="context">Contains information about the login session as well as the user <see cref="T:System.Security.Claims.ClaimsIdentity" />.</param>
    /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> representing the completed operation.</returns>
    public override Task Authenticated(FacebookAuthenticatedContext context)
    {
        TryParseProperty(context, "first_name", Claims.FirstName);
        TryParseProperty(context, "last_name", Claims.LastName);
        TryParseProperty(context, "picture.data.url", Claims.PhotoUrl);

        return base.Authenticated(context);
    }

    private void TryParseProperty(FacebookAuthenticatedContext context, string name, string targetName)
    {
        var value = context.User.SelectToken(name);
        if (value != null)
        {
            context.Identity.AddClaim(targetName, value.ToString());
        }
    }

}

这基本上将所有数据都放在索赔中,并且可以在任何其他地方以相同的方式检索。

答案 1 :(得分:2)

外部提供商(在本例中为Facebook)将填充声明,并且可以在LoginInfo中的回调方法中访问这些声明。

以下是阅读Facebook Access令牌的代码:

var accessToken = loginInfo.ExternalIdentity.Claims.FirstOrDefault(x => x.Type == Constants.FacebookAccessToken).Value;

如果您在那里设置断点,您将能够看到Facebook返回的其他内容。

答案 2 :(得分:1)

John Mc真的指出了我正确的方向,这是一个更完整的解决方案。

// POST api/Account/RegisterExternalToken
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternalToken")]
public async Task<IHttpActionResult> RegisterExternalToken()
{
    ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

    if (externalLogin == null)
    {
        return InternalServerError();
    }

    var facebookToken = externalLogin.Token;

然后在声明中(这是关键部分)正如约翰所指出的那样:

        private class ExternalLoginData
        {
            public string LoginProvider { get; set; }
            public string ProviderKey { get; set; }
            public string UserName { get; set; }
            public string Token { get; set; }

            public IList<Claim> GetClaims()
            {
                IList<Claim> claims = new List<Claim>();
                claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));

                if (UserName != null)
                {
                    claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
                }

                if (Token != null)
                {
                    claims.Add(new Claim("FacebookAccessToken", Token, null, LoginProvider));
                }

                return claims;
            }

            public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
            {
                if (identity == null)
                {
                    return null;
                }

                Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);

                if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
                    || String.IsNullOrEmpty(providerKeyClaim.Value))
                {
                    return null;
                }

                if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
                {
                    return null;
                }

                return new ExternalLoginData
                {
                    LoginProvider = providerKeyClaim.Issuer,
                    ProviderKey = providerKeyClaim.Value,
                    UserName = identity.FindFirstValue(ClaimTypes.Name),
                    Token = identity.Claims.FirstOrDefault(x => x.Type.Contains("FacebookAccessToken")).Value
                };
            }
        }