How do I access Microsoft.Owin.Security.xyz OnAuthenticated context AddClaims data?

I am trying to get custom properties that are returned in the OnAuthenticated context and added as claims in the following example: How do I access Facebook personal information using an ASP.NET identifier (Owin)?

I see that the data that I expect is returned at login and added as a claim to Starup.Auth.cs. But when I am in the Account Controller, the only complaints that appear in the UserManager or UserStore are issued by LOCAL AUTHORITY. No claims can be found for Facebook (or other external providers). Where do claims added to context end? (I am using VS2013 RTM.)

The full source and live Azure website linked here: https://github.com/johndpalm/IdentityUserPropertiesSample/tree/VS2013rtm

Here is what I have in 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); 

An alternative way to capture the external properties of the input is to add a separate application for the access token and fill it with properties:

 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); } } }; 

NOTE. This example does not work: although it would be nice to pass one requirement with properties. An external cookie seems to honor the claims properties. Properties become empty when they are retrieved later from the identifier.

+13
facebook asp.net-mvc asp.net-identity owin
Oct 18 '13 at 17:49
source share
3 answers

I managed to create a working example using the RTM MVC 5 patterns, the OWIN bit and ASP.NET. You can find the full source and link to a live working example here: https://github.com/johndpalm/IdentityUserPropertiesSample

Here is what worked for me:

Create a new (enter the vendor name here) AuthenticationOptions object in Startup.ConfigureAuth (StartupAuth.cs), passing it the client ID, client secret and new AuthenticationProvider. You will use a lambda expression to pass some code to the OnAuthenticated method to add claims to an identifier that contains values โ€‹โ€‹that you extract from the 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); } 

NOTE. The auth provider of Facebook works with the code used here. If you use the same code from a Microsoft account provider (or Foursquare provider that I created using the MS account code as a model), it cannot log in. If you select only the access_token parameter, it will work fine. It seems that some parameters interfere with the login process. ( The problem was discovered at katanaproject.codeplex.com if your interest in this is interesting. ) I will update if I find the reason. I havenโ€™t done much with Twitter or Google without checking that I can get access_token.

 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); 

In AccountController, I am extracting ClaimsIdentity from the AuthenticationManager using an external cookie. Then I add it to the identifier created using the application cookie. I ignored any claims that begin with "... schemas.xmlsoap.org/ws/2005/05/identity/claims" as it seemed to violate the login.

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); } } } 

And finally, I want to display any values โ€‹โ€‹from LOCAL AUTHORITY. I created a partial view of _ExternalUserPropertiesListPartial , which is displayed on the / Account / Manage page. I receive claims that I previously stored in AuthenticationManager.User.Claim and then submitted them to the submission.

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>(); } 

And just to be thorough, the view:

_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> } 

Again a working example and full code are in GitHub: https://github.com/johndpalm/IdentityUserPropertiesSample

And any feedback, corrections or improvements will be appreciated.

+16
Oct 23 '13 at 0:20
source share

So this article explains how all this works very well: owin external auth decoupling

But the short answer is: when you get authentication from facebook, it gives you an external identity. Then you need to accept this external identification and โ€œlog inโ€ to the local application name, in this step, so that you need to add any claims that you want from the external identity to the ClaimsIdentity, which becomes User.Identity.

Edit: To clarify, you can do this inside 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); } 

So, you will need to pass additional data to SignIn, which will look something like this:

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

In this ClaimsIdentity statement, your requirement will be added, and you will need to add this claim to the identifier created in the SignInAsync method so that it displays.

+1
Oct 19 '13 at 1:11
source share

In short, the line that is required after using AddClaim is as follows:

Taken from johns answer above.

 ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie); 
0
Oct 26 '13 at 6:34 on
source share



All Articles