WEB API 2 : 在 oauth RegisterExternal (facebook) 期间获取配置文件数据
WEB API 2 : get profile data during oauth RegisterExternal (facebook)
开箱即用 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 上的数据(名字、姓氏、电子邮件、朋友、分机..)填充另一个数据库
我在注册期间获得的 Bearer 令牌可以不能简单地这样称呼:
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 方法中获取该数据?
外部提供商(在本例中为 Facebook)将填充声明,这些可以在您在 LoginInfo 中的回调方法中访问。
这是读取 Facebook 访问令牌的代码:
var accessToken = loginInfo.ExternalIdentity.Claims.FirstOrDefault(x => x.Type == Constants.FacebookAccessToken).Value;
如果您在此处设置断点,您将能够看到 Facebook 返回的其他内容。
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;
然后在声明中(这是关键部分),正如 John 所指出的:
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
};
}
}
我遇到了同样的问题(我认为)- 问题是 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());
}
}
}
这基本上将所有数据都放在声明中,并且可以用相同的方式在其他任何地方检索。
开箱即用 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 上的数据(名字、姓氏、电子邮件、朋友、分机..)填充另一个数据库
我在注册期间获得的 Bearer 令牌可以不能简单地这样称呼:
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 方法中获取该数据?
外部提供商(在本例中为 Facebook)将填充声明,这些可以在您在 LoginInfo 中的回调方法中访问。
这是读取 Facebook 访问令牌的代码:
var accessToken = loginInfo.ExternalIdentity.Claims.FirstOrDefault(x => x.Type == Constants.FacebookAccessToken).Value;
如果您在此处设置断点,您将能够看到 Facebook 返回的其他内容。
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;
然后在声明中(这是关键部分),正如 John 所指出的:
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
};
}
}
我遇到了同样的问题(我认为)- 问题是 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());
}
}
}
这基本上将所有数据都放在声明中,并且可以用相同的方式在其他任何地方检索。