【发布时间】:2014-01-22 14:46:37
【问题描述】:
我知道提供了“姓名”字段,但我更愿意明确访问名字和姓氏。有人可以帮忙吗?我还在纠结 ASP.Net MVC。
【问题讨论】:
标签: asp.net-mvc facebook asp.net-mvc-5 owin
我知道提供了“姓名”字段,但我更愿意明确访问名字和姓氏。有人可以帮忙吗?我还在纠结 ASP.Net MVC。
【问题讨论】:
标签: asp.net-mvc facebook asp.net-mvc-5 owin
在您的 Startup.Auth.cs ConfigureAuth(IAppBuilder app) 方法中,为 Facebook 设置以下内容:
var x = new FacebookAuthenticationOptions();
x.Scope.Add("email");
x.AppId = "*";
x.AppSecret = "**";
x.Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
foreach (var claim in context.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
}
};
x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseFacebookAuthentication(x);
/*
app.UseFacebookAuthentication(
appId: "*",
appSecret: "*");
* */
然后使用它来访问用户的登录信息:
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
然后如下获取名字:
var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:first_name");
【讨论】:
public string FirstName { get { return chainValue( "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", // Google "urn:facebook:first_name" // Facebook ); } }
Facebook 更改了其权限 API。你可以在这里获得更多信息:https://developers.facebook.com/docs/facebook-login/permissions
名称需要 public_profile 权限
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = "appId",
AppSecret = "key"
};
facebookAuthenticationOptions.Scope.Add("email");
facebookAuthenticationOptions.Scope.Add("public_profile");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
你可以使用:
var loginInfo = await authenticationManager.GetExternalLoginInfoAsync();
loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:name")
authenticationManager 是一个实例,你可以使用:
HttpContext.GetOwinContext().Authentication;
【讨论】:
null。另外,您知道分别获取名字和姓氏的方法吗?
null。
不幸的是,由于 Facebook 使用 API 更新 2.4 更改了其默认返回值,此方法不再有效
现在获取 first_name 等的唯一方法似乎是使用 Facebook Graph API (like this posts suggests)。
我还在 Katana 项目网站上找到了解决此问题的 this post,并且已经提交了拉取请求,但尚未合并 jet。
希望这可以为某人节省一点时间;)
【讨论】:
截至 2017 年,这是为我工作的代码(感谢上面 David Poxon 的代码)。确保您已升级到 Microsoft.Owin.Security.Facebook 的 3.1.0 版。
在 Startup.Auth.cs(或在某些情况下为 Startup.cs)中,放置以下代码:
app.UseFacebookAuthentication(new FacebookAuthenticationOptions()
{
AppId = "***",
AppSecret = "****",
BackchannelHttpHandler = new HttpClientHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name",
Scope = { "email" },
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
foreach (var claim in context.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
}
}
});
然后在你控制器的外部登录回调方法中,添加这段代码:
var firstName = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:first_name").Value;
同样获取姓氏,使用上面的行并将urn:facebook:first_name替换为urn:facebook:last_name
【讨论】:
private Uri RedirectUri
{
get
{
var uriBuilder = new UriBuilder(Request.Url);
uriBuilder.Query = null;
uriBuilder.Fragment = null;
uriBuilder.Path = Url.Action("FacebookCallback");
return uriBuilder.Uri;
}
}
[AllowAnonymous]
public ActionResult Facebook()
{
var fb = new FacebookClient();
var loginUrl = fb.GetLoginUrl(new
{
client_id = "296002327404***",
client_secret = "4614cd636ed2029436f75c77961a8***",
redirect_uri = RedirectUri.AbsoluteUri,
response_type = "code",
scope = "email" // Add other permissions as needed
});
return Redirect(loginUrl.AbsoluteUri);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return View("Login");
}
public ActionResult FacebookCallback(string code)
{
var fb = new FacebookClient();
dynamic result = fb.Post("oauth/access_token", new
{
client_id = "296002327404***",
client_secret = "4614cd636ed2029436f75c77961a8***",
redirect_uri = RedirectUri.AbsoluteUri,
code = code
});
var accessToken = result.access_token;
// Store the access token in the session for farther use
Session["AccessToken"] = accessToken;
// update the facebook client with the access token so
// we can make requests on behalf of the user
fb.AccessToken = accessToken;
// Get the user's information
dynamic me = fb.Get("me?fields=first_name,middle_name,last_name,id,email");
string email = me.email;
string firstname = me.first_name;
string middlename = me.middle_name;
string lastname = me.last_name;
db.Insert_customer(firstname,email,null,null,null,null,null,null,null,null,null,null,1,1,System.DateTime.Now,1,System.DateTime.Now);
// Set the auth cookie
FormsAuthentication.SetAuthCookie(email, false);
return RedirectToAction("Index", "Home");
}
}
}
【讨论】:
截至 2019 年 1 月,我想确认如何执行此操作并提供一些额外的信息(有很多相互矛盾的信息,具体取决于答案的编写年份!)。 David 和 Waqas 有最好的答案 (IMO)。我正在使用 MVC5、AspNetIdentity 2 和 IdentityServer 3。
首先,您的 Facebook 身份提供商配置:
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
AuthenticationType = "facebook",
Caption = "Login with Facebook",
SignInAsAuthenticationType = signInAsType,
AppId = ConfigurationManager.AppSettings["FacebookAppId"],
AppSecret = ConfigurationManager.AppSettings["FacebookAppSecret"],
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = ctx =>
{
foreach (var claim in ctx.User)
{
var claimType = $"urn:facebook:{claim.Key}";
var claimValue = claim.Value.ToString();
if (!ctx.Identity.HasClaim(claim.Key, claimValue))
{
ctx.Identity.AddClaim(new Claim(claim.Key, claimValue));
}
}
return Task.FromResult(0);
}
}
});
与其他一些答案不同,这将额外请求的字段与您默认获得的字段相结合,并将 urn:facebook: 从声明的前面去掉,因此它与默认声明命名方案相匹配。
您不需要添加任何额外的Scopes 或Fields(至少不需要添加名字和姓氏)。 Microsoft.Owin.Security.Facebook 的 4.1 版已经为您完成了这项工作。 source code for the FacebookAuthenticationOptions is here。相关位:
public FacebookAuthenticationOptions()
: base(Constants.DefaultAuthenticationType)
{
Caption = Constants.DefaultAuthenticationType;
CallbackPath = new PathString("/signin-facebook");
AuthenticationMode = AuthenticationMode.Passive;
Scope = new List<string>();
BackchannelTimeout = TimeSpan.FromSeconds(60);
SendAppSecretProof = true;
_fields = new HashSet<string>();
CookieManager = new CookieManager();
AuthorizationEndpoint = Constants.AuthorizationEndpoint;
TokenEndpoint = Constants.TokenEndpoint;
UserInformationEndpoint = Constants.UserInformationEndpoint;
Scope.Add("public_profile");
Scope.Add("email");
Fields.Add("name");
Fields.Add("email");
Fields.Add("first_name");
Fields.Add("last_name");
}
如果您使用的是 IdentityServer 3(就像我一样),那么您需要在您的自定义 UserService 中获取这些身份验证声明,如下所示:
public async override Task AuthenticateExternalAsync(ExternalAuthenticationContext ctx)
{
// first, lets see if we have enough data from this external provider
// at a minimum, FirstName, LastName, and Email are required
string email = null;
string firstName = null;
string lastName = null;
var idp = ctx.ExternalIdentity.Provider;
email = GetClaimValue(ctx, "email");
if (idp == "google")
{
firstName = GetClaimValue(ctx, "given_name");
lastName = GetClaimValue(ctx, "family_name");
}
else if (idp == "facebook")
{
firstName = GetClaimValue(ctx, "first_name");
lastName = GetClaimValue(ctx, "last_name");
}
var missingClaims = "";
if (email == null)
{
missingClaims = "email";
}
if (firstName == null)
{
if (missingClaims.Length > 0) { missingClaims += ", "; }
missingClaims += "first name";
}
if (lastName == null)
{
if (missingClaims.Length > 0) { missingClaims += ", "; }
missingClaims += "last name";
}
if (missingClaims.Length > 0)
{
var errorMessage = $"The external login provider didn't provide the minimum required user profile data. Missing: {missingClaims} " +
"Verify that these fields are specified in your external login provider user profile and that you have allowed external apps (i.e. this one) access to them. " +
"Alternatively, you can try a different external login provider, or create a local acount right here.";
ctx.AuthenticateResult = new AuthenticateResult(errorMessage);
return;
}
var login = new Microsoft.AspNet.Identity.UserLoginInfo(ctx.ExternalIdentity.Provider, ctx.ExternalIdentity.ProviderId);
var user = await _userManager.FindAsync(login);
if (user == null)
{
// this user either does not exist or has not logged in with this identity provider
// let's see if they already exist (by checking to see if there is a user account with this email address)
user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
// there is no existing user with this email, therefore, a new user will be created
user = new MotoTallyUser()
{
Id = Guid.NewGuid(),
UserName = email,
Email = email,
EmailConfirmed = true,
FirstName = firstName,
LastName = lastName
};
await _userManager.CreateAsync(user);
await _userManager.AddLoginAsync(user.Id, login);
}
else
{
// this user DOES exist (matched email provided by external login provider)
// however, they have not logged in with this identity provider
// therefore, update the user info with that reported by the external identity provider, and add the external login
user.UserName = email;
user.Email = email;
user.EmailConfirmed = true;
user.FirstName = firstName;
user.LastName = lastName;
await _userManager.UpdateAsync(user);
await _userManager.AddLoginAsync(user.Id, login);
}
}
else
{
// this user DOES exist (they already have an external login on record)
// therefore, update the user info with that reported by the external identity provider (no need to add external login, its already there)
user.UserName = email;
user.Email = email;
user.EmailConfirmed = true;
user.FirstName = firstName;
user.LastName = lastName;
await _userManager.UpdateAsync(user);
}
ctx.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.Email, null, ctx.ExternalIdentity.Provider);
return;
}
private string GetClaimValue(ExternalAuthenticationContext ctx, string claimType)
{
if (ctx.ExternalIdentity.Claims.FirstOrDefault(x => x.Type == claimType) != null)
{
return ctx.ExternalIdentity.Claims.FirstOrDefault(x => x.Type == claimType).Value;
}
return null;
}
希望这对某人有所帮助!
【讨论】:
对于 VB.NET 开发人员,这是 Startup.Auth.vb 中的代码
Dim fb = New FacebookAuthenticationOptions()
fb.Scope.Add("email")
fb.AppId = "*"
fb.AppSecret = "*"
fb.Provider = New FacebookAuthenticationProvider() With
{
.OnAuthenticated = Async Function(context)
context.Identity.AddClaim(New System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken))
For Each claim In context.User
Dim claimType = String.Format("urn:facebook:{0}", claim.Key)
Dim claimValue As String = claim.Value.ToString()
If Not context.Identity.HasClaim(claimType, claimValue) Then context.Identity.AddClaim(New System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"))
Next
End Function
}
fb.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie
app.UseFacebookAuthentication(fb)
【讨论】:
Facebook 在升级 2.4 中改变了他们的 Graph API 返回值的方式。现在您需要明确指定您想要返回的所有字段。
请参阅此说明:facebook for developers Upgrade Info:
2.4 版中的图形 API 更改
过去,来自 Graph API 调用的响应会返回一组默认字段。为了减少有效载荷大小并改善移动设备的延迟 我们减少了返回的默认字段数量的网络 大多数 Graph API 调用。在 v2.4 中,您需要以声明方式列出 您的呼叫的响应字段。
从 facebook 获取电子邮件、名字和姓氏:
首先需要安装Facebook SDK for .NETnuget包
然后,在您的 startup.Auth.cs 中,将 Facebook Authentication 的配置更改如下:
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
// put your AppId and AppSecret here. I am reading them from AppSettings
AppId = ConfigurationManager.AppSettings["FacebookAppId"],
AppSecret = ConfigurationManager.AppSettings["FacebookAppSecret"],
Scope = { "email" },
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
return Task.FromResult(true);
}
}
});
// this is no longer needed
//app.UseFacebookAuthentication(
// appId: ConfigurationManager.AppSettings["FacebookAppId"],
// appSecret: ConfigurationManager.AppSettings["FacebookAppSecret"]);
最后,在您的 AccountController 中,将以下代码添加到 ExternalLoginCallback 方法:
if (string.Equals(loginInfo.Login.LoginProvider, "facebook", StringComparison.CurrentCultureIgnoreCase))
{
var identity = AuthenticationManager.GetExternalIdentity(DefaultAuthenticationTypes.ExternalCookie);
var access_token = identity.FindFirstValue("FacebookAccessToken");
var fb = new FacebookClient(access_token);
// you need to specify all the fields that you want to get back
dynamic myInfo = fb.Get("/me?fields=email,first_name,last_name");
string email = myInfo.email;
string firstName = myInfo.first_name;
string lastName = myInfo.last_name;
}
请参阅facebook API Guid 了解更多您可以取回的参数。
【讨论】:
在 facebook 选项范围中添加名字和姓氏
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = "your app id",
AppSecret = "your app secret",
};
facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("first_name");
facebookOptions.Scope.Add("last_name");
return facebookOptions;
【讨论】: