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.