Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Migrating Data Between Blob Storage Accounts in Azure

Over the last couple of posts I’ve been talking about working with different configurations and in my previous post I noted that one of the things we had to do was to migrate some data that had been entered into the Test environment into Production environment (again I stress that I’m not recommending it but occasionally you have to bend the process a little). One of the challenges we encountered was that we not only had to migrate the database, which was easy using the database copy capability in the Azure portal, we also needed to migrate the related blob storage data from one account into another. Here’s some quick code that makes use of the Azure Storage client library (WindowsAzure.Storage package via NuGet and more information here).

Firstly in the app.config we have two connection strings:

<connectionStrings>
    <add name="BlobMigrator.Properties.Settings.SourceStorage"
      connectionString="DefaultEndpointsProtocol=https;AccountName=sourceaccount;AccountKey=YYYYYYYYYYY" />
    <add name="BlobMigrator.Properties.Settings.TargetStorage"
      connectionString="DefaultEndpointsProtocol=https;AccountName=targetaccount;AccountKey=XXXXXXXXXXXXXXX" />

</connectionStrings>

Next, some straight forward code to iterate through containers in one storage account and copy content across to the target account:

var source= CloudStorageAccount.Parse(Settings.Default.SourceStorage);
var target= CloudStorageAccount.Parse(Settings.Default.TargetStorage);

var sourceClient = source.CreateCloudBlobClient();
var targetClient = target.CreateCloudBlobClient();

var containers= sourceClient.ListContainers("searchprefix").ToArray();
Debug.WriteLine("Source containers: " + containers.Length);
var idx = 0;
foreach (var cnt in containers)
{
    var tcnt =targetClient.GetContainerReference(cnt.Name);
    await tcnt.CreateIfNotExistsAsync();

    var sblobs = cnt.ListBlobs();
    foreach (var sblob in sblobs)
    {
        var b = await sourceClient.GetBlobReferenceFromServerAsync(sblob.Uri);
        var tb = tcnt.GetBlockBlobReference(b.Name);
        var ok = await tb.StartCopyFromBlobAsync(b.Uri);
        Debug.WriteLine(ok);
    }
    idx++;
    Debug.WriteLine("Migrated {0} of {1} - {2}",idx,containers.Length,cnt.Name);
}

In this case it’s limiting the containers that are copied to those that start with the prefix “searchprefix” but this is optional if you want to copy all containers.

Different Cloud Environments for Development, Testing and Production

One of the aspects of developing applications that have a cloud backend that gets overlooked initially is how to separate development from test and production versions of the application. For web applications ASP.NET solved this by supporting transformations in the web.config file based on build configuration (eg web.Debug.config and web.Release.config). However, this issue is harder with client applications that don’t have config files and don’t understand configuration transformations. The other issue with transformations is that they’re only applied during the publishing process, rather than simply when you change the build configuration in Visual Studio.

I’ll come back to talk about how I’ve chosen to handle different application configurations in a later post. In this post I want to discuss how we’ve handled having multiple environments for our Mobile Service backend; this includes how we decided to do this working with our development team v’s the client site.

Our strategy was to have three environments: Development, Testing and Production. Development was housed within the Built to Roam development Azure subscription which the development team have access to. For the most part anyone within the development team could deploy to this environment at any stage – of course there was some self management involved to minimize breaking changes. As an aside, as I’ve pointed out in a previous post, it is possible to set up Mobile Services to run locally, even if you enable Azure Active Directory authentication. The Development environment was also based on an Azure Active Directory (AAD) tenant explicitly created for the development of that project – that way accounts could be added/removed without affecting any other AAD.

Test and Production were both created in the customers Azure subscription. This was to minimize differences between these environments. These environments also connected to the customers AAD which meant that testing could be carried out with real user accounts since their AAD was synchronized with their internal AD. In a case where writing is supported back to AAD you may want to consider having test pointing to a separate AAD instance but for our purposes AAD was read only so there was no issue in using the same AAD tenant for both Test and Production.

For each of these we created a separate Mobile Service, named according to the environment, with production being the exception as we decided to drop the “production” suffix. Taking the RealEstateInspector example our services would be called:

Development – RealEstateInspectorDev
Testing – RealEstateInspectorTest
Production – RealEstateInspector

Note that we shortened both Development and Testing to just Dev and Test for simplicity.

We also created corresponding storage accounts, with names that matched the names of the mobile service

We also created corresponding applications in the appropriate Azure Active Directory, again with names that matched the corresponding environment. We didn’t use the same applications for Testing and Production to ensure we could configure them separately if required.

One issue we faced is that during the first iteration of development as the system was undergoing final testing in the Testing environment some real data was entered into the system. This meant that rather than simply deploying to Production we actually needed to migrate data from Testing to Production (definitely not something I would recommend as best practice). To do this was actually relatively simple using the ability in Azure to copy a SQL database and then within the Mobile Service change the database that it points to. We also had to migrate content from one storage account to another for which we couldn’t find a simple out of the box tool to use. However, this was actually much simpler than we thought and I’ll come back to this in a future post.

Integration Synchronization Wrapper and Restructuring Application Services

So far all the Mobile Service operations, including holding the instance of the MobileServiceClient, has been done by the MainViewModel. Clearly as the application grows this is not a viable solution so we need some application services which can be used to hold the reference to the MobileServiceClient and to facilitate application logic such as data access and synchronisation. To this end I’m going to create two services, IDataService and ISyncService with their corresponding implementations as follows:

public interface IDataService
{
    IMobileServiceClient MobileService { get; }

    Task Initialize(string aadAccessToken);
}

public class DataService: IDataService
{
    private readonly MobileServiceClient mobileService = new MobileServiceClient(
        Constants.MobileServiceRootUri,
        "wpxaIplpeXtkn------QEBcg12",
        new MobileServiceHttpHandler()
        );

    public IMobileServiceClient MobileService
    {
        get { return mobileService; }
    }

    public async Task Initialize(string aadAccessToken)
    {
        var jobj = new JObject();
        jobj["access_token"] = aadAccessToken;
        var access = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, jobj);
        Debug.WriteLine(access != null);
        var data = new MobileServiceSQLiteStore("inspections.db");
        data.DefineTable<RealEstateProperty>();
        data.DefineTable<Inspection>();

        await MobileService.SyncContext.InitializeAsync(data, new CustomMobileServiceSyncHandler());

    }
}

The IDataService implementation holds the reference to the IMoblieServiceClient. This will need to be initialized by passing in the Azure Active Directory access token but there after the MobileService accessor can be used to access data directly through the IMobileServiceClient instance.

public interface ISyncService
{
    event EventHandler<DualParameterEventArgs<double, string>> Progress;
    Task Synchronise(bool waitForCompletion);
    Task ForceUpload();
}

public class SyncService: ISyncService
{
    [Flags]
    private enum SyncStages
    {
        None = 0,
        UploadChanges = 1,
        PullProperties = 2,
        PullInspections = 4,
        All = UploadChanges | PullProperties | PullInspections
    }

    public event EventHandler<DualParameterEventArgs<double,string>> Progress;

    public IDataService DataService { get; set; }

    private ISynchronizationContext<SyncStages> SynchronizationManager { get; set; }

    public SyncService(IDataService dataService)
    {
        DataService = dataService;
        SynchronizationManager = new SynchronizationContext<SyncStages>();
        SynchronizationManager.DefineSynchronizationStep(SyncStages.UploadChanges, UploadPendingLocalChanges);
        SynchronizationManager.DefineSynchronizationStep(SyncStages.PullProperties, DownloadChangesToRealEstateProperties);
        SynchronizationManager.DefineSynchronizationStep(SyncStages.PullInspections, DownloadChangesToInspections);
        SynchronizationManager.SynchronizationChanged += SynchronizationManager_SynchronizationProgressChanged;
    }

    public async Task Synchronise(bool waitForCompletion)
    {
        await SynchronizationManager.Synchronize(SyncStages.All, waitForSynchronizationToComplete: waitForCompletion);
    }

    public async Task ForceUpload()
    {
        await SynchronizationManager.Synchronize(SyncStages.UploadChanges, true, true);
    }

    private void SynchronizationManager_SynchronizationProgressChanged(object sender, SynchronizationEventArgs<SyncStages> e)
    {
        var message = e.ToString();
        if (Progress != null)
        {
            Progress(this,new object[]{ e.PercentageComplete, message});
        }
    }

    private async Task<bool> UploadPendingLocalChanges(ISynchronizationStage<SyncStages> stage)
    {
        await DataService.MobileService.SyncContext.PushAsync(stage.CancellationToken);
        return true;
    }
    private async Task<bool> DownloadChangesToRealEstateProperties(ISynchronizationStage<SyncStages> stage)
    {
        await DataService.MobileService.PullLatestAsync<RealEstateProperty>(stage.CancellationToken);
        return true;
    }
    private async Task<bool> DownloadChangesToInspections(ISynchronizationStage<SyncStages> stage)
    {
        await DataService.MobileService.PullLatestAsync<Inspection>(stage.CancellationToken);
        return true;
    }
}

The ISyncService defines the actual synchronization steps. Rather than simply exposing a generic Synchronize method that accepts the a SyncStages parameter to determine which steps are synchronized, the ISyncService actually exposes high level methods for performing a full synchronize (Synchronize) and just to upload pending changes (ForceUpload). Note that the former has a parameter indicating whether the method should wait synchronization completion before returning, whereas the latter will always wait for the upload part of the synchronize to complete.

To make these services available to the view models of the application the BaseViewModel has been updated to include properties for both services:

public class BaseViewModel : INotifyPropertyChanged
{
    public IDataService DataService { get; set; }
    public ISyncService SyncService { get; set; }

And of course the ViewModelLocator is updated to create instances of these services and assign them to the view model when they’re created:

public class ViewModelLocator
{
    public IDataService DataService { get; set; }
    public ISyncService SyncService { get; set; }

    public ViewModelLocator()
    {
        DataService=new DataService();
        SyncService=new SyncService(DataService);
    }

    public MainViewModel Main
    {
        get { return CreateViewModel<MainViewModel>(); }
    }

    private readonly Dictionary<Type, object> viewModels = new Dictionary<Type, object>();

    private T CreateViewModel<T>() where T:new()
    {
        var type = typeof (T);
        object existing;
        if (!viewModels.TryGetValue(type, out existing))
        {
            existing = new T();
            viewModels[type] = existing;
        }

        var baseVM = existing as BaseViewModel;
        if (baseVM != null)
        {
            baseVM.DataService = DataService;
            baseVM.SyncService = SyncService;
        }

        return (T)existing;
    }
}

Fixing up the Client For Writing to Azure Blob Storage with Shared Access Signature

In my previous post I updated the service logic for retrieving the Shared Access Signature (SAS) to return the full Url of the blob container including the SAS. In order for this to work I also need to update the client logic. This gets much simpler as I can simply construct a new CloudBlockBlob by amending the Url to include the name of the blob to be written to.

private async void CaptureClick(object sender, RoutedEventArgs e)
{
    var picker = new MediaPicker();
    var sas = string.Empty;
    using (var media = await picker.PickPhotoAsync())
    using (var strm = media.GetStream())
    {
        sas = await CurrentViewModel.RetrieveSharedAccessSignature();

        // Append the image file name to the Path (this will
        // retain the SAS as it's in the query string
        var builder = new UriBuilder(sas);
        builder.Path += "/testimage" + Path.GetExtension(media.Path);
        var imageUri = builder.Uri;

        // Upload the new image as a BLOB from the stream.
        var blob = new CloudBlockBlob(imageUri);
        await blob.UploadFromStreamAsync(strm.AsInputStream());
    }
}

But, we can actually do even better…. what we get back is a Url, including the SAS, for the blob container. So we can use the Azure Storage library to create a CloudBlobContainer and then acquire the blob reference from there – this does the work of combining the urls for us.

private async void CaptureClick(object sender, RoutedEventArgs e)
{
    var picker = new MediaPicker();
    var sas = string.Empty;
    using (var media = await picker.PickPhotoAsync())
    using (var strm = media.GetStream())
    {
        sas = await CurrentViewModel.RetrieveSharedAccessSignature();
        var container = new CloudBlobContainer(new Uri(sas));
        var blobFromContainer = container.GetBlockBlobReference("testimage" + Path.GetExtension(media.Path));
        await blobFromContainer.UploadFromStreamAsync(strm.AsInputStream());
    }
}

Modifying the GET Request for the SharedAccesSignature Controller

In the previous post I noted that the code was pretty messy, particularly for the client code with a bunch of hardcoded literals. To fix this I’m going to encapsulate the full URL for blob storage into the server code, meaning that the client shouldn’t have to know the url of blob storage – this will make it easy to administer this in the future as things change.

It turns out that in order to make this change all I needed to do is to return the full blob container url (including the SAS) instead of just the SAS.

var ub = new UriBuilder(container.Uri.OriginalString)
{
    Query = container.GetSharedAccessSignature(sasPolicy).TrimStart('?')
};
sas =  ub.Uri.OriginalString;

The client code of course needs to be updated to handle the full Uri being passed back – Note that we didn’t include the name of the blob as part of creating the Uri. This is something the client should do. Since the SAS is for access to the whole container, the client doesn’t have to request a new SAS for each blob, only for each container it wants to write to.

Saving Image to Blob Storage Using Shared Access Signature

In this post I’m  going to bring together a couple of my previous posts that discuss retrieving and saving images, and retrieving a Shared Access Signature from a controller which will allow me to write to a particular container within Blob Storage. To complete the implementation I’ll use the Windows Azure Storage library from NuGet – it only installs for Windows platforms as there’s no PCKL or Xamarin support for this library currently.

image

As the Windows Azure Storage library is current platform specific, I’ll need to wrap it in a simple interface that makes it easy for me to write data to Blob Storage – I’ll come back to that. For the time being I’m just going to retrieve the SAS and use it along with the storage library to upload an image. So I’ll start by invoking the sharedaccesssignature controller using the GET verb as I want to ensure the container is created if it doesn’t already exist. This will return a SAS which I can use in the upload process.

public async Task<string> RetrieveSharedAccessSignature()
{
    var sas = await MobileService.InvokeApiAsync<string>("sharedaccesssignature", HttpMethod.Get,
        new Dictionary<string, string> { { "id", "test" } });
    return sas;
}

Next I want to capture an image, in this case picking a photo, and uploading it to a specified blobg.

private async void CaptureClick(object sender, RoutedEventArgs e)
{
    var picker = new MediaPicker();
    var sas = string.Empty;
    using (var media = await picker.PickPhotoAsync())
    using (var strm = media.GetStream())
    {
        sas = await CurrentViewModel.RetrieveSharedAccessSignature();

        Debug.WriteLine(sas);

        // Get the URI generated that contains the SAS
        // and extract the storage credentials.
        var cred = new StorageCredentials(sas);
        var imageUri = new Uri("
https://realestateinspector.blob.core.windows.net/test/testimage.png");

        // Instantiate a Blob store container based on the info in the returned item.
        var container = new CloudBlobContainer(
            new Uri(string.Format("
https://{0}/{1}",
                imageUri.Host, "test")), cred);

        // Upload the new image as a BLOB from the stream.
        var blobFromSASCredential = container.GetBlockBlobReference("testimage.png");
        await blobFromSASCredential.UploadFromStreamAsync(strm.AsInputStream());
    }

}

Clearly this code isn’t well factored but it’s here as a quick example of how you can use a SAS to upload content to blob storage.

Simplifying Shared Access Signature Generation with the Mobile Services ResourceBroker

In my post Storing the Big Stuff in Blob Storage I showed you how to manually create a shared access signature. The Azure Mobile Services team have done a really nice job of making this even easier with the ResourceBroker NuGet package. Getting started documentation is available via GitHub (https://github.com/Azure/azure-mobile-services-resourcebroker) and of course the package, which I’ve added to my Mobile Service project, is available via NuGet.

image

The changes I needed to make to my SharedAccessSignature controller are:

- Change my Web.config to include the ResourceBrokerStorageConnectionString appSetting – the documentation talks about adding this via the Configure panel of the mobile service portal but you’ll need to add it to web.config for debugging. Also the format of this string should be similar to the following:

<add key="ResourceBrokerStorageConnectionString"
     value="DefaultEndpointsProtocol=https;AccountName=realestateinspector;AccountKey=LxWu0q2UvQ7ddxXvIP3UfV4ozDkLpgaSkUxkK8--------------------------BYHTpTrAGaHjLoynH+61ng==" />

- Change the base class of the controller to ResourcesControllerBase (I needed to add an import statement to the top of the file too)

- Add routing information to the WebApiConfig.cs file (as per the documentation on GitHub)

// Create a custom route mapping the resource type into the URI.    
var resourcesRoute = config.Routes.CreateRoute(
     routeTemplate: "api/resources/{type}",
     defaults: new { controller = "resources" },
     constraints: null);

// Insert the ResourcesController route at the top of the collection to avoid conflicting with predefined routes.
config.Routes.Insert(0, "Resources", resourcesRoute);

- Initially I removed the contents of my controller but then I realised that there are limitations on the ResourceControllerBase (eg the Blob container must exist and that I needed to specify an actual blob, not just a container for access), so I kept my code and modified it to work with the new connection string.

public class SharedAccessSignatureController :  ResourcesControllerBase
{
    public async Task<string> Get(string id)
    {
        var sas = string.Empty;

        if (!string.IsNullOrEmpty(id))
        {
            // Try to get the Azure storage account token from app settings. 
            string storageAccountConnectionString;

            if (Services.Settings.TryGetValue("ResourceBrokerStorageConnectionString", out storageAccountConnectionString) )
            {
                // Set the URI for the Blob Storage service.
                var account = CloudStorageAccount.Parse(storageAccountConnectionString);
                // Create the BLOB service client.
                var blobClient = new CloudBlobClient(account.BlobStorageUri,account.Credentials);

                // Create a container, if it doesn't already exist.
                var container = blobClient.GetContainerReference(id);
                await container.CreateIfNotExistsAsync();

                // Create a shared access permission policy.
                var containerPermissions = new BlobContainerPermissions();

                // Enable anonymous read access to BLOBs.
                containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob;
                container.SetPermissions(containerPermissions);

                // Define a policy that gives write access to the container for 1h
                var sasPolicy = new SharedAccessBlobPolicy()
                {
                    SharedAccessStartTime = DateTime.UtcNow,
                    SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(59).AddSeconds(59),
                    Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read
                };

                sas = container.GetSharedAccessSignature(sasPolicy);
            }
        }

        return sas;
    }
}

- To get my code to work I also had to amend the route to the following, adding the id as an optional parameter

var resourcesRoute = config.Routes.CreateRoute(
        routeTemplate: "api/sharedaccesssignature/{type}/{id}",
        defaults: new { controller = "sharedaccesssignature", id = RouteParameter.Optional },
        constraints: null);

Calling the controller from Fiddler can be done in two ways:

GET: http://localhost:51539/api/sharedaccesssignature/blob/test

POST: http://localhost:51539/api/sharedaccesssignature/blob
Content-Type: application/json
{
    "name": "myblob",
    "container": "test",
    "permissions": "w",
    "expiry": "2015-12-01T07:34:42Z"
}

Don’t forget that if during debugging you set authenticationlevel to anonymous, to make sure you change it back to User or Application before publishing.

Storing the Big Stuff in Blob Storage

Often mobile applications need to store and retrieve large object data, typically photos and video. This is where Azure blog storage comes into play. In order to write into blob storage you need an access key. However, you’d never distribute an actual access key out to a mobile application, even temporarily, as it’s a really bad idea – if someone gets hold of the access key they have access to everything stored in your blob storage. Luckily blob storage has the notion of shared access signatures which you can think of as short term access passes to blob storage. These are typically created using a full access key and as such this operation is done service side.

I’m going to create a dedicated API just for granting shared access signatures to specific containers (which you can think of as single level folders). In this case the containers will be created with public read access on the contents – since the list of blobs per container will be protected and clients will still need a shared access signature in order to write to containers, this should be ample security for a large proportion of application scenarios.

I’ll start off by creating a new Storage area within the Azure management portal.

image

Once created you’ll need to record the storage account name (realestateinspector) and the storage account access key. Add these values into the appSettings section of the web.config file for the Azure Mobile Service

<appSettings>

  <add key="STORAGE_ACCOUNT_NAME"
       value="realestateinspector" />
  <add key="STORAGE_ACCOUNT_ACCESS_KEY"
       value="LxWu0q2UvQ7ddxXvIP3UfV4ozDkLpgaSkUx------------------------------------33WBYHTpTrAGaHjLoynH+61ng==" />

I’ll create a new controller in my Azure Mobile Service based on the Custom Controller item template

image

The bulk of this api controller sits within a single GET operation:

[AuthorizeLevel(AuthorizationLevel.User)]
public class SharedAccessSignatureController : ApiController
{
    public ApiServices Services { get; set; }

    public async Task<string> Get(string containerToAccess)
    {
        var sas = string.Empty;

        if (!string.IsNullOrEmpty(containerToAccess))
        {
            // Try to get the Azure storage account token from app settings. 
            string storageAccountName;
            string storageAccountKey;

            if (Services.Settings.TryGetValue("STORAGE_ACCOUNT_NAME", out storageAccountName) &&
                Services.Settings.TryGetValue("STORAGE_ACCOUNT_ACCESS_KEY", out storageAccountKey))
            {
                // Set the URI for the Blob Storage service.
                var blobEndpoint = new Uri(string.Format("
https://{0}.blob.core.windows.net", storageAccountName));

                // Create the BLOB service client.
                var blobClient = new CloudBlobClient(blobEndpoint, new StorageCredentials(storageAccountName, storageAccountKey));

                // Create a container, if it doesn't already exist.
                var container = blobClient.GetContainerReference(containerToAccess);
                await container.CreateIfNotExistsAsync();

                // Create a shared access permission policy.
                var containerPermissions = new BlobContainerPermissions();

                // Enable anonymous read access to BLOBs.
                containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob;
                container.SetPermissions(containerPermissions);

                // Define a policy that gives write access to the container for 1h
                var sasPolicy = new SharedAccessBlobPolicy()
                {
                    SharedAccessStartTime = DateTime.UtcNow,
                    SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(59).AddSeconds(59),
                    Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read
                };

                sas = container.GetSharedAccessSignature(sasPolicy);
            }
        }

        return sas;
    }
}

If you change the AuthorizationLevel to Anonymous you can run up this controller and use Fiddler to generate the shared access signature by invoking a GET on (eg http://localhost:51539/api/SharedAccessSignature/test, where test is the name of the container we’re requesting access to. If you want to check that the container has been created and the appropriate security set, you can use the CloudBerry Explorer for Azure Blob Storage.

image

After entering credentials you can immediately see the folders in your blob storage which will in this case have the container “test” which was created when I made the request to the SharedAccessSignature service.

image

You can also use Fiddler to prepare and launch a query – don’t forget to switch the AuthorizationLevel back to User before deploying your services otherwise anyone will be able to access content from your blob storage.

Long Running Azure Mobile Service Call With Feedback using SignalR

I’ve been thinking a little more about alternative mechanisms for sending feedback to the client applications when a long running mobile service call is executing. In my previous post on Long Running Custom API Calls in Azure Mobile Service I discussed returning immediately to the client application whilst continuing to process a long running task. Unfortunately this means there is no way to provide feedback. In this post I’m going to add in SignalR to provide that real time communications link.

 

 

image

I’ll define a class that in herits from Hub and exposes a Services property – this will be populated automatically by the Mobile Service.

public class LongRunningFeedbackHub : Hub
{
    public ApiServices Services { get; set; }
}

Next I’ll amend the Get service method to a) take a parameter (which is the connection id of signalr on the client) and b) provide feedback during the long running task via the hub object. Note the use of the dynamic Progress method which will correlate to the client side proxy method that it subscribes to.

public async Task<string> Get(string id)
{
    var host = new ReportHost();
    host.DoWork(async (cancel) =>
    {
        try
        {
            var hub = Services.GetRealtime<LongRunningFeedbackHub>();

            var max = 5;
            for (int i = 0; i < max; i++)
            {
                await Task.Delay(TimeSpan.FromSeconds(5), cancel);

                hub.Clients.
                    Client(id)
                    .Progress(string.Format("{0}% complete",100*i/5));
            }
        }
        catch (Exception ex)
        {
            // Don't bubble the exception - do something sensible here!
            Debug.WriteLine(ex.Message);
        }
    });
    Services.Log.Info("Hello from custom controller!");
    return "Hello";
}

I added the initialization logic for the SignalR to the Register method of the WebApiConfig.cs file, as well as providing an explicit route to support the id parameter

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { action = RouteParameter.Optional, id = RouteParameter.Optional }
);

SignalRExtensionConfig.Initialize();

On the client side I add the SignalR .NET Client library from NuGet

image

And then add a ConnectToSignalR method to establish the hub connection and return a connectionId, which will then be passed into the view model via the GenerateReport method.

private async void GenerateReportClick(object sender, RoutedEventArgs e)
{
    var connectionId=await  ConnectToSignalR();
    CurrentViewModel.GenerateReport(connectionId);
}

private async Task<string> ConnectToSignalR()
{
    var hubConnection = new HubConnection(MainViewModel.MobileService.ApplicationUri.AbsoluteUri);
    //if (user != null)
    //{
    //    hubConnection.Headers["x-zumo-auth"] = user.MobileServiceAuthenticationToken;
    //}
    //else
    //{
    hubConnection.Headers["x-zumo-application"] = MainViewModel.MobileService.ApplicationKey;
    //}
    IHubProxy proxy = hubConnection.CreateHubProxy("LongRunningFeedbackHub");
    await hubConnection.Start();

    //string result = await proxy.Invoke<string>("Send", "Hello World!");
    //var invokeDialog = new MessageDialog(result);
    //await invokeDialog.ShowAsync();

    proxy.On<string>("Progress",
        msg => Debug.WriteLine(msg));

    return hubConnection.ConnectionId;
}

The only change to the GenerateReport method is for it to accept an id parameter and for this parameter to be passed into the custom api

var result = await MobileService.InvokeApiAsync<string>("Reporter", HttpMethod.Get, new Dictionary<string, string>{{"id",connectionId}});

When this is run and the GenerateReport method is invoked, the current percentage complete is passed back to the client and appears in the handler for the Progress message.

Long Running Custom API Calls in Azure Mobile Service

As I pointed out in my previous post a common scenario for custom apis in a Mobile Service is to hand off tasks that aren’t easily done on a mobile device, or are better suited to being done server side (eg report creation). Quite often these tasks can take longer than the default timeout of most service requests (typically 60seconds) which means the mobile app ends up raising an exception which is not very useful. A better approach is to queue the work somehow and then to periodically check on it’s progress. In this post I’m just going to demonstrate one way to allow the service to respond immediately, whilst continuing to carry out the task in the background. Note that the ReportHost is based on the JobHost class described in Phil Haack’s post on the dangers of background tasks

public async Task<string> Get()
{
    var host = new ReportHost();
    host.DoWork(async (cancel) =>
    {
        try
        {
            await Task.Delay(TimeSpan.FromMinutes(2), cancel);
        }
        catch (Exception ex)
        {
            // Don't bubble the exception - do something sensible here!
            Debug.WriteLine(ex.Message);
        }
    });
    Services.Log.Info("Hello from custom controller!");
    return "Hello";
}

And the ReportHost class

public class ReportHost : IRegisteredObject
{
    private readonly ManualResetEvent reportLock = new ManualResetEvent(false);
    private readonly CancellationTokenSource cancellation=new CancellationTokenSource();

    public ReportHost()
    {
        HostingEnvironment.RegisterObject(this);
    }

    public void Stop(bool immediate)
    {
        cancellation.Cancel();
        reportLock.WaitOne();
        HostingEnvironment.UnregisterObject(this);
    }

    public void DoWork(Func<CancellationToken,Task> work)
    {
        Task.Run(async () =>
        {
            await work(cancellation.Token);
            reportLock.Set();
        });
    }
}

I haven’t shown any client side code for the timebeing because it remains the same (although it won’t timeout now!). The next step is to provide some way that the client can check on the progress of the work item.

Invoking a Custom API in Azure Mobile Service

The full scenario is that we have a task that needs to be performed by the Mobile Service that might take a while to complete. The first step is to define a custom api which will invoke the task (alternatively you could hijack a table controller to launch the task as part of one of the CRUD actions) and to have this called from the client applications. However, this alone is not sufficient for long running tasks as the call to the service may timeout before the task completes. I’ll come back to that in a future post but for now, let’s look at creating a custom api.

The first step is to add a new controller based on the Microsoft Azure Mobile Services Custom Controller template.

image

I’ll give the new controller a name

image

For the time being the only change I’ll make is to include the AutorizeLevel and AuthorizeInspector attributes to enforce the security policies required for accessing our services:

[AuthorizeLevel(AuthorizationLevel.User)]
[AuthorizeInspector]
public class ReporterController : ApiController
{
    public ApiServices Services { get; set; }

    // GET api/Reporter
    public async Task<string> Get()
    {
        Services.Log.Info("Hello from custom controller!");
        return "Hello";
    }

}

Invoking this from the client can easily be done from within the MainViewModel:

public async void GenerateReport()
{
    string message;
    try
    {
        var result = await MobileService.InvokeApiAsync<string>("Reporter", HttpMethod.Get, null);
        message = result;
    }
    catch (MobileServiceInvalidOperationException ex)
    {
        message = ex.Message;
    }
    Debug.WriteLine(message);
}

Easy done – a simple api that we can invoke within our Mobile Service to do work. Note that in this case it’s a Get requrest with no parameters and a simple string return type. We can adjust this to be a Post, accept parameters and return a complex object by adjusting both the controller method definition (ie change Get to Post, or even just add a Post method) and invokeapiasync call.

Handling MobileServiceConflictException with Azure Mobile Service with Offline Sync

Whenever you do offline sync, there is a risk of conflicts emerging between client and server updates. Of course the data architecture should be done to minimise this (eg guid based primary keys) but this won’t always eliminate the issue. One example of where this might happen is if the client gets cut off midway through pushing changes to the server. For example if I were to insert a new record on the client, push the changes but before the changes had been confirmed back to the client, the network connection was terminated. The server now has the new record but the client thinks it still needs to send the record – when it does, a conflict arises where both server and client have records with the same primary key. Let’s replicate this and then look at solving it.

I’ll create an AddProperty method into my MainViewModel:

public async Task AddProperty()
{
    var table=MobileService.GetSyncTable<RealEstateProperty>();
    var prop = new RealEstateProperty
    {
        Address = "New Random Property"
    };
    await table.InsertAsync(prop);
    await MobileService.SyncContext.PushAsync();
}

Run this, and insert a breakpoint after the InsertAsync but before the PushAsync. At this point inspect the prop object and retrieve the Id. Next, using either Sql Server Management Studio or Visual Studio 2015, connect to the SQL Server instance and run the following query (replacing the Id with the one retrieved in previous step).

insert into realestateinspector.RealEstateProperties (Id,Address,Deleted) Select '0a1f8994-4a4b-4548-921a-4da0186b3f6c','Not created on client',0

Now, if I let the PushAsync continue it will fail, causing an exception to be raised.

image

There are a couple of places that this can be handled. The first is where the call to PushAsync is made – this isn’t great as pushing to the remove service won’t necessarily happen at this point. For example you might insert a record but not push immediately. In this case when you next issue a pull request the push will be done prior to doing the pull. A better way to handle it is to supply a custom MobileServiceSyncHandler as part of the initialization of the sync context:

 

await MobileService.SyncContext.InitializeAsync(data, new CustomMobileServiceSyncHandler());

The sync handler could look like the following (this is very basic and just drops any conflicts)

public class CustomMobileServiceSyncHandler : MobileServiceSyncHandler
{
    public async override Task<JObject> ExecuteTableOperationAsync(IMobileServiceTableOperation operation)
    {
        try
        {
            return await base.ExecuteTableOperationAsync(operation);
        }
        catch (MobileServiceConflictException cex)
        {
            Debug.WriteLine(cex.Message);
            throw;
        }
    }

    public override Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
    {
        foreach (var error in result.Errors)
        {
            if (error.Status == HttpStatusCode.Conflict)
            {
                error.CancelAndUpdateItemAsync(error.Result);
                error.Handled = true;
            }
        }
        return base.OnPushCompleteAsync(result);
    }
}

Restricting Access to Azure Mobile Service Base on Azure Active Directory Group

In my previous post I controlled access to the GetAll method on my base controller by determining whether the authenticated (from AAD) user was a member of the Inspectors AAD group. This is actually quite a slow process and not something you really want to do every session. Ideally I’d like this check to be done once when the user authenticates against the mobile service (which happens after they authenticate against AAD) and for the IsInspector claim to be added to the user identity. Unfortunately for the life of me I can’t work out how to force OWIN into accepting an additional claim – I’m sure there’s a way, but I ended up settling for an alternative approach.

My approach actually improves on two aspects over what I was previously doing. The first is that I implement the checking logic as an attribute which can then be applied to the root controller. The second is that by storing a cookie in the response, I can reduce the need to re-query AAD for the group membership. This solution is based on a couple of great blog posts:

http://www.acupofcode.com/2014/04/general-roles-based-access-control-in-the-net-backend/

http://www.acupofcode.com/2014/03/roles-based-access-control-in-mobile-services-and-azure-active-directory/

The AuthorizeInspector attribute looks as follows:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AuthorizeInspector : AuthorizationFilterAttribute
{
    public static ActiveDirectoryClient RetrieveActiveDirectoryClient(string token)
    {
        var baseServiceUri = new Uri(Microsoft.Azure.ActiveDirectory.GraphClient.Constants.ResourceId);
        var activeDirectoryClient =
            new ActiveDirectoryClient(new Uri(baseServiceUri, Constants.ADTenant),
                async () => token);
        return activeDirectoryClient;
    }

    public async override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        await base.OnAuthorizationAsync(actionContext, cancellationToken);

        var cookie = HttpContext.Current.Request.Cookies["IsInspector"];
        var isInspector = cookie != null ? cookie.Value : null;
        if (isInspector != null)
        {
            if (!(bool.Parse(isInspector)))
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            }
            return;
        }

        var controller = actionContext.ControllerContext.Controller as ApiController;
        if (controller == null)
        {
            return;
        }
        var user = controller.User as ServiceUser;

        //var user = User as ServiceUser;
        var aadCreds = (await user.GetIdentitiesAsync()).OfType<AzureActiveDirectoryCredentials>().FirstOrDefault();
        Debug.WriteLine(aadCreds.AccessToken);

        var token = actionContext.Request.Headers.GetValues(Constants.RefreshTokenHeaderKey)
            .FirstOrDefault();

        var auth = new AuthenticationContext(Constants.ADAuthority, false);
        var newToken = await auth.AcquireTokenByRefreshTokenAsync(token,
                Constants.ADNativeClientApplicationClientId, "
https://graph.windows.net");

        var client = RetrieveActiveDirectoryClient(newToken.AccessToken);
        var grps = await client.Groups.ExecuteAsync();
        var moreGroups = grps.CurrentPage;

        while (moreGroups != null)
        {
            foreach (var grp in grps.CurrentPage)
            {
                if (grp.DisplayName == "Inspectors")
                {
                    if ((await client.IsMemberOfAsync(grp.ObjectId, aadCreds.ObjectId)) ?? false)
                    {
                        HttpContext.Current.Response.Cookies.Add(new HttpCookie("IsInspector", true.ToString()));

                        return;
                    }
                }
            }
            if (grps.MorePagesAvailable)
            {
                grps = await grps.GetNextPageAsync();
                moreGroups = grps.CurrentPage;
            }
            else
            {
                grps = null;
                moreGroups = null;
            }
        }
        HttpContext.Current.Response.Cookies.Add(new HttpCookie("IsInspector", false.ToString()));
    }
}

As you can see this follows roughly the same logic for querying AAD group membership. However, this time I’m adding a cookie based on whether the user is an Inspector or not.This attribute can now be applied to the RealEstateBaseTableController.

[AuthorizeInspector]
public class RealEstateBaseTableController<TEntity> : TableController<TEntity>
    where TEntity : class, ITableData
{

One thing to be aware of is that this cookie will persist even if the user logs out. As such, we need some way of associating the cookie with the current user session. It may be that an additional cookie is used to associate the access token with the IsInspector cookie. For example:

public override async Task OnAuthorizationAsync(HttpActionContext actionContext,
    CancellationToken cancellationToken)
{
    await base.OnAuthorizationAsync(actionContext, cancellationToken);

    var controller = actionContext.ControllerContext.Controller as ApiController;
    if (controller == null)
    {
        return;
    }
    var user = controller.User as ServiceUser;

    //var user = User as ServiceUser;
    var aadCreds = (await user.GetIdentitiesAsync()).OfType<AzureActiveDirectoryCredentials>().FirstOrDefault();
    Debug.WriteLine(aadCreds.AccessToken);

    var cookie = HttpContext.Current.Request.Cookies["IsInspector"];
    var isInspector = cookie != null ? cookie.Value : null;
    var accessTokenCookie = HttpContext.Current.Request.Cookies["IsInspectorAccessToken"];
    var access_token = accessTokenCookie != null ? accessTokenCookie.Value : null;
    if (isInspector != null && access_token == aadCreds.AccessToken)
    {
        if (!(bool.Parse(isInspector)))
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
        }
        return;
    }

    var token = actionContext.Request.Headers.GetValues(Constants.RefreshTokenHeaderKey)
        .FirstOrDefault();

    var auth = new AuthenticationContext(Constants.ADAuthority, false);
    var newToken = await auth.AcquireTokenByRefreshTokenAsync(token,
        Constants.ADNativeClientApplicationClientId, "
https://graph.windows.net");

    var client = RetrieveActiveDirectoryClient(newToken.AccessToken);
    var grps = await client.Groups.ExecuteAsync();
    var moreGroups = grps.CurrentPage;

    try
    {
        while (moreGroups != null)
        {
            foreach (var grp in grps.CurrentPage)
            {
                if (grp.DisplayName == "Inspectors")
                {
                    if ((await client.IsMemberOfAsync(grp.ObjectId, aadCreds.ObjectId)) ?? false)
                    {
                        HttpContext.Current.Response.Cookies.Add(new HttpCookie("IsInspector", true.ToString()));

                        return;
                    }
                }
            }
            if (grps.MorePagesAvailable)
            {
                grps = await grps.GetNextPageAsync();
                moreGroups = grps.CurrentPage;
            }
            else
            {
                grps = null;
                moreGroups = null;
            }
        }
        HttpContext.Current.Response.Cookies.Add(new HttpCookie("IsInspector", false.ToString()));
    }
    finally
    {
        HttpContext.Current.Response.Cookies.Add(new HttpCookie("IsInspectorAccessToken", aadCreds.AccessToken));

    }
}

Augmenting Mobile Service Response with Azure Active Directory Group Membership

In the previous post we saw how you can query Azure Active Directory after authenticating a Mobile Service client against Azure Active Directory. Now I’m going to use this knowledge to restrict access to data based on group membership. In this case the user has to belong to a group with the name “Inspectors”. One thing you’ll notice is that this process is quite slow, so we’ll have to look for a better way to enforce security, without having to query AAD with each service request. The full GetAll method is as follows:

public async Task<IQueryable<TEntity>> GetAll()
{
    var user = User as ServiceUser;
    var aadCreds = (await user.GetIdentitiesAsync()).OfType<AzureActiveDirectoryCredentials>().FirstOrDefault();
    Debug.WriteLine(aadCreds.AccessToken);

    var token = this.ActionContext.Request.Headers.GetValues(Constants.RefreshTokenHeaderKey)
        .FirstOrDefault();

    var auth = new AuthenticationContext(Constants.ADAuthority, false);
    var newToken = await auth.AcquireTokenByRefreshTokenAsync(token,
        Constants.ADNativeClientApplicationClientId, "
https://graph.windows.net");

    var client = RetrieveActiveDirectoryClient(newToken.AccessToken);
    var grps = await client.Groups.ExecuteAsync();
    var moreGroups = grps.CurrentPage;
    while (moreGroups != null)
    {
        foreach (var grp in grps.CurrentPage)
        {
            if (grp.DisplayName == "Inspectors")
            {
                if ((await client.IsMemberOfAsync(grp.ObjectId, aadCreds.ObjectId)) ?? false)
                {
                    return Query();
                }
            }
            Debug.WriteLine(grp != null);
        }
        if (grps.MorePagesAvailable)
        {
            grps = await grps.GetNextPageAsync();
            moreGroups = grps.CurrentPage;
        }
    }

    return null;
}

Accessing Active Directory Graph API from Azure Mobile Service using Azure Active Directory Authentication

After getting local debugging to work with my Azure AD protected Mobile Service I’m now ready to start querying Azure Active Directory for information about the authenticated user, which can then be used to control what data is returned (being a better approach than allowing the client to determine what information it queries for). However, this turned out to be slightly harder than I thought it would be.

Having taken a look at various blogs/articles that talk about accessing AAD they all referred to the Graph API and there’s a NuGet package that can easily be installed into my service project which will make it easy for me to access the various aspects of AAD.

image

After installing this NuGet package when you run the Mobile Service project you may experience an error where it can’t resolve certain assemblies. To resolve this issue, look in the web.config file and ensure the assemblyBinding section has been correctly updated to point to the version of assemblies referenced by the Mobile Service project, and ensure there are no duplicates.

There appears to be two ways to use the graph client library. The older, and now deprecated, way is to use the GraphConnection class; the new way, which we’ll look at, is using the ActiveDirectoryClient class. This can be instantiate as follows:

public static ActiveDirectoryClient RetrieveActiveDirectoryClient(string token)
{
    var baseServiceUri = new Uri(Microsoft.Azure.ActiveDirectory.GraphClient.Constants.ResourceId);
    var activeDirectoryClient =
        new ActiveDirectoryClient(new Uri(baseServiceUri, Constants.ADTenant),
            async () => token);
    return activeDirectoryClient;
}

Now, my initial assumption was that I could simply pass in the access token that I can extract from the claims passed into the Mobile Service. This isn’t the case but should you need this information, it can easily be retrieved by accessing the User property that’s made available within the base TableController class.

var user = User as ServiceUser;
var aadCreds = (await user.GetIdentitiesAsync()).OfType<AzureActiveDirectoryCredentials>().FirstOrDefault();
Debug.WriteLine(aadCreds.AccessToken);

Of course, what I was trying to do would never work, since the claims I have in my access token are for the Mobile Service endpoint, not for Azure Active Directory (even though AAD authenticated the user). What I need to do is to request an access token with the AAD Graph API as the resource that I’m requesting access to. Luckily I don’t need to go through the whole authentication process, as I can use the RefreshToken that was returned along with the initial access token to request a different access token with permission to access a different resource. But first, I need to go into the Native Client application in AAD and give it permissions to access Windows Azure Active Directory. This might seem a little counter intuitive since we’re going to be accessing AAD from within the Mobile Service but if you think about the access token that will be used, it was issued via the native client application within AAD, so it’s here that we need to control the permission set.

image

The challenge I now faced is that within the Mobile Service I don’t have access to the refresh token; I only have the objectId of the authenticated user in AAD, the tenantId and the access token – this isn’t enough for me to gain access via the graph api. I have two options 1) the native client could use the refresh token it has to request another access token with access to the graph api and then to pass this through with the Mobile Service request, or 2) for the refresh token to be passed through into the Mobile Service and let it request the refresh token. I opted for the option 2 as I’m a bit reluctant to have access tokens out in the wild that can access the graph api and I’d prefer to have that work shielded behind the service facade. In fact, with either option I needed to find a way to pass an additional token through to the Mobile Service – as you can imagine this is easily done if I were manually creating the requests. Luckily the Mobile Service client library allows for a delegatinghandler to be specified that will give me access to the raw requests before they’re sent. So, here are the changes I need to make:

I created a MobileServiceHttpHandler which will insert the refresh token as a header into all requests. The name of the header is somewhat arbitrary so long as you use the same header on the client and server sides. I have RefreshTokenHeaderKey = “X-RefreshToken”

public class MobileServiceHttpHandler : DelegatingHandler
{
    public static string RefreshToken { get; set; }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!string.IsNullOrWhiteSpace(RefreshToken))
        {
            request.Headers.Add(Constants.RefreshTokenHeaderKey, RefreshToken);
        }

        return base.SendAsync(request, cancellationToken);
    }
}

I then updated the declaration of my Mobile Service client to use this new delegatinghandler

public static MobileServiceClient MobileService = new MobileServiceClient(
    Constants.MobileServiceRootUri,
    "wpxaIplp-----------------------XiVlZAPYQEBcg12",
    new MobileServiceHttpHandler()
    );

In the AuthenticationHelper, before returning the access token, I’m going to assign the refresh token to the delegatinghandler:

Debug.WriteLine(authResult != null);
MobileServiceHttpHandler.RefreshToken = authResult.RefreshToken;
return authResult.AccessToken;

In my Mobile Service I have a simple method to create an instance of the ActiveDirectoryClient:

public static ActiveDirectoryClient RetrieveActiveDirectoryClient(string token)
{
    var baseServiceUri = new Uri(Microsoft.Azure.ActiveDirectory.GraphClient.Constants.ResourceId);
    var activeDirectoryClient = new ActiveDirectoryClient(new Uri(baseServiceUri, Constants.ADTenant), async () => token);
    return activeDirectoryClient;
}

In one of the methods, I’m going to use the GetAll method, of my RealEstateBaseTableController (in the Mobile Services project), I’m going to add the following code:

var token = this.ActionContext.Request.Headers.GetValues(Constants.RefreshTokenHeaderKey).FirstOrDefault();

var auth = new AuthenticationContext(Constants.ADAuthority, false);
var newToken = await auth.AcquireTokenByRefreshTokenAsync(token, Constants.ADNativeClientApplicationClientId, "
https://graph.windows.net");

var client = RetrieveActiveDirectoryClient(newToken.AccessToken);
var grps = await client.Groups.ExecuteAsync();

Here you can see that I’m retrieving the groups that are defined in this tenant within AAD. I also had to update the method signature to make it async and return a Task:

public async Task<IQueryable<TEntity>> GetAll()

If you simply run this, you’re unlikely to see any results unless you have at least one group defined in your AAD. To create a group, go to the tenant within AAD and select the Groups tab. Create a new group by assigning it a name and description:

image

In the next post we’ll take this a step further an use this knowledge to control what the user can access.

Debugging Azure Active Directory Authentication with Azure Mobile Service .NET Backend When Running Locally

In a recent project one of the more challenging things was to debug the Azure Mobile Services (AMS) when running locally. For the most part it was pretty straight forward as a lot of the requests, whilst they required a user to be authenticated, the returned data wasn’t contingent on who the user was. When you run Azure Mobile Services locally, which you can with the .NET backend but not with the Node.js backend, by default it doesn’t enforce any of the security requirements. This makes it easy to debug scenarios where it doesn’t matter who the user is but very hard for those scenarios where the data needs to be altered based on who the user is, particularly if the user is being authenticated using Azure Active Directory (AAD). In this post I’ll walk through getting debugging to run locally.

The first thing I need to do is to configure the Mobile Service to enforce the same security when running locally, as if it were running in Azure. I do this by adding the following immediately following the ServiceConfig.Initialize line:

// This tells the local mobile service project to run as if it is being hosted in Azure,
// including honoring the AuthorizeLevel settings. Without this setting, all HTTP requests to
// localhost are permitted without authentication despite the AuthorizeLevel setting.
config.SetIsHosted(true);

Side Note (feel free to skip over this section if you’re not interested on how I attempted to get this to work)

My initial approach was to use what I new about the way AAD connects with AMS which involves an application within AAD that defines the relationship between the native client (which is authenticating) and the AMS (which is the resource it is requesting access to). As my AMS is running locally it is hosted at http://localhost:51539/ I thought that I needed to create another AAD Web application pointing at http://localhost:51539/login/aad eg

image

And then in the Native Client AAD application I had to delegate permissions to the new AAD Web application eg

image

In the native application, in this case my WPF application, I changed both the resource url for the AAD authentication and the mobile services url to use http://localhost:51539/ ie http://localhost:51539/login/aad for the resource url and just http://localhost:51539/ for the mobile services url. This seemed to work correctly initially as it displayed the sign in prompt, I could sign in and get back an AAD access token. Unfortunately when this was passed to the AMS it failed miserably with an unauthorised exception.

Note: I actually feel what I did here was correct that that the Mobile Services team are incorrectly validating the claims based on the illusion that the local services is being hosted in the cloud. This means you actually need to authenticate with AAD as if you were requesting access to the resource https://realestateinspector.azure-mobile.net/login/aad and then actually connect to the locally running mobile service.

End of Side Note – Here’s how you really do it

Ok, so you DON’T need to change the url that is being specified as the resource when signing into AAD. However, you DO need to change the url that is used when creating the instance of the mobile service client. My constants file now looks like this:

#if DEBUG
#define DEBUGLOCAL
#endif

public static class Constants
{
    public const string ADTenant = "realestateinspector.onmicrosoft.com";
    public const string ADAuthority="
https://login.windows.net/" + ADTenant;

    public const string ADNativeClientApplicationClientId = "a5a10ee9-f871-4bde-997f-3f1c323fefa5";

    public const string ADRedirectUri = "http://builttoroam.com";

#if DEBUGLOCAL
    public const string ActualMobileServiceRootUri = "
https://realestateinspector.azure-mobile.net/";
    public const string MobileServiceRootUri = "http://localhost:51539/";
    public const string MobileServiceAppIdUri = ActualMobileServiceRootUri + "login/aad";
#else
    public const string MobileServiceRootUri = "
https://realestateinspector.azure-mobile.net/";
    public const string MobileServiceAppIdUri = MobileServiceRootUri + "login/aad";
#endif
}

Note that I’ve used an inline compilation constant to allow me to switch from debugging against the local services versus those hosted in the cloud.

If you attempt to run this, the user will be directed to authenticate using the AAD signin with a resource https://realestateinspector.azure-mobile.net/login/aad and then the application will attempt to login to the Mobile Service running locally at http://localhost:51539/ (Note – I typically set both the native client project (in this case the WPF application) and the Mobile Service project as start projects). However, you’ll still get an unauthorized exception because the Mobile Service actual does verify that the claims in the access token returned from AAD match the settings of the Mobile Services (excluding the base url that the service is being run from - see my early argument why this imho is the wrong approach). Let’s first take a look at the access token using the site http://jwt.io.

image 

If you look in the Web.config file for the Mobile service you’ll see that there is a list of Mobile Service settings in the appSettings section. The first of which is typically the MS_MobileServiceName and I’m guessing it’s this setting that is being used to validate that the claim in the token is for the right mobile serivce – in the token you can see that the resource is https://realestateinspector.azure-mobile.net/login/aad where the only piece that varies between Mobile Services is the service name.

<add key="MS_MobileServiceName" value="realestateinspector" />
The next thing to look at is in the actual login request that the mobile service client library makes:

POST: https://realestateinspector.azure-mobile.net/login/aad
X-ZUMO-INSTALLATION-ID: c2ac49d1-af97-472b-af61-bfb06663f137
X-ZUMO-APPLICATION: wpxaIplpeXtknXQhqXiVlZAPYQEBcg12
X-ZUMO-AUTH: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ1cm46bWljcm9zb2Z0OndpbmRvd3MtYXp1cmU6enVtbyIsImF1ZCI6InVybjptaWNyb3NvZnQ6d2luZG93cy1henVyZTp6dW1vIiwibmJmIjoxNDIyMDkxOTY3LCJleHAiOjE0MjIwOTU0OTYsInVybjptaWNyb3NvZnQ6Y3JlZGVudGlhbHMiOiJ7XCJ0ZW5hbnRJZFwiOlwiZTY4OGQ1OTQtODY0My00YmRmLTllNGMtMGJlOGJjYmM2NDVmXCIsXCJvYmplY3RJZFwiOlwiYWJkNzc5MjgtNDFkMS00YzNjLThmNmYtMmE1NmQ4NDRkMDVlXCIsXCJhY2Nlc3NUb2tlblwiOlwiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKU1V6STFOaUlzSW5nMWRDSTZJbXR5YVUxUVpHMUNkbmcyT0hOclZEZ3RiVkJCUWpOQ2MyVmxRU0o5LmV5SmhkV1FpT2lKb2RIUndjem92TDNKbFlXeGxjM1JoZEdWcGJuTndaV04wYjNJdVlYcDFjbVV0Ylc5aWFXeGxMbTVsZEM5c2IyZHBiaTloWVdRaUxDSnBjM01pT2lKb2RIUndjem92TDNOMGN5NTNhVzVrYjNkekxtNWxkQzlsTmpnNFpEVTVOQzA0TmpRekxUUmlaR1l0T1dVMFl5MHdZbVU0WW1OaVl6WTBOV1l2SWl3aWFXRjBJam94TkRJeU1Ea3hOalUyTENKdVltWWlPakUwTWpJd09URTJOVFlzSW1WNGNDSTZNVFF5TWpBNU5UVTFOaXdpZG1WeUlqb2lNUzR3SWl3aWRHbGtJam9pWlRZNE9HUTFPVFF0T0RZME15MDBZbVJtTFRsbE5HTXRNR0psT0dKalltTTJORFZtSWl3aVlXMXlJanBiSW5CM1pDSmRMQ0p2YVdRaU9pSmhZbVEzTnpreU9DMDBNV1F4TFRSak0yTXRPR1kyWmkweVlUVTJaRGcwTkdRd05XVWlMQ0oxY0c0aU9pSnBibk53WldOMGIzSkFjbVZoYkdWemRHRjBaV2x1YzNCbFkzUnZjaTV2Ym0xcFkzSnZjMjltZEM1amIyMGlMQ0p6ZFdJaU9pSm5WMlJUY0VwbllqSnBUbkpyWm5wbVlUQlViWGhPTFZGeU0yMXFhamhoY0ZoZlYxbG5lVGRrVDNOUklpd2libUZ0WlNJNklrbHVjM0JsWTNSdmNpSXNJblZ1YVhGMVpWOXVZVzFsSWpvaWFXNXpjR1ZqZEc5eVFISmxZV3hsYzNSaGRHVnBibk53WldOMGIzSXViMjV0YVdOeWIzTnZablF1WTI5dElpd2lZWEJ3YVdRaU9pSmhOV0V4TUdWbE9TMW1PRGN4TFRSaVpHVXRPVGszWmkwelpqRmpNekl6Wm1WbVlUVWlMQ0poY0hCcFpHRmpjaUk2SWpBaUxDSnpZM0FpT2lKMWMyVnlYMmx0Y0dWeWMyOXVZWFJwYjI0aUxDSmhZM0lpT2lJeEluMC5IVEhlUEFEcW5XYjE2ZTdwYTQ1VkppdFlTV2pvanZzaEwyZ3I4cTR3S1k2QkpWNF9INXBoQklmQ1p3RUtGeU05MlpQWkpza1NDVkl5QlNvNnU0eTgyUlJKcWxmVW9NN3hjRG1zZ0dOVlNYZzhoOGhJWm56X1ZIcGg4ZUp4TXRvZ0VrR2tZWXFld0R5Wi1zWlVTaVZ5ckhpZWlCd2Qzd2M4T0hQaDRXeGUwbUh6OHItekZoOGtmY2lQWW9KRkJ3aGczVGFaZ3hMQWJnaWVMUG83V1ZBUFpIUjV5SG1UaW02RXhQOGdUejQ0WmFiYTI3QnpISUI1dV9oa1F2QWlmU2M2blBXcVZwcC1CamZQM25mbXQwai0xemkwb1RHMVlvMjZNQzNXSHhCTUdKbGpKcVg3UmhBenV4anZESDFyRG5PeE9YaldSVk1qV2gyNzRXUjdXa0YwM0FcIn0iLCJ1aWQiOiJBYWQ6Z1dkU3BKZ2IyaU5ya2Z6ZmEwVG14Ti1RcjNtamo4YXBYX1dZZ3k3ZE9zUSIsInZlciI6IjIifQ.s6fjFOzFhdvvYgL3yD3lUEiUcxALg-avOvwJb1gILDU
Accept: application/json
User-Agent: ZUMO/1.3 (lang=Managed; os=Windows; os_version=6.2.0.9200; arch=Win32NT; version=1.3.21121.0)
X-ZUMO-VERSION: ZUMO/1.3 (lang=Managed; os=Windows; os_version=6.2.0.9200; arch=Win32NT; version=1.3.21121.0)
Content-Type: application/json; charset=utf-8
Host: localhost:51539
Cookie: ARRAffinity=0289d9a2e779a2431db31b4a154e84828a77f89dbbe1fe391d5fe9794f54f970
Content-Length: 1237
Expect: 100-continue
Accept-Encoding: gzip
Connection: Keep-Alive

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtyaU1QZG1Cdng2OHNrVDgtbVBBQjNCc2VlQSJ9.eyJhdWQiOiJodHRwczovL3JlYWxlc3RhdGVpbnNwZWN0b3IuYXp1cmUtbW9iaWxlLm5ldC9sb2dpbi9hYWQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9lNjg4ZDU5NC04NjQzLTRiZGYtOWU0Yy0wYmU4YmNiYzY0NWYvIiwiaWF0IjoxNDIyMDkxNjU2LCJuYmYiOjE0MjIwOTE2NTYsImV4cCI6MTQyMjA5NTU1NiwidmVyIjoiMS4wIiwidGlkIjoiZTY4OGQ1OTQtODY0My00YmRmLTllNGMtMGJlOGJjYmM2NDVmIiwiYW1yIjpbInB3ZCJdLCJvaWQiOiJhYmQ3NzkyOC00MWQxLTRjM2MtOGY2Zi0yYTU2ZDg0NGQwNWUiLCJ1cG4iOiJpbnNwZWN0b3JAcmVhbGVzdGF0ZWluc3BlY3Rvci5vbm1pY3Jvc29mdC5jb20iLCJzdWIiOiJnV2RTcEpnYjJpTnJrZnpmYTBUbXhOLVFyM21qajhhcFhfV1lneTdkT3NRIiwibmFtZSI6Ikluc3BlY3RvciIsInVuaXF1ZV9uYW1lIjoiaW5zcGVjdG9yQHJlYWxlc3RhdGVpbnNwZWN0b3Iub25taWNyb3NvZnQuY29tIiwiYXBwaWQiOiJhNWExMGVlOS1mODcxLTRiZGUtOTk3Zi0zZjFjMzIzZmVmYTUiLCJhcHBpZGFjciI6IjAiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJhY3IiOiIxIn0.HTHePADqnWb16e7pa45VJitYSWjojvshL2gr8q4wKY6BJV4_H5phBIfCZwEKFyM92ZPZJskSCVIyBSo6u4y82RRJqlfUoM7xcDmsgGNVSXg8h8hIZnz_VHph8eJxMtogEkGkYYqewDyZ-sZUSiVyrHieiBwd3wc8OHPh4Wxe0mHz8r-zFh8kfciPYoJFBwhg3TaZgxLAbgieLPo7WVAPZHR5yHmTim6ExP8gTz44Zaba27BzHIB5u_hkQvAifSc6nPWqVpp-BjfP3nfmt0j-1zi0oTG1Yo26MC3WHxBMGJljJqX7RhAzuxjvDH1rDnOxOXjWRVMjWh274WR7WkF03A"
}

The X-ZUMO-APPLICATION header specified in the request needs to match the MS_ApplicationKey setting specified in the Web.config file. Most of these settings have a default value of “Overridden by portal settings” which, as it implies, is because they are overridden when the Mobile Service is hosted in the cloud. However, when running locally some of these settings need to be specified in order to get authentication to work. In this case it’s both the MS_ApplicationKey and MS_AadTenants:

<add key="MS_ApplicationKey" value="wpxaIplpeXtknXQhqXiVlZAPYQEBcg12" />
<add key="MS_AadTenants" value="realestateinspector.onmicrosoft.com" />

With this in place, running the native application again and you can login, retrieve the AAD access token, and then when the mobile service client attempts to login it will do successfully. When you then make a call to one of the controllers, you can set a break point and access the User property to retrieve information about the current user and their claims. This image shows the AAD claim for this user.

 

image

Xamarin.Forms, Windows Phone, Azure Active Directory and iOS

In my previous post I thought that there was an issue with using the Azure Active Directory Authentication Library (ADAL) and the updated Xamarin for iOS. This has been confirmed by the team at Microsoft responsible for ADAL with a view that it will be resolved in a future version (no specifics on which version or when). This is unfortunate as it means my iOS version will have to be put on hold for the timebeing.

On a different note I saw a mention in the forums that apparently Xamarin.Forms 1.4 will support Windows Phone 8.1 (and I hope as part of a universal app, also supporting Windows 8.1). Of course, there’s no timing on this one but does pose the question as to whether I should wait for the next iteration of Xamarin.Forms before proceeding with the sample apps. For the timebeing I’ll continue and see how far I get.

Active Directory Authentication with iOS with Xamarin.Forms

Essentially this doesn’t appear to currently work. Due to the alignment of iOS projects to the Unified APIs I think there is currently a compatibility issue between the ADAL prerelease library and the Xamarin.Forms implementation. I’ll come back to this once we have a resolution for this.

Adding Azure Active Directory Authentication to Android Xamarin.Forms Project

In my previous post I covered adding authentication to the Xamarin Forms Windows Phone 8.0 project. Next step, add it to the Android project. I figured this would be much simpler. However after adding a reference to ADAL from NuGet my Android project failed to build with some esoteric error about some missing layout xml in a referenced component. A quick internet search later I figured it might be due to the path lengths of my projects. I’d been relatively descriptive in my project names (eg RealEstateInspector.XForms.Droid) which had resulted in some very long paths, particularly during the build process where components are added based on the compilation path, the referenced library path and of course the file name. The upshot was that I needed to shorten not the project names, just the physical paths of the projects (easily done via VSO and Source Control Explorer within VS).

After doing the path restructure, I then amended the login the MainActivity:

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
}
protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);

    global::Xamarin.Forms.Forms.Init(this, bundle);
    RealEstateInspector.XForms.MainPage.AuthenticateRequested += Authenticate;
    LoadApplication(new App());
}

public async void Authenticate(object sender, EventArgs e)
{
    var page = sender as RealEstateInspector.XForms.MainPage;
    var token = await AuthenticationHelper.Authenticate(this);
    Debug.WriteLine(token);
    (page.BindingContext as MainViewModel).LoadPropertyData(token);
}

You’ll notice that the activity is passed into the Authenticate method, which means I’ve had to make some minor changes to the AuthenticationHelper code:

public static async Task<string> Authenticate(
#if DROID
    Activity callerActivity
#endif
    )
{
    try
    {
        var authContext = new AuthenticationContext(Constants.ADAuthority);
#if !SILVERLIGHT
        if (authContext.TokenCache.ReadItems().Count() > 0)
            authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
#endif
        var authResult =
            await
                authContext.AcquireTokenAsync(Constants.MobileServiceAppIdUri,
                Constants.ADNativeClientApplicationClientId,
                new Uri(Constants.ADRedirectUri),
#if WINDOWS_PHONE_APP || SILVERLIGHT
                new AuthorizationParameters()
#elif DROID
new AuthorizationParameters(callerActivity)
#else
                    new AuthorizationParameters(PromptBehavior.Auto, false)
#endif
                );
        Debug.WriteLine(authResult != null);

        return authResult.AccessToken;

    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
        return null;
    }
}

At this point I also ran into an issue using my dummy redirect uri of http://tba.com, which is actually a real website and has an alternative mobile site which they redirect to. The issue was that after the user had authenticated the web view would redirect to http://tba.com but would then be redirected to the mobile site. I switched to using http://builttoroam.com which I know doesn’t have a redirect. Don’t forget when doing this you also have to update the redirect uri in the Native application in Azure Active Directory.

Adding Azure Active Directory Authentication to Windows Phone 8.0 Application with Xamarin.Forms

In the previous post I covered addition authentication to the Windows platform applications but I explicitly excluded the Windows Phone 8.0 project that’s part of the XForms set of projects. This was because the ADAL doesn’t currently support Windows Phone 8.0. In this post we’ll add a custom implementation which will authenticate the user using a WebBrowser control within the Windows Phone 8.0 application. We’ll start by adding a reference to the Windows Phone 8.0 project to the Shared.Client project – this will cause build errors as there is no reference to ADAL so the classes which are referenced in this shared code don’t exist. I’ll make some small amendments using the SILVERLIGHT compilation attribute (note that this is only defined for the WP8.0 project):

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
#if !SILVERLIGHT
using Microsoft.IdentityModel.Clients.ActiveDirectory;
#else
using RealEstateInspector.XForms.WinPhone;
#endif
using RealEstateInspector.Core;

namespace RealEstateInspector.Shared.Client
{
    public static class AuthenticationHelper
    {

        public static async Task<string> Authenticate()
        {
            try
            {
                var authContext = new AuthenticationContext(Constants.ADAuthority);
#if !SILVERLIGHT
                if (authContext.TokenCache.ReadItems().Count() > 0)
                    authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
#endif

                var authResult =
                    await
                        authContext.AcquireTokenAsync(Constants.MobileServiceAppIdUri,
                        Constants.ADNativeClientApplicationClientId,
                        new Uri(Constants.ADRedirectUri),
#if WINDOWS_PHONE_APP || SILVERLIGHT
                        new AuthorizationParameters()
#else
                            new AuthorizationParameters(PromptBehavior.Auto, false)
#endif
                        );
                Debug.WriteLine(authResult != null);

                return authResult.AccessToken;

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                return null;
            }
        }
    }
}

The next thing to do is to provide an implementation of the AuthenticationContext class and it’s associated classes. These are added to the Windows Phone 8.0 project:

public class AuthenticationResult
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
}

public class AuthorizationParameters
{
}

public class AuthenticationContext
{
    public string Authority { get; set; }
    public string Resource { get; set; }
    public string ClientId { get; set; }
    public Uri RedirectUri { get; set; }

    public AuthenticationContext(string authority)
    {
        Authority = authority;
    }

    public async Task<AuthenticationResult> AcquireTokenAsync(
        string resource,
        string clientId,
        Uri redirectUri,
        AuthorizationParameters parameters)
    {
        Resource = resource;
        ClientId = clientId;
        RedirectUri = redirectUri;
        var code = await Authenticate();

        var http = new HttpClient();

        var tokenUrl = string.Format("{0}/oauth2/token",Authority);
        var formData = new Dictionary<string, string>
        {
            {"grant_type","authorization_code"},
            {"client_id",ClientId},
            {"code",code},
            {"resource",Resource},
            {"redirect_uri",RedirectUri.OriginalString}
        };

        var content = new FormUrlEncodedContent(formData);
        var data = await http.PostAsync(new Uri(tokenUrl), content);
        var result = JsonConvert.DeserializeObject<AuthenticationResult>(await data.Content.ReadAsStringAsync());
        return result;
    }

    private ManualResetEvent authenticateWaiter = new ManualResetEvent(false);
    private string AccessToken { get; set; }
    public async Task<string> Authenticate()
    {
        authenticateWaiter.Reset();
        var authUrlTemplate =
            "{0}/oauth2/authorize?response_type=code&client_id={1}&redirect_uri={2}";
        var authUrl = string.Format(authUrlTemplate,
            Authority,
            ClientId,
            Uri.EscapeDataString(RedirectUri.OriginalString)
            );

        var page = (Application.Current.RootVisual as Frame).Content as Page;
        var firstChild = page.Content;
        if (!(firstChild is Grid))
        {
            page.Content = null;
            var gd = new Grid();
            gd.Children.Add(firstChild);
            page.Content = gd;
            firstChild = gd;
        }

        var mainGrid = firstChild as Grid;
        var browser = new WebBrowser
        {
            IsScriptEnabled = true
        };
        browser.Navigating += BrowserNavigating;
        Grid.SetRowSpan(browser, (mainGrid.RowDefinitions != null && mainGrid.RowDefinitions.Count > 0) ? mainGrid.RowDefinitions.Count : 1);
        Grid.SetColumnSpan(browser, (mainGrid.ColumnDefinitions != null && mainGrid.ColumnDefinitions.Count > 0) ? mainGrid.ColumnDefinitions.Count : 1);
        mainGrid.Children.Add(browser);
        browser.Navigate(new Uri(authUrl));

        await Task.Run(() => authenticateWaiter.WaitOne());
        return AccessToken;
    }

    private void BrowserNavigating(object sender, NavigatingEventArgs e)
    {
        if (e.Uri.OriginalString.ToLower().StartsWith(RedirectUri.OriginalString.ToLower()))
        {
            try
            {
                var query = e.Uri.OriginalString.Substring(e.Uri.OriginalString.IndexOf('?') + 1);
                var code = (from pair in query.Split('&')
                            let bits = pair.Split('=')
                            where bits.Length == 2
                                    && bits[0] == "code"
                            select bits[1]).FirstOrDefault();
                AccessToken = code;
            }
            catch (Exception ex)
            {
                AccessToken = null;
            }
            finally
            {
                authenticateWaiter.Set();
                var browser = sender as WebBrowser;
                browser.Navigating -= BrowserNavigating;
                (browser.Parent as Grid).Children.Remove(browser);
            }
        }
    }
}

Reading this code you’ll see that I’m manually inserting and removing a WebBrowser control into the current page of the WP8.0 application. This is then used to authenticate the user and return the

authorization code which is then used to make the POST to request the access token (basically following what I did in my previous post when I walked this through manually in the browser/Fiddler).

I’ve also updated the XAML in my XForms MainPage:

<Page
    x:Class="RealEstateInspector.MainPage"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.Resources>
            <DataTemplate
                x:Key="PropertyItemTemplate">
                <TextBlock
                    Text="{Binding Address}"
                    FontSize="30"
                    Foreground="WhiteSmoke"/>
            </DataTemplate>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Content="Authenticate" Click="AuthenticateClick"/>
        <ListView
            ItemTemplate="{StaticResource PropertyItemTemplate}"
            Grid.Row="1"
            ItemsSource="{Binding Properties}"/>
    </Grid>
</Page>

As well as the code behind to handle the button click:

public static event EventHandler AuthenticateRequested;

public void AuthenticateClick(object sender, EventArgs e)
{
    if (AuthenticateRequested != null)
    {
        AuthenticateRequested(this, EventArgs.Empty);
    }
}

Lastly in the MainPage of my Windows Phone 8.0 application I’ve wired up a listener for the AuthenticateRequested event to make sure I can trigger the authentication process:

public MainPage()
{
    InitializeComponent();
    SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;

    global::Xamarin.Forms.Forms.Init();
    RealEstateInspector.XForms.MainPage.AuthenticateRequested += Authenticate;
    LoadApplication(new RealEstateInspector.XForms.App());
}

public async void Authenticate(object sender, EventArgs e)
{
    var page = sender as RealEstateInspector.XForms.MainPage;
    var token = await AuthenticationHelper.Authenticate();
    Debug.WriteLine(token);
    (page.BindingContext as MainViewModel).LoadPropertyData(token);
}