我正在尝试检索作为 OnAuthenticated 上下文返回的用户属性,并作为声明添加到以下示例之后:如何使用 ASP.NET 身份 (OWIN) 访问 Facebook 私人信息?
我可以看到我期望的数据在登录时返回,并作为声明添加到Starup.Auth.cs中。 但是,当我在帐户控制器中时,用户管理器或用户存储中出现的唯一声明是由地方当局颁发的。 找不到Facebook(或其他外部提供商)的索赔。 添加到上下文中的声明最终会在哪里?(我使用的是VS2013 RTM。
Azure 上的完整源和实时站点链接在此处:https://github.com/johndpalm/IdentityUserPropertiesSample/tree/VS2013rtm
以下是我在Startup.Auth.cs中的内容:
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) =>
{
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
foreach (var x in context.User)
{
var claimType = string.Format("urn:facebook:{0}", x.Key);
string claimValue = x.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook"));
}
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
return Task.FromResult(0);
}
}
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);
捕获外部登录属性的另一种方法是为访问令牌添加单个声明,并用属性填充它:
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
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) =>
{
var claim = new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook");
foreach (var x in context.User)
{
string key = string.Format("urn:facebook:{0}", x.Key);
string value = x.Value.ToString();
claim.Properties.Add(key, value);
}
context.Identity.AddClaim(claim);
return Task.FromResult(0);
}
}
};
注意 - 此示例不起作用:尽管传递带有属性的单个声明会很好。 外部 cookie 似乎注意到尊重声明属性。 稍后从标识中检索属性时,这些属性为空。
我能够使用 MVC 5 RTM 模板、OWIN 和 ASP.NET 标识位创建一个工作示例。 您可以在此处找到完整的源代码和实时工作示例的链接:https://github.com/johndpalm/IdentityUserPropertiesSample
以下是对我有用的方法:
在 Startup.ConfigureAuth (StartupAuth.cs) 中创建一个新的(在此处插入提供程序名称)AuthenticationOptions 对象,并向其传递客户端 ID、客户端密钥和新的 AuthenticationProvider。 您将使用 lambda 表达式向 OnAuthenticated 方法传递一些代码,以将声明添加到包含从上下文中提取的值的标识。身份。
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"));
foreach (var x in context.User)
{
var claimType = string.Format("urn:facebook:{0}", x.Key);
string claimValue = x.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, XmlSchemaString, "Facebook"));
}
return Task.FromResult(0);
}
}
};
app.UseFacebookAuthentication(facebookOptions);
}
注意:Facebook 身份验证提供程序使用此处使用的代码。 如果您将相同的代码与Microsoft帐户提供程序(或我使用 MS 帐户代码作为模型创建的 Foursquare 提供程序)一起使用,则无法登录。 如果仅选择 access_token 参数,则工作正常。 似乎某些参数会破坏登录过程。(如果您对此的进度感兴趣,katanaproject.codeplex.com 已打开一个问题。如果我找到原因,我会更新。除了验证我能得到access_token之外,我在Twitter或谷歌上没有做太多事情。
var msaccountOptions = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings.Get("MicrosoftClientId"),
ClientSecret = ConfigurationManager.AppSettings.Get("MicrosoftClientSecret"),
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:microsoftaccount:access_token", context.AccessToken, XmlSchemaString, "Microsoft"));
return Task.FromResult(0);
}
}
};
app.UseMicrosoftAccountAuthentication(msaccountOptions);
在AccountController中,我使用外部cookie从AuthenticationManager中提取ClaimIdentity。 然后,我将其添加到使用应用程序 Cookie 创建的标识中。 我忽略了任何以"...schemas.xmlsoap.org/ws/2005/05/identity/claims",因为它似乎破坏了登录。
帐户控制器.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);
}
}
}
最后,我想显示任何不是来自地方当局的值。 我创建了一个显示在"/帐户/管理"页面上的部分视图_ExternalUserPropertiesListPartial。 我从 AuthenticationManager.User.Claims 获取以前存储的声明,然后将其传递给视图。
帐户控制器.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
任何反馈、更正或改进将不胜感激。
本文很好地解释了这一切是如何工作的: 解耦外部身份验证
但简短的回答是,当你从Facebook获得身份验证时,这给了你一个外部身份。 然后,需要获取该外部标识并"登录"本地应用标识,在该步骤中,需要将所需的任何声明从外部标识添加到成为 User.Identity 的 ClaimIdentity。
编辑:为了进一步澄清,您可以在ExternalLoginCallback中执行此操作:
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl) {
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null) {
return RedirectToAction("Login");
}
// Sign in this external identity if its already linked
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null) {
await SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent) {
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
因此,您需要将额外的数据传递到登录,如下所示:
ClaimsIdentity id = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
此声明标识将具有你添加的声明,你需要将该声明添加到在 SignInAsync 方法中创建的标识,才能显示它。
简而言之,使用 AddClaim 后所需的行如下所示:
取自上面的约翰斯回答。
ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);