Use Passwordvault To Store Your Tokenswindows Azure Mobile Services
- 24 Jun 2013
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.
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:
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.
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:
This should get your ideas flowing and if this helps let me know!
<< Go Back