I realise this question has been asked and answered a lot in the last 10 years, but I need a more modern answer than most I could find.
I have an ASP.Net MVC project (C#), and have followed the MS guide to enabling Facebook and Google+ authentication. This was super simple, register the apps in the providers and uncomment three lines each in startup.auth.cs - super, super simple. It also "just works", bango I have new users created in the database that can login via these providers. Wunderbar.
Like so many applications though I need more than authentication, I also need a small amount of identity - firstname, surname, email, birthday, profile image where available.
Using a combination of Scope and Fields in the FacebookAuthenticationOptions I have managed to get the Facebook authorization screen to warn the user that the app wants their birthday, but try as I might I can't find anywhere that this information is returned in the OAuth process or any claims that might represent the half a dozen other fields I've asked for.
Many "solutions" online talked about overriding the "OnAuthenticated" delegate of the FacebookAuthenticationOptions.Provider (which is a FacebookAuthenticationProvider). I've pasted in some stolen code but it never hits the breakpoint in there.
After I get this working with Facebook, I had hoped to repeat with Google+, Twitter, and Microsoft account providers and I'm hoping it's a standardised approach where I can tell each provider what fields I want in their format, then get them all out using a standard getter somewhere. As I understand it that is actually the whole underlying point of OAuth - no?
So in my startup.auth.cs (fake Id and secret of course):
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
AppId = "xxxxxxxxxxx",
AppSecret = "xxxxxxxxxxxxxxxxxxxxx",
Scope = { "user_birthday", "public_profile", "email" },
Fields = { "user_birthday", "picture", "name", "email", "gender", "first_name", "last_name" },
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = async ctx =>
{
Console.WriteLine("auth"); //this breakpoint never hits
ctx.Identity.AddClaim(new Claim("FacebookAccessToken", ctx.AccessToken));
foreach (var claim in ctx.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!ctx.Identity.HasClaim(claimType, claimValue))
{
ctx.Identity.AddClaim(new Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
}
}
}
});
Thanks to @Jeremy Thompson in the comments that pointed me to what I originally thought was just another copy of the same thing I stole in the first place, but actually triggered me to solve this myself in a way.
The solution Jeremy linked to "worked", it looked much like my original code except the OnAuthenticated method was firing. Unfortunately it didn't actually have the birthday in it, so I added it, and then it stopped working. Aha...
So glazing over the how I got there with this, the problem seems to be that a silent exception occurs which prevents this OnAuthenticated from firing which in my case was because I had a field name incorrect. Here is the working code which returns my name, email, birthday, current town, and a profile picture (the essentials you need to create a profile in your own system!)
In the OWIN startup class of course:
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
AppId = "...", //Your App Id of course
AppSecret = "...", //Your App Secret of course
Scope = { "user_birthday", "public_profile", "email", "user_gender", "user_location" },
Fields = { "birthday", "picture", "name", "email", "gender", "first_name", "last_name", "location" }, //notice the fields are not prefixed with "user_". This was
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = async ctx =>
{
ctx.Identity.AddClaim(new Claim("FacebookAccessToken", ctx.AccessToken));
foreach (var claim in ctx.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!ctx.Identity.HasClaim(claimType, claimValue))
{
ctx.Identity.AddClaim(new Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
}
}
}
});
And now in my AccountController's ExternalLoginCallback function I can access a heap of properties by looking at the loginInfo.Claims property (loginInfo as below).
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();