Use PasswordVault to Store Your Tokens–Windows Azure Mobile Services

There was no end to end sample of how you might use the PasswordVault to store your JWT used for user authentication in Mobile Services.  I created this class to be used with your Windows Store app.

Copy Code:

 using Microsoft.WindowsAzure.MobileServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Credentials;

namespace MobileServices.Samples.Authentication
{
    class AuthProviderVault
    {
        MobileServiceClient _mobileService = null;
        private PasswordVault providerVault = null;

        public AuthProviderVault(MobileServiceClient mobileService)
        {
            // PasswordVault is specific to this Windows Store app
            providerVault = new PasswordVault();
            // Set to the application MobileService
            _mobileService = mobileService;
        }

        // Use the MobileService login
        public async Task<MobileServiceUser> LoginUser(MobileServiceAuthenticationProvider provider)
        {
            MobileServiceUser user = null;
            //There should be a MobileService attached!
            if (_mobileService != null)
            {
                try
                {
                    // Clear the CurrentUser
                    _mobileService.CurrentUser = null;
                    // Use the OAuth Login for the provider
                    user = await _mobileService.LoginAsync(provider);
                    // Have user will add!
                    addToVault(provider, user);
                    //  Set the current user
                    _mobileService.CurrentUser = user;
                }
                catch (InvalidOperationException exLogin)
                {
                    string error = "An error occurred during login. Login Required. Message: " + exLogin.Message;
                    user = null;
                }

            }
            // might be null
            return user;
        }

        // Will use the user JWT stored in the vault or go to LoginUser
        public async Task<MobileServiceUser> GetOrLoginMobileServiceUser(MobileServiceAuthenticationProvider provider)
        {
            MobileServiceUser userReturned = getStoredMobileServiceUser(provider);

            // See if there is a user stored in the vault use it otherwise
            // call LoginUser (which will also store the user in the vault)
            if (userReturned == null)
            {
                userReturned = await LoginUser(provider);
            }

            //userReturned could still be null if the user does not login
            _mobileService.CurrentUser = userReturned;
            return userReturned;
        }

        // adds a provider based User to the vault or replaces and existing one
        private void addToVault(MobileServiceAuthenticationProvider provider, MobileServiceUser user)
        {
            IReadOnlyList<PasswordCredential> creds = null;
            try
            {
                // Will throw an exception if none found (I hate that design)
                creds = providerVault.FindAllByResource(vaultKey(provider));
            }
            catch (Exception)
            {
                //expect an exception if not found in container
            }

            // remove existing creds (there should be only one if this is the only access to the App PasswordVault
            if (creds != null)
            {
                foreach (PasswordCredential cred in creds)
                {
                    providerVault.Remove(cred);
                }
            }

            // Create a new cred and add it to the vault
            PasswordCredential newCred = 
new PasswordCredential(vaultKey(provider), user.UserId, user.MobileServiceAuthenticationToken);
            providerVault.Add(newCred);

        }

        private string vaultKey(MobileServiceAuthenticationProvider provider)
        {
            return provider + ":" + _mobileService.ApplicationUri.DnsSafeHost;
        }

        private MobileServiceUser getStoredMobileServiceUser(MobileServiceAuthenticationProvider provider)
        {
            // if found in the vault return the mobile service user with info
            MobileServiceUser userReturned = null;

            try
            {
                PasswordCredential userCred = null;
                IReadOnlyList<PasswordCredential> creds = providerVault.FindAllByResource(vaultKey(provider));
                if (creds != null)
                {
                    // In my code there should ONLY be one cred per provider so problably should check that.
                    userCred = creds[0];

                    // Create a User to use with the Mobile Service (instead of doing the login sequence)
                    userReturned = new MobileServiceUser(userCred.UserName);
                    // Explicitly fetch the PWD into memory 
                    userCred.RetrievePassword();
                    // Set the JWT 
                    userReturned.MobileServiceAuthenticationToken = userCred.Password;

                    // remove from memory
                    userCred = null;
                }
            }
            catch (Exception e)
            {
                // if there was an exeption, eat the error
                userReturned = null;
            }

            return userReturned;
        }
    }
}

 

This is pretty easy to used.  I modified our Auth Sample to use this with each of the providers.  Here is an example from Scenario1.xaml.cx:

Copy Code:

  private async void Launch_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                AuthProviderVault vlt = new AuthProviderVault(App.MobileService);
                MobileServiceUser user = 
await vlt.GetOrLoginMobileServiceUser(MobileServiceAuthenticationProvider.Facebook);
                if (user != null)
                {
                    this.OutputPrint(string.Format("You are now logged in - {0}", user.UserId));
                    App.MobileService.CurrentUser = user;
                }
                else
                {
                    this.DebugPrint("An error occurred during login. Login Required.");
                }
            }
            catch (InvalidOperationException)
            {
                this.DebugPrint("An error occurred during login. Login Required.");
            }
        }

When you attempt to use a table you could fail with an Unauthorized exception.  If this does happen simply clear the old credential for the provider you are using and call LoginUser to get and store new credentials in the vault.

 

Copy Code:

             try
            {
                items = await todoTable.ToCollectionAsync();
            }
            catch (MobileServiceInvalidOperationException msEx)
            {
                if (msEx.Response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    AuthProviderVault vlt = new AuthProviderVault(App.MobileService);

                    // LoginUser goes right to the OAuth Login and then stores the user info in the vault
                    MobileServiceUser user = await vlt.LoginUser(MobileServiceAuthenticationProvider.Facebook);
                    if (user != null)
                    {
                        // user has been set on the MobileService so retry the query up to x times here!
                    }
                    else
                    {
                        // user could have denied to login and hit the back button
                    }
                }
            }

You can see the credentials (and remove them) in the credential manager after you have stored them:

image

 

This should get your ideas flowing and if this helps let me know!