2013-10-18 18 views
13

我试图检索返回作为OnAuthenticated背景和添加下面这个例子中的权利要求用户属性:How to access Facebook private information by using ASP.NET Identity (OWIN)?如何访问Microsoft.Owin.Security.xyz OnAuthenticated上下文AddClaims值?

我可以看到数据,我希望在登录时被返回,被添加为在Starup.Auth.cs中声明。但是,当我在帐户控制器中时,出现在UserManager或UserStore中的唯一声明由LOCAL AUTHORITY颁发。 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似乎要注意声明属性。从身份中稍后检索它们时,属性为空。

回答

16

我能够创建一个工作示例,使用MVC 5 RTM模板,OWIN和ASP.NET身份位。你可以找到完整的源和现场工作示例的链接在这里:https://github.com/johndpalm/IdentityUserPropertiesSample

下面是我工作:

(此处插入提供商名称)创建一个新的AuthenticationOptions在Startup.ConfigureAuth对象(StartupAuth.cs ),将客户端ID,客户端密钥和新的AuthenticationProvider传递给它。您将使用一个lambda表达式来传递OnAuthenticated方法的一些代码,以将声明添加到包含您从context.Identity中提取的值的标识中。

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 provider作为模型),则无法登录。如果您只选择access_token参数,则它工作正常。似乎有些参数会破坏登录过程。 (An issue has been opened on katanaproject.codeplex.com if progress on this is of interest to you.)如果我找到原因,我会更新。除了验证我可以获得access_token之外,我没有用Twitter或Google做太多工作。

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中提取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); 
     } 
    } 
} 

最后,我想显示什么值不是来自地方政府。我创建了出现在/Account/Manage page上的部分视图_ExternalUserPropertiesListPartial。我得到我以前从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>(); 
} 

而只是为了彻底,认为:

_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

还有任何反馈,更正或改进将不胜感激。

+0

当我使用你的代码时,我得到了一个Google 404的错误代码。我可以告诉Google不再支持这个,oauth2。 –

1

因此本文解释了这一切是如何工作得很好:Decoupling owin external auth

但是简单的答案是,当你获得来自Facebook的身份验证,这是给你的外部标识。然后,您需要获取该外部身份并“登录”本地应用身份,其中需要您将所需的来自外部身份的声明添加到成为User.Identity的ClaimsIdentity。

编辑:为了进一步澄清,你可以做到这一点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); 

此ClaimsIdentity将添加您的声明,您需要将该声明添加到在SignInAsync方法中创建的身份以显示。

+0

浩,这是我的理解。当我在ExternalLoginCallback调试我没有看到任何指控,但“地方政府”的AuthenticationManager会(或的UserManager)。我应该在哪里看到我在上面的代码中添加的声明? –

+0

我有完全相同的问题。我添加了声明,我可以看到他们被添加,但是当我尝试从上下文中读取它们时,所有我看到的都是一个本地AUTHORITY声明:/ –

+0

我设法从任何位置获取信息的唯一方法Startup.Auth类是通过一个非常可怕的cookie,需要加密。会话不可用,并且AddClaim方法为空。每次添加一个加密的cookie似乎都是错误的。 –

0

在短行所需一旦AddClaim使用如下:

来自约翰两者回答以上。

ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);