Questions? Feedback? powered by Olark live chat software
Hopp over navigasjon

Custom Login Scopes, Single Sign-On, new ASP.NET Web API – updates to the Azure Mobile Services .NET backend

Posted on 2 oktober, 2014

SR. Software Engineer, Azure Mobile Services
We just released many updates to the .NET backend of Azure Mobile Services, including:
  • Custom scopes from authentication providers
  • Single sign-on support for Windows Store applications
  • Updated dependency to Web API 5.2
If you are new to Azure Mobile Services .NET then check out this great overview and here is more information about Azure Mobile Services in general.

Getting the updates

As has been the case, you can get the latest updates to your project via the NuGet Package Explorer. Right-click the project node or the references node in your project in the solution explorer, then select the “Manage NuGet Packages” option: 001-ManageNuGetPackages In the NuGet dialog, select the “Updates” option in the left, and type “mobileservices” in the search box. You should see the latest version of the mobile services-related packages, with the latest version (1.0.402). Select the packages and click the “Update” button. 002-UpdateNuGetPackages Notice: there are some dependencies which cannot be updated at this moment, so don’t use the “Update All” button. If you update only the mobile service-related packages, it will automatically update any dependencies that it works well with.

Custom Scopes for Social Authentication Providers

This has been a long-standing feature request, which is present in the node.js backend. When you log in to the .NET backend, you can ask in the server for a token to talk to the authentication providers. By default, the token only grants some basic information about the user. Now, in the .NET backend, you can also request additional login scopes so that the access token which you receive at the server can be used to retrieve more information from the authentication provider. Like in the node.js backend, this feature is available for Facebook, Google and Microsoft accounts. Like in the node.js backend, the login scopes can be defined using app settings, which can be set in the “configure” tab in the portal: MS_FacebookScope, MS_GoogleScope and MS_MicrosoftScope (for Facebook, Google and Microsoft accounts, respectively). Let’s look at an example of those scopes being used. I’ve set up a .NET mobile service with authentication with two of the providers mentioned above (Facebook and Microsoft). I’ll also add a controller that talks to the providers to retrieve information about the logged in user:
    public class UserInfoController : ApiController
    {
        public ApiServices Services { get; set; }

        [AuthorizeLevel(AuthorizationLevel.User)]
        public async Task<JObject> GetUserInfo()
        {
            ServiceUser user = this.User as ServiceUser;
            if (user == null)
            {
                throw new InvalidOperationException("This can only be called by authenticated clients");
            }

            var identities = await user.GetIdentitiesAsync();
            var result = new JObject();
            var fb = identities.OfType<FacebookCredentials>().FirstOrDefault();
            if (fb != null)
            {
                var accessToken = fb.AccessToken;
                result.Add("facebook", await GetProviderInfo("https://graph.facebook.com/me?access_token=" + accessToken));
            }

            var ms = identities.OfType<MicrosoftAccountCredentials>().FirstOrDefault();
            if (ms != null)
            {
                var accessToken = ms.AccessToken;
                result.Add("microsoft", await GetProviderInfo("https://apis.live.net/v5.0/me/?method=GET&access_token=" + accessToken));
            }

            return result;
        }

        private async Task<JToken> GetProviderInfo(string url)
        {
            var c = new HttpClient();
            var resp = await c.GetAsync(url);
            resp.EnsureSuccessStatusCode();
            return JToken.Parse(await resp.Content.ReadAsStringAsync());
        }
    }
Then, we can have an application that authenticates and gets information about its user. For this example, I’ll use a simple app with buttons for each of the authentication providers mentioned above.
    public sealed partial class MainPage : Page
    {
         public static MobileServiceClient MobileService = new MobileServiceClient(
              "https://blog20141002.azure-mobile.net/",
              "yourapplicationkeyshouldbehere00"
        );

        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void btnFacebook_Click(object sender, RoutedEventArgs e)
        {
            await LoginAndGetUserInfo(MobileServiceAuthenticationProvider.Facebook);
        }

        private async void btnMicrosoft_Click(object sender, RoutedEventArgs e)
        {
            await LoginAndGetUserInfo(MobileServiceAuthenticationProvider.MicrosoftAccount);
        }

        private async Task LoginAndGetUserInfo(MobileServiceAuthenticationProvider provider)
        {
            try
            {
                var user = await MobileService.LoginAsync(provider);
                Debug("Logged in as {0}", user.UserId);
                var userInfo = await MobileService.InvokeApiAsync("userInfo", HttpMethod.Get, null);
                Debug("User info: {0}", userInfo);
                MobileService.Logout();
                Debug("Logged out");
                Debug("");
            }
            catch (Exception ex)
            {
                Debug("Error: {0}", ex);
            }
        }

        private void Debug(string text, params object[] args)
        {
            if (args != null && args.Length > 0) text = string.Format(text, args);
            this.txtDebug.Text = this.txtDebug.Text + text + Environment.NewLine;
        }
    }
If we run the app and login with each of the three providers, we’d get some basic information about the user. For example, this is what I get with my credentials:
Logged in as Facebook:xxxxxxxxxxxxx9805
User info: {
  "facebook": {
    "id": "xxxxxxxxxxxxx9805",
    "first_name": "Carlos",
    "gender": "male",
    "last_name": "Figueira",
    "link": "https://www.facebook.com/app_scoped_user_id/xxxxxxxxxxxxx9805/",
    "locale": "en_US",
    "name": "Carlos Figueira",
    "timezone": -7,
    "updated_time": "2013-12-12T04:09:57Z",
    "verified": true
  }
}
Logged out

Logged in as MicrosoftAccount:xxxxxxxxxxxxd789
User info: {
  "microsoft": {
    "id": "xxxxxxxxxxxxd789",
    "name": "Carlos Figueira",
    "first_name": "Carlos",
    "last_name": "Figueira",
    "link": "https://profile.live.com/",
    "gender": null,
    "locale": "en_US",
    "updated_time": "2014-09-30T09:38:42Z"
  }
}
Logged out
That’s some basic information, but if my application also needed the user’s e-mail or some other information, the access token granted by the service login didn’t have access to that. But if we request additional scopes during the login, by setting the MS_FacebookScope and MS_MicrosoftScope app settings, we’d get the additional information we need: 003-AppSettingsWithScopes And running the client app again we’d get the newly requested information (after the user grants the application access to the additional info).
Logged in as Facebook:xxxxxxxxxxxxx9805
User info: {
  "facebook": {
    "id": "xxxxxxxxxxxxx9805",
    "birthday": "xx/yy/zzzz",
    "email": "xxxxxxxxxxxxxxxxxxx@hotmail.com",
    "first_name": "Carlos",
    "gender": "male",
    "last_name": "Figueira",
    "link": "https://www.facebook.com/app_scoped_user_id/xxxxxxxxxxxxx9805/",
    "locale": "en_US",
    "name": "Carlos Figueira",
    "timezone": -7,
    "updated_time": "2013-12-12T04:09:57Z",
    "verified": true
  }
}
Logged out

Logged in as MicrosoftAccount:xxxxxxxxxxxxd789
User info: {
  "microsoft": {
    "id": "xxxxxxxxxxxxd789",
    "name": "Carlos Figueira",
    "first_name": "Carlos",
    "last_name": "Figueira",
    "link": "https://profile.live.com/",
    "gender": null,
    "emails": {
      "preferred": "xxxxxxxxxxxxxxxxxxx@hotmail.com",
      "account": "xxxxxxxxxxxxxxxxxxx@hotmail.com",
      "personal": null,
      "business": null
    },
    "locale": "en_US",
    "updated_time": "2014-09-30T09:38:42Z"
  }
}
Logged out
One final note about requesting additional scopes: 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.

Single Sign-On Support for Windows Store Applications

When you use the mobile services SDK in a Windows Store application, every time the app calls the method LoginAsync in the MobileServiceClient passing the authentication provider the authentication window is show, and the user has to enter their credentials and click the “sign in” button in the authentication page – even if the user selected the “remember me” button in the provider’s login page (Windows may have cached the credentials so that they don’t need to be entered, but the user still needs to click the button to log in). That’s because by default the cookies from an authentication session are not preserved, so that when the provider page is shown again, there will be no cookies from a previous authentication to identify the user. There’s an overload to LoginAsync which takes an additional flag which indicates that the client should cache the cookies in the authentication sessions, so that the next time LoginAsync is called, the authentication dialog will just be shown briefly and then automatically dismissed, making for a better user experience. In the client shown in the previous section, all we need to do is to use the additional overload and pass true to the second parameter of LoginAsync.
    private async Task LoginAndGetUserInfo(MobileServiceAuthenticationProvider provider)
    {
        try
        {
            var user = await MobileService.LoginAsync(provider, true);
            Debug("Logged in as {0}", user.UserId);
            var userInfo = await MobileService.InvokeApiAsync("userInfo", HttpMethod.Get, null);
            Debug("User info: {0}", userInfo);
            MobileService.Logout();
            Debug("Logged out");
            Debug("");
        }
        catch (Exception ex)
        {
            Debug("Error: {0}", ex);
        }
    }
There’s one change which needs to be done in the server side as well, if you haven’t yet. To enable this scenario, the application needs to be associated with an app in the Windows Store, since the package SID (one of the app identifiers) for that application needs to be stored in the service. You will get a package SID by creating the app in the Windows Store Dashboard, and you can see how to find that value in the tutorial to register the app package for Microsoft authentication. If your app will not use Microsoft authentication (for example, use Facebook or Twitter), you won’t need to copy the client id / secrets, but you’ll still need to copy the package SID in the microsoft account settings under the  identity tab in the portal.

Support for ASP.NET Web API 2.2

With this update the .NET backend now supports ASP.NET Web API 2.2 (or the version 5.2.x of the Microsoft.AspNet.WebApi.Owin NuGet package). With that you can now take advantage of all the new features and bug fixes from that release, such as attribute routing improvements and OData v4. The full list of changes can be found in “What’s New in ASP.NET Web API 2.2”.

Wrapping up

That’s it. We hope that those features will be helpful. As usual, please send us feedback either as comments in this post, via twitter @AzureMobile or in our MSDN forums.