Xamarin.Forms Social Authentication with Azure Mobile Services – Part 3

Ok, in the previous two articles (part one and part two) we successfully created an app that authenticates a user using social login. Now we will learn how to get extra information from this user, like his name, email, etc.

Get extra information from the user

One of Azure’s biggest faults in my opinion is that it doesn’t provide user details that we normally need like name and email.  In order to do that the first thing we need to do is tell Azure to request that information from the providers. For this example the only extra information I’m requesting is the user’s email, if you need more just check the specific provider’s API and add those to the scope as well. Here’s how we do it:

  1. Go to your Azure account and select your Mobile Service
  2. Click on the tab Configure and scroll down to app settings
  3. Here we’ll add two settings MS_GoogleScope and MS_FacebookScope

Ok, now when Azure requests the user authentication it will request for the user’s email and we’ll be able to retrieve that information now. In the next step we need to create a custom API in our Mobile Services at Azure.

  1. Go to your Azure account and select your Mobile Service
  2. Click on the tab API and then on Create at the bottom of the page
  3. Choose a name and select your desired configuration, this is how mine looks
  4. At the script tab paste the code below

[code lang=”js”]exports.get = function(request, response) {
var currentUser = request.user;
currentUser.getIdentities({
success: function (identities) {
var url = null;
var oauth = null;
if (identities.google) {
url =
‘https://www.googleapis.com/oauth2/v3/userinfo?access_token=’
+ identities.google.accessToken;
} else if (identities.facebook) {
url = ‘https://graph.facebook.com/me?access_token=’
+ identities.facebook.accessToken;
} else if (identities.twitter) {
var userId = currentUser.userId;
var twitterId = userId.substring(userId.indexOf(‘:’) + 1);
url = ‘https://api.twitter.com/1.1/users/show.json?user_id=’
+ twitterId;
var consumerKey = process.env.MS_TwitterConsumerKey;
var consumerSecret = process.env.MS_TwitterConsumerSecret;
oauth = {
consumer_key: consumerKey,
consumer_secret: consumerSecret,
token: identities.twitter.accessToken,
token_secret: identities.twitter.accessTokenSecret
};
}

if (url) {
var requestCallback = function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.error(‘Error sending data to the provider: ‘,
err);
request.respond(statusCodes.INTERNAL_SERVER_ERROR,
body);
} else {
try {
var userData = JSON.parse(body);
response.send(statusCodes.OK,
{ message : userData });
} catch (ex) {
console.error(
‘Error parsing response from the provider API: ‘,
ex);
request.respond(statusCodes.INTERNAL_SERVER_ERROR,
ex);
}
}
}
var req = require(‘request’);
var reqOptions = {
uri: url,
headers: { Accept: "application/json" }
};
if (oauth) {
reqOptions.oauth = oauth;
}
req(reqOptions, requestCallback);
} else {
response.send(statusCodes.OK, { message : ‘Error!’ });
}
}
});
};[/code]

This is how it looks:

What this script does is get the user that logged in, request the extra information from the providers (in this case Facebook, Google+ and Twitter) and return that information to the app.

Now let’s make some changes to the app…

The first thing I did was to create a class to deserialize the JSON we get from our custom API:

[code lang=”csharp”]public class SocialLoginResult
{
public Message Message { get; set; }
}

public class Message
{
public string SocialId
{
get { return string.IsNullOrEmpty(Sub) ? Id : Sub; }
}

public string Email { get; set; }
public string Sub { get; set; }
public string Id { get; set; }
}[/code]

Facebook and Twitter return Id as their unique identifier for the user while Google uses Sub, that’s why I created a property named SocialId to retrieve this information regardless of provider.

Now we go back to our SocialLogin class and add this method:

[code lang=”csharp”]private static async Task<SocialLoginResult> GetUserData()
{
return await Client.InvokeApiAsync<SocialLoginResult>(
"getextrauserinfo",
HttpMethod.Get, null);
}[/code]

Now we change the methods for the providers AuthenticateGoogle()AuthenticateTwitter() and AuthenticateFacebook() to return SocialLoginResult instead of MobileServiceUser. This is what they will look like:

[code lang=”csharp”]public static async Task<SocialLoginResult> AuthenticateFacebook()
{
await Authenticate(MobileServiceAuthenticationProvider.Facebook);
return await GetUserData();
}[/code]

Since the client has the Azure token we don’t need to do anything else here, just make a request to the API and Azure will “know” which user we’re talking about.

The final step is to change our App.cs class. We need to change all buttons to use the email instead of Azure’s id:

[code lang=”csharp”]fbButton.Clicked += async (sender, args) =>
{
var user = await SocialLogin.AuthenticateFacebook();
welcomeLabel.Text = string.Format("Welcome {0}!", user.Message.Email);
};[/code]

Perfect, now we test our app, and when we return from the provider we see…

Yes, the email is there 🙂

That was it, I hope it was helpful to you!

Final project on GitHub




6 Comments

Great article.
I have been trying to implement this in my application (not exactly as you made) however, when i call Client.InvokeApiAsync(“myApi”, HttpMethod.Get, null); the following error is thrown:
Cannot read property ‘getIdentities’ of undefined
Do you have any tip to help me? Thanks!!

Well, are you sure the function was created? It’s been almost two years since I wrote this article, so I’m not sure of any recent changes… but I’d start there. Did you create the function, did you paste the script?

Yes, I am getting the same error too. I simplified your function to the following for debugging purposes:

exports.get = function(request, response) {
var currentUser = request.user;
console.log(‘user: ‘, request.user);
// currentUser.getIdentities({
// success: function (identities) {
//
// }
// });
};

Even in this case, the Console output on Azure shows that request.user is undefined. I know it has been a while since you posted this article. But can you try again on Azure? Maybe something has changed on Azure since you posted. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *