As I've found out here, I can't call
HttpContext.GetOwinContext().Authentication.SignIn(...)
in FacebookAuthenticationProvider OnAuthenticated
event. It seems to be a different context.
My users will be from Facebook, which means that I will need to retrieve data such as id
, email
... This information is accessible on the OnAuthenticated
event and I need this information to SignIn.
I need to access this information on my controller...
I tried this in the event:
context.OwinContext.Set("FacebookUser", rawUserObjectFromFacebookAsJson);
But on the controller if I try to retrieve this, it is null.
var fbuser = HttpContext.GetOwinContext()
.Get<Newtonsoft.Json.Linq.JObject>("FacebookUser");
So my question is, how can I pass this data to the controller so I can sign in?
I found a way to do this by looking at this answer and figuring out what I could do.
Using Claims
I can add all values retrieved from Facebook and throw into identity claims.
OnAuthenticated = (context) =>
{
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
var rawUserObjectFromFacebookAsJson = context.User;
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);
}
Then on my controller I can get that identity by using this
ClaimsIdentity identity = await HttpContext.GetOwinContext().Authentication
.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
Then I will have on my action
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
ClaimsIdentity identity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var user = new IdentityUser()
{
Id = identity.GetUserId(),
UserName = identity.Name,
};
await LoginAsync(user, identity);
if (!identity.IsAuthenticated)
{
return RedirectToAction("Login");
}
return RedirectToAction("Index", "Home");
}
And my LoginAsync method
private async Task LoginAsync(IdentityUser user, ClaimsIdentity identity)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
// I can't just use the identity I got on Facebook
// I need to create this one, or else it will not signin properly
// The authentication type has to be ApplicationCookie and the property
// is readonly, so...
var userIdentity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// now we have to transfer the claims, adding a check to avoid duplicates
foreach (var claim in identity.Claims)
{
if (!userIdentity.HasClaim(c => c.Type == claim.Type))
userIdentity.AddClaim(claim);
}
// then it will signin successfully
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true }, userIdentity);
}
Then I can access
HttpContext.GetOwinContext().Authentication.User.Claims
at any time and retrieve what I need.