Expanded login scopes in Azure Mobile Services

A while back I wrote a post about getting more information about the logged in user in your mobile service, and this has been by far the most viewed post in the list I wrote about mobile services. By getting the identity object for the logged in user, the server scripts could talk to the authentication providers to get more information about the users, and with that have additional information (other than some id) about the users of their mobile applications. But that feature had some limitations, mainly related to the amount of information available from the providers to the user scripts. The issue was that the authentication scopes requested by the login flow was fixed (for example, the user’s e-mail was not available). With a feature we announced a couple of weeks back, this limitation is now removed. Notice that the feature is still in preview mode, so there’s not a lot of documentation out there, which is why I decided to write about it on this post.

The change made in the release 1.6.4247 of the Azure Mobile Services allows us to specify custom scopes which will be passed to the authentication providers when doing a server-side (i.e., web-based) authentication. That will, in turn, make the access token given by the getIdentities method in the users object more powerful, in a sense that the script can now use it to request additional information from the provider API.

Quick note before moving on: most of the for this blog post can be found in my blogsamples repository in GitHub, under AzureMobileServices/GettingUserInfoFromAuthProviders, so the code in the post will be simplified (i.e., no error handling, some code duplication) but you can find the full code in the repository.

Google

Let’s start with Google, for example. By default, the authentication flow will give the app (via the access token) some basic information about the user (more specifically, the information from the https://www.googleapis.com/auth/userinfo.profile scope). So if a logged in user calls an API with the following code:

  1. exports.get = function (request, response) {
  2.     request.user.getIdentities({
  3.         success: function (identities) {
  4.             var http = require('request');
  5.             console.log('Identities: ', identities);
  6.             var url = 'https://www.googleapis.com/oauth2/v3/userinfo' +
  7.                 '?access_token=' + identities.google.accessToken;
  8.             var reqParams = { uri: url, headers: { Accept: 'application/json' } };
  9.             http.get(reqParams, function (err, resp, body) {
  10.                 var userData = JSON.parse(body);
  11.                 response.send(200, userData);
  12.             });
  13.         }
  14.     });
  15. };

They will retrieve a response similar to this one (which I got when I signed in with my Google account):

{
"sub": "my-google-id",
"name": "Carlos Figueira",
"given_name": "Carlos",
"family_name": "Figueira",
"profile": "https://plus.google.com/my-google-plus-profile-number",
"picture": "https://lh3.googleusercontent.com/some-path/some-other-path/yet-another-path/photo.jpg",
"gender": "male",
"locale": "en"
}

But the user e-mail isn’t one available, and there was no way for the application to request more information from the user. Let’s now request an additional scope from the Google login, by going to the configure tab in the portal, and set the ‘MS_GoogleScope’ application setting:

001-GoogleScopes

Now if I login again to the mobile service and invoke the same API, we’ll see that we have some additional information in the response:

{
"sub": "my-google-id",
"name": "Carlos Figueira",
"given_name": "Carlos",
"family_name": "Figueira",
"profile": "https://plus.google.com/my-google-plus-profile-number",
"picture": "https://lh3.googleusercontent.com/some-path/some-other-path/yet-another-path/photo.jpg",
"email": "my-email@my-provider.com",
"email_verified": true,

"gender": "male",
"locale": "en"
}

Microsoft

For two other providers, the idea is the same. Let’s take a look at Microsoft next. By default, the login requests only the ‘wl.basic’ scope. But if we want to get more information about the user, we can do it the same way. For a very similar API (which talks to the Live, instead of Google API), here’s the result of a request to the /me endpoint:

{
"id": "my-live-id",
"name": "Carlos Figueira",
"first_name": "Carlos",
"last_name": "Figueira",
"link": "https://profile.live.com/",
"gender": null,
"locale": "en_US",
"updated_time": "2013-12-10T16:03:43-08:00"
}

But if I set some additional scopes:

002-MicrosoftScopes

I’d get the additional information I requested after logging in again:

{
"id": "my-live-id",
"name": "Carlos Figueira",
"first_name": "Carlos",
"last_name": "Figueira",
"link": "https://profile.live.com/",
"gender": null,
  "emails": {
"preferred": "carlosfigueirafilho@hotmail.com",
"account": "carlosfigueirafilho@hotmail.com",
"personal": null,
"business": null
},

"locale": "en_US",
"updated_time": "2013-12-10T16:03:43-08:00"
}

Facebook

For Facebook is the same thing: we can ask for additional scopes (using the ‘MS_FacebookScope’ app setting) and the token we get to talk to the Facebook Graph API gives us more access to the additional resources. Along in the same lines of the previous examples, let’s use this API to get some information about our logged in user.

  1. exports.get = function (request, response) {
  2.     request.user.getIdentities({
  3.         success: function (identities) {
  4.             var http = require('request');
  5.             console.log('Identities: ', identities);
  6.             var url = 'https://graph.facebook.com/me?fields=id,name,birthday,hometown,email&access_token=' +
  7.                 identities.facebook.accessToken;
  8.  
  9.             var reqParams = { uri: url, headers: { Accept: 'application/json' } };
  10.             http.get(reqParams, function (err, resp, body) {
  11.                 var userData = JSON.parse(body);
  12.                 response.send(200, userData);
  13.             });
  14.         }
  15.     });
  16. };

And if I call it without any custom scopes, I only get the users id and name:

{
"id": "my-facebook-id",
"name": "Carlos Figueira"
}

But if I request additional scopes for that provider:

003-FacebookScopes

I’ll be able to call the same API (after logging in again) and get that additional data.

{
"email": "my-email@my-provider.com",
"id": "my-facebook-id",
"name": "Carlos Figueira",
"birthday": "my-birthday",
"hometown": {
"id": "101883789854098",
"name": "Recife, Brazil"
}
}

Final notes

As a good general rule, only request the minimum information you need from the user. Many users don’t like giving out a lot of information to the apps they use, so they may simply give up using your app just because they’re being asked too much when logging in.

Also, you may have noticed that there’s no setting for requesting custom scopes in the Twitter authentication. For that provider, “scope” for the twitter API can be controlled by the twitter application itself, so there’s no need for the custom application setting.