Azure

Facebook Authentication with Azure

Important Note

Microsoft have recently released Azure App Services which aims to replace Mobile Services. I will be writing an updated guide shortly.


 

Azure Mobile Services (AMS) is a great platform for .NET developers to build complex apps that require a backend in almost no time and at very competitive prices. I’ve been using it for about 6 months to develop a beer tracking app called BeerDrinkin.

One of the requirements of BeerDrinkin was its ability to sync account data accross all the users devices and AMS makes this incredibly easy with offline sync.

I couldn’t help but feel that if I’m going to be storing data in Azure, showing what beers people love and hate, then I really should be doing something more interesting than simply syncing to a handful of devices. This is why I decided to try and build a beer recommendation engine into BeerDrinkins backend. The aim is to make use of Azure Machine Learning to make suggestions about what beers you might like based on your constumption history and those of your peers.

In order to build a users profile to feed into machine learning, I needed more information than the simple GUID that AMS returns when calling the FacebookLoginProvier within the ConfigOptions of a Mobile Service WebApiConfig class.

The information that I wanted to have about the user:

  • Email Address
  • First Name
  • Last Name
  • Gender
  • Date of Birth

I will be adding additional data about users at various points during the users interaction with the app. One example is location data and I even plan on recroding local weather conditions for each beer the user checks in. With this, I can use machine learning to predict beers based on current weather conditions. This is important as on a warm day, most people will likely want a lager rather than a thick, blood warming Belgian double.

Creating a custom LoginProvider

To fetch the extra information, I needed to do a couple of things. First things first, I needed to remove the default FacebookLoginProvider from my ConfigOptions. To do this I called the following:


options.LoginProviders.Remove(typeof(FacebookLoginProvider));

I then went ahead and created a new class which I named CustomFacebookLoginProvider which importantly overrides the CreateCentials method.

public class CustomFacebookLoginProvider : FacebookLoginProvider
{
public CustomFacebookLoginProvider(HttpConfiguration config, IServiceTokenHandler tokenHandler)
: base(config, tokenHandler)
{
}

public override ProviderCredentials CreateCredentials(ClaimsIdentity claimsIdentity)
{
var accessToken = string.Empty;
var emailAddress = string.Empty;
foreach (var claim in claimsIdentity.Claims)
{
if (claim.Type == "Zumo:ProviderAccessToken")
{
accessToken = claim.Value;
}

if (claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")
{
emailAddress = claim.Value;
}
}
}
}

I now had some basic information regarding the user, such as their Facebook token (which is essential for gathering more information) and their email address. I then use a 3rd party Nuget package to query Facebook’s opengraph for more information regarding the user.

The entire method in BeerDrinkin’s Azure backend looks something like this:

public class CustomFacebookLoginProvider : FacebookLoginProvider
{
public CustomFacebookLoginProvider(HttpConfiguration config, IServiceTokenHandler tokenHandler)
: base(config, tokenHandler)
{
}

public override ProviderCredentials CreateCredentials(ClaimsIdentity claimsIdentity)
{
var accessToken = string.Empty;
var emailAddress = string.Empty;
foreach (var claim in claimsIdentity.Claims)
{
if (claim.Type == "Zumo:ProviderAccessToken")
{
accessToken = claim.Value;
}

if (claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddres")
{
emailAddress = claim.Value;
}
}

if (string.IsNullOrEmpty(accessToken))
return null;

var client = new FacebookClient(accessToken);
dynamic user = client.Get("me");

DateTime dateOfBirth;
DateTime.TryParse(user.birthday, out dateOfBirth);

//Keeping userItem for the moment but may well kill it. I was going to seperate userItem into public info and accountItem into public
var userItem = new UserItem
{
Id = user.id,
};

var accountItem = new AccountItem
{
Id = userItem.Id,
Email = emailAddress,
FirstName = user.first_name,
LastName = user.last_name,
IsMale = user.gender == "male",
DateOfBirth = dateOfBirth,
AvatarUrl = $"https://graph.facebook.com/{userItem.Id}/picture?type=large"
};

var context = new BeerDrinkinContext();
if (context.UserItems.FirstOrDefault(x => x.Id == userItem.Id) != null)
return base.CreateCredentials(claimsIdentity);

context.AccountItems.Add(accountItem);
context.UserItems.Add(userItem);
context.SaveChanges();

return base.CreateCredentials(claimsIdentity);
}
}

Using the CustomFacebookLoginProvider

In order to use my implementation of the login provider, I needed to go ahead and add it to the ConfigOptions.

options.LoginProviders.Add(typeof(CustomFacebookLoginProvider));

Scopes

One thing I forgot when I first implemented this approach was ensuring that I updated my scopes. By default, Facebook wont provide me with the information I require. In order to have Azure Mobile Service pass the correct request to Facebook, I needed to log into my Azure management portal and add MS_FacebookScope to my App Settings. The exact scope I’m requesting is email user_birthday user_friends.

Disclaimer

BeerDrinkin (espically the backend) is worked on mostly whilst ‘testing’ the app (drinking beer). Some of the code is horrible and need to be refactored. The above code could 100% do with a tidy up but works so I’ve left it as is. The project is on GitHub.so please do contribute if you can.

2 thoughts on “Facebook Authentication with Azure”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s