Nick's .NET Travels

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

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 WPF Client with Azure Active Directory Authentication and Azure Mobile Service

In today’s post I was going to cover adding Azure AD authentication to my iOS project but I don’t have my Mac build machine handy so there’s a bit of a change of plans. Today I’m going to add a WPF desktop application to my solution and catch it up to where the other client applications are up to. I’ll start by creating the WPF Application.

image

At this point I realised that I missed setting the framework in the Add New Project dialog, so I opened the Properties pane of the application and on the Application tab I set the Target framework to .Net Framework 4.5.

image

Next we need to add references to the following solution projects:

- RealEstateInspector.Core

- RealEstateInspector.Shared.Client

And then add reference to both the ADAL and Mobile Service SqliteStore packages. From the downloads page on sqlite.org you’ll also need to download the “32-bit DLL (x86) for SQLite” which is under the Precompiled Binaries for Windows heading. Unblock and Extract the zip file and add the sqlite3.dll file to the new WPF project, setting the Build Action to Content and setting the Copy to Output Directory to Copy always.

I also need to check the Prefer 32-bit checkbox and define the DESKTOP compilation symbol for All Configurations.

image

I’ll add the following XAML markup to the MainWindow.xaml

<Window x:Class="RealEstateInspector.Desktop.MainWindow"
        xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <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>
</Window>

And the following code to the MainWindow.xaml.cs

public partial class MainWindow : IWin32Window
{
    public IntPtr Handle
    {
        get
        {
            var interopHelper = new WindowInteropHelper(this);
            return interopHelper.Handle;
        }
    }
    public MainWindow()
    {
        InitializeComponent();

        Loaded += MainWindow_Loaded;
    }
    public MainViewModel CurrentViewModel
    {
        get { return DataContext as MainViewModel; }
    }
    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        var vm = new MainViewModel();
        DataContext = vm;
    }

    private async void AuthenticateClick(object sender, RoutedEventArgs e)
    {
        var token = await AuthenticationHelper.Authenticate(Handle);
        await CurrentViewModel.LoadPropertyData(token);
    }
}

I also had to make some minor changes to the AuthenticationHelper

public static class AuthenticationHelper
{

    public static async Task<string> Authenticate(
#if DROID
    Android.App.Activity callerActivity
#elif DESKTOP
IntPtr callerHandle
#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)
#elif DESKTOP
                        new AuthorizationParameters(PromptBehavior.Auto, callerHandle)
#else
                        new AuthorizationParameters(PromptBehavior.Auto, false)
#endif
                    );
            Debug.WriteLine(authResult != null);

            return authResult.AccessToken;

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

And that’s it, the WPF desktop application can be run up, the user can sign in and properties are synchronized before being displayed.

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);
}

Authenticating With Azure Active Directory In Universal Windows Platform Applications

The AAD team are continuing to evolve the samples and NuGet packages that can be referenced when authenticating with Azure Active Directory. In this post I’m going to add authentication to the Windows platform applications using the Active Directory Authentication Library (package id: Microsoft.Indentity.Clients.ActiveDirectory) and the corresponding sample found on github (https://github.com/AzureADSamples/NativeClient-MultiTarget-DotNet). One limitation of the current ADAL is that it doesn’t support Windows Phone 8.0 – adding support for this platform is slightly harder than for universal applications as there is no WebAuthenticationBroker so doing authentication will have to be done via a hosted web browser control within the application.

The first step in adding authentication support across the applications is to a reference to the ADAL package. I’ve added it to the Windows and Windows Phone 8.1 projects, as well as the iOS and Android XForms applications. As the package doesn’t support WP8.0 it can’t be added to either the XForms portable class library, the Core portable class library, nor the Windows Phone 8.0 projects. At this stage we’ll exclude the Mobile Service project as we currently don’t need ADAL there.

Into the RealEstateInspector.Core portable class library I’m going to add a Constants class which contains the various values required when requesting the authorization code and access token using the ADAL.

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://tba.com";

    public const string MobileServiceAppIdUri= "https://realestateinspector.azure-mobile.net/login/aad";
}

Rather than having to duplicate the authentication code between each of the client projects I’m going to create another Shared project called RealEstateInspector.Shared.Client and add a class called AuthenticationHelper. I’ll add a reference to this project to all of the client projects, except the Windows Phone 8.0 project, since this is currently unsupported.

public static class AuthenticationHelper
{

    public static async Task<string> Authenticate()
    {
        try
        {
            var authContext = new AuthenticationContext(Constants.ADAuthority);
            if (authContext.TokenCache.ReadItems().Count() > 0)
                authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
            var authResult =
                await
                    authContext.AcquireTokenAsync(Constants.MobileServiceAppIdUri,
                    Constants.ADNativeClientApplicationClientId,
                    new Uri(Constants.ADRedirectUri),
#if WINDOWS_PHONE_APP
                    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;
        }
    }
}

In our Windows platform projects I need to update the AuthenticationClick methods as follows:

private async void AuthenticateClick(object sender, RoutedEventArgs e)
{
    var token = await AuthenticationHelper.Authenticate();
    await CurrentViewModel.LoadPropertyData(token);

}

And I’ve made a simple tweak to the LoadPropertyData method to accept the access token as a parameter.

The last thing to do is in the App.xaml.cs file to add an override to the OnActivated method. Note that this is required due to the differences in the WebAuthenticationBroker implementations between Windows and Windows Phone 8.1.

protected override void OnActivated(IActivatedEventArgs args)
{
    base.OnActivated(args);

#if WINDOWS_PHONE_APP
    if (args is IWebAuthenticationBrokerContinuationEventArgs)
    {
        WebAuthenticationBrokerContinuationHelper.SetWebAuthenticationBrokerContinuationEventArgs(args as IWebAuthenticationBrokerContinuationEventArgs);
    }
#endif

}

At this point whilst we’ve added references to ADAL to the client projects, the only applications that can be run and signed into are the Windows platform applications (Windows and Windows Phone 8.1).

Accessing Authentication Required Mobile Service

Previously I added security to my mobile service by Requiring Authentication on the Azure Mobile Service and in my previous post I showed how you can manually request an authorization and access token. In addition to exploring the id_token (which is the unassigned JWT) if you explore the access_token you can see that it has a claim pertaining to the Mobile Service

image

We’re going to use this value to authenticate against our Mobile Service, which will allow us to query the data. In my MainViewModel I’m going to adjust the code slightly to use this access token value – this is just to demonstrate how the access token is going to be used, of course in a real world application you’ll need to authenticate the user using a web browser in the application in order to retrieve the access_token, but more on that later. My LoadPropertyData method now looks like this (the access_token value has been shortened to make the code more readable but was copied directly from the response in fiddler:

public async Task LoadPropertyData()
{
    var access_token="eyJ0eXAiOiJKV1Qi---------TOF9eYN97Jey-r_oDq5anQ";
    var jobj = new JObject();
    jobj["access_token"] = access_token;
    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 MobileServiceSyncHandler());

    await MobileService.PullLatestAsync<RealEstateProperty>();
    await MobileService.PullLatestAsync<Inspection>();

    var props = await MobileService.GetSyncTable<RealEstateProperty>().ToListAsync();
    foreach (var prop in props)
    {
        Properties.Add(prop);
    }
}

This code can be run and will retrieve the properties in the Mobile Service, at least whilst the access_token remains valid

Manually Using Fiddler to Authenticate (Part II – Actual Walkthrough)

In this second part of Manually Using Fiddler to Authenticate I’ll use a combination of web browser and fiddler to request both an authorization code and then an access token for the Azure Active Directory I setup in an earlier post. This is going to follow the workflow covered in this MSDN document.

There are a couple of things I’ll need before I start:

- The tenant id of the AAD that I’m going to be signing into. This can be the actual id (eg e688d594-8643-4bdf-9e4c-0be8bcbc645f which you can see in the address bar when editing information via the Azure portal eg https:[email protected][email protected]-9e4c-0be8bcbc645f) or the domain:

realestateinspector.onmicrosoft.com

- The Client Id of the application I setup for the native client (remember that I’m effectively simulating the workflow that my native clients will go through, so it makes sense to use that AAD application)

client_id=a5a10ee9-f871-4bde-997f-3f1c323fefa5

- The redirect uri that was defined in the AAD application for the native client. When we actually come to use this process in the native client we will of course have to determine the native clients redirect uri and add that to the AAD application but for the timebeing I’ll use the value I specified when setting up the AAD application:

redirect_uri=http//tba.com

NOTE: I need to make sure that I appropriately url encode any query string parameters (there are plenty of web based converters – I use this URL decoder/encoder), so in this case the redirect uri will need to be specified as follows:

redirect_uri=http%3A%2F%2Ftba.com

Putting this all together we can generate the url for requesting an authorization code https://login.windows.net/realestateinspector.onmicrosoft.com/oauth2/authorize?response_type=code&client_id=a5a10ee9-f871-4bde-997f-3f1c323fefa5&redirect_uri=http%3A%2F%2Ftba.com. Navigating to this url via a web browser will prompt the user to sign in using the standard aad sign in prompt

image

After signing in the user will be redirected to the redirect uri, in this case http://tba.com with the authorization code specified in the address eg http://www.tba.com/?code=AAABAAAAvPM1KaPlrEqdFSBzjqfTGKrfFcsSklul1Lrd_bv-lbWetE6ZyTlruH0Yy7bB6Zue1lg0hpJ_2h5dLxd8gweAItUT6Hgvh7dXcyKsTuYfW_gpo9kkiGHYzxo53ayrrBJqsVyPLkJ6SagQp7_8vyfvieYrGF2tTXy_bMPpA3HG63qe_D_Iqpie9AtSnXWT7ax2UlufFSHNiu4pqIPxz_yc_TXBAHNKjSCevVTbLc4Kg71pyf3galmjXwi72KxDRn4QSwmY9Gdmhllo3A1ywOVrfct0DtZhe3oxzIdVpi5TUhKmDxbsnk2kHo60H1_SnmMFoIY-H2GTWdEScF0kPEa7NztBTJ9RIvx3YBMSPL4tZCgWo6Ta-xfCfeo47LZmWr_k07f1N85S1dhfumeVsSORGJI3CtTnnQxDxbLCKhaQweIW08pl1198STbwpFokuOMjH20KXX2KdbXxoaw67wWMsoyQypVDhBFgDPcZqHPOVT1J8pj-G6ic_WR3JtQ_Ig1xIAA&session_state=d2b5f011-74db-4b0e-84eb-979250fdee97

The next step is to request an access token. Now remember that this access token should be one that has permissions to access the Mobile Service I setup. As such, when requesting the access token I need to specify the Mobile Service as the resource I want to have access to. Here are the attributes I need for the access token request.

grant_type=authorization_code
client_id=a5a10ee9-f871-4bde-997f-3f1c323fefa5                <—
same as previous request
code=AAABAAAAvPM1KaP-----------_WR3JtQ_Ig1xIAA      <—truncated for brevity 
resource=https://realestateinspector.azure-mobile.net/login/aad    <—
the Mobile Service APP ID URI specified in the AAD application setup for the Mobile Service
redirect_uri=http://tba.com     <—the same redirect uri used in the authorization code request

Putting this all together into a POST request that I can issue in Fiddler:

POST: https://login.windows.net/realestateinspector.onmicrosoft.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&client_id=a5a10ee9-f871-4bde-997f-3f1c323fefa5&code=AAABAAAAvPM1KaPlrEqdFSBzjqfTGKrfFcsSklul1Lrd_bv-lbWetE6ZyTlruH0Yy7bB6Zue1lg0hpJ_2h5dLxd8gweAItUT6Hgvh7dXcyKsTuYfW_gpo9kkiGHYzxo53ayrrBJqsVyPLkJ6SagQp7_8vyfvieYrGF2tTXy_bMPpA3HG63qe_D_Iqpie9AtSnXWT7ax2UlufFSHNiu4pqIPxz_yc_TXBAHNKjSCevVTbLc4Kg71pyf3galmjXwi72KxDRn4QSwmY9Gdmhllo3A1ywOVrfct0DtZhe3oxzIdVpi5TUhKmDxbsnk2kHo60H1_SnmMFoIY-H2GTWdEScF0kPEa7NztBTJ9RIvx3YBMSPL4tZCgWo6Ta-xfCfeo47LZmWr_k07f1N85S1dhfumeVsSORGJI3CtTnnQxDxbLCKhaQweIW08pl1198STbwpFokuOMjH20KXX2KdbXxoaw67wWMsoyQypVDhBFgDPcZqHPOVT1J8pj-G6ic_WR3JtQ_Ig1xIAA&resource=https%3A%2F%2Frealestateinspector.azure-mobile.net%2Flogin%2Faad&redirect_uri=http%3A%2F%2Ftba.com

What I get back from this request is a JSON response

image

The response gives a number of return values which can be used for a variety of things, one of which is the unassigned JSON Web Token which you can explore using tools such as http://jwt.io/.

image

Manually Using Fiddler to Authenticate

One thing I would encourage anyone planning to use Azure Active Directory to authenticate users is to understand a bit more about the oauth 2.0 workflow. To this end I’m going to follow the instructions on the blog post, Using Fiddler to acquire a JWT ( JSON Web Token ) for use with the Graph API, to access content from the Azure Mobile Service. I’d also suggest reading this page which covers the Authorization Code Grant Flow

Requiring Authentication on the Azure Mobile Service

In the previous post I configured a Azure Active Directory and the Mobile Service with the appropriate application definitions. This in itself isn’t enough – you can prove this by making a request using Fiddler to the existing controllers eg  GET https://realestateinspector.azure-mobile.net/tables/realestateproperty (don’t forget you’ll need to specify the X-ZUMO-APPLICATION header). Mobile Services already require the use of the X-ZUMO-APPLICATION key to be specified on requests, but what we want to do is enforce a user-level authentication.Luckily it’s as simple as adding the AuthorizationLevel attribute to the controllers with the level set to AuthorizationLevel.User. In the following code I’ve applied the attribute to the base controller I created earlier.

public class RealEstatePropertyController : RealEstateBaseTableController<RealEstateProperty> { }

public class InspectionController : RealEstateBaseTableController<Inspection> { }

[AuthorizeLevel(AuthorizationLevel.User)]
public class RealEstateBaseTableController<TEntity> : TableController<TEntity>
    where TEntity : class, ITableData
{

When I publish this update and attempt to run the same Fiddler request I get a 401 unauthorized exception:

HTTP/1.1 401 Unauthorized
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 61
Content-Type: application/json; charset=utf-8
Expires: 0
Server: Microsoft-IIS/8.0
WWW-Authenticate: Basic realm="Service"
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=0289d9a2e779a2431db31b4a154e84828a77f89dbbe1fe391d5fe9794f54f970;Path=/;Domain=realestateinspector.azure-mobile.net
Date: Wed, 14 Jan 2015 11:14:21 GMT

{"message":"Authorization has been denied for this request."}

Adding Azure Active Directory Authentication to Azure Mobile Service

In my post “Add Azure Active Directory Support to Azure Mobile Services-Enabled Windows Phone Apps” I go through how to setup Azure Mobile Service to work with Azure Active Directory. Firstly I’m going to create a separate directory for this application. From the toolbar at the bottom of the Azure management portal I select App Service –> Active Directory –> Directory –> Custom Create

image 

The Name of the Azure Active Directory (AAD) is a friendly name which you can use to identify the directory in the portal. The Domain Name has to be unique and will be the identifier, and default tenant, of the directory.

image

The next thing I’ll need is to create two Application entries within the AAD. You can think of applications as the glue that connects AAD to both the Mobile Service (the resource being protected) and the native client applications.

I’ll first create an application that will link AAD to the Mobile Service. From the Applications pane in the AAD, click Create, followed by the link to “Add an application my organisation is developing”.

image

Next I need to give the application a name (again this is for identification in the portal – as you’ll see the name I chose is quite long and gets truncated in some of the later views, so I’d suggest a shorter name), and select “Web Application and/or Web API” under the Type heading.

image 

On the next pane I’m prompted to enter a App Id and Reply Url. On a separate pane I’ll open up the Mobile Service and go to the Identity pane. At the bottom under the Windows Azure Active Directory section I’ll copy out the App Url (ie https://realestateinspector.azure-mobile.net/login/aad). This value should be set for both App Id and Reply Url. After completing the creation process for the application, copy the Client Id value from the Configure pane and switch back to the Identity pane of the Mobile Service. Enter the Client Id into the Client Id field under the Windows Azure Active Directory section. I also need to specify the allowed tenant, which in this case is the default tenant from the AAD I created earlier.

image

Next I’ll create an application that will connect our native client applications to AAD. This time I’ll select Native Client Application under the Type heading.

image

On the next pane I’m prompted for a redirect url. For the timebeing I’m going to enter a placeholder value (eg http://tba.com)– we’ll come back to this when I start writing the code in the native applications to authenticate with AAD.

Now that I have applications that represent both the Mobile Service and the native client applications, I need to establish a trust relationship between them. From the Configure pane of the application for the native clients, select Add Application from the “permissions to other applications” section. Select Other in the Show dropbox and click the tick button to invoke the search. Select the application for the Mobile Service (ie in this case the Real Estate Inspector Mobile Service) and click the tick button at the bottom of the dialog.

image

From the Delegated Permissions dropdown, check the box next to “Access Real Estate Inspector Mobile Service” and click Save from the lower toolbar of the pane.

image

This completes the configuration of AAD to work with Mobile Service and Native Client applications. In the coming posts we’ll leverage this configuration to authenticate the mobile applications.

Building the Xamarin.Forms Basic Layout Using XAML

The initial content page that was created when we created the XForms projects was done in code. Rather than doing the layout in code, I prefer to work in XAML – not only does this make the design more declarative, it is also miles easier to data binding. I’ll start by creating a new Forms Xaml Page.

image

Next we’ll add some XAML to the page (similar to what we added to the Windows MainPage):

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="
http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="RealEstateInspector.XForms.MainPage">
  <ListView ItemsSource="{Binding Properties}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <ViewCell.View>
            <Label Text="{Binding Address}" />
          </ViewCell.View>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>
</ContentPage>

And in the code behind for the MainPAge we’ll create, assign and load data into the MainViewModel:

protected async override void OnAppearing()
{
    base.OnAppearing();

    var vm = new MainViewModel();
    BindingContext = vm;
    await vm.LoadPropertyData();
}

And of course I need to update the code in App.cs to use the new XAML MainPage, rather than the code created ContentPage:

public App()
{
    // The root page of your application
    MainPage =new MainPage();
}

When you go to run this you’re likely to see errors due to lack of references for the SQLite extensions. Most references will get added if they are used by referenced libraries. What makes SQLite different is that there are platform specific implementations which are only added to the platform specific project. As such you need to make sure all the client applications have the SQLiteStore NuGet package referenced. I ran into issues applying the NuGet package to the Android project as it seemed to not be able to find the HttpClient NuGet package – I had to add this NuGet package to the Android project first, before applying the SqliteStore NuGet Package.

image

After doing this I was able to run up the XForms applications on each of the platforms, each displaying the addresses of the properties in my local offline database.

Thinking About MVVM and View Models

At this point I’m rather postponing the inevitable need to add an MVVM framework into my application. I’m not going to delve into into what MVVM is as I think that’s a conversation for another day and there are existing sites/reference that more than do this topic justice. However, enough to say that there is an important role for MVVM in the mobile app development space, particularly with Windows platform and Xamarin.Forms where we can use data binding. The reason I’ve been putting it off is that in the past I would have gone the MVVMcross route but I’m yet to see this work against Xamarin.Forms. This leaves me with the need to evaluate other frameworks such as MvvmLite (a long term player in the MVVM space), ReactiveUI and Calcium. I’m still on the hunt for the next killer framework that I’ll use religiously, in the meantime I’m going to avoid the need to make a decision by starting by manually creating and assigning the viewmodel for the main page of the application.

To abstract our view models away from the specifics of how the data is going to be presented (ie the premise of MVVM) I’ll create a separate Portable Class Library which will contain my view models.

image

When setting the targets, don’t forget to include Windows Phone Silverlight 8, otherwise you won’t be able to use the PCL from the XForms project.

image

Into this library I’ll create a ViewModels folder and then my first view model, MainViewModel. I’ll then migrate the logic that I had in my shared code from my Windows platform applications across into this view model. This includes

- Adding NuGet package reference to the Microsoft.WindowsAzure.Mobile and Microsoft.WindowsAzure.Mobile.SQLiteStore (just add the latter as the former will be added automatically)

- Add reference to the shared entities project so that the view models can access the data entities

- Remove references to the shared entities projects from the Windows platform projects (otherwise you’ll end up with type reference errors since you’ll effectively have two copies of the entity classes).

- Move the MobileServiceClientExtensions class I created previously into the new portable class library

- Add reference to the new PCL to the XForms and Windows platform projects.

The final outcome is a view model which loads all the properties when the LoadPropertyData method is invoked

public class MainViewModel
{

    public static MobileServiceClient MobileService = new MobileServiceClient(
        "https://realestateinspector.azure-mobile.net/",
        "wpxaIplpeX-----------------------------g12"
        );

    private ObservableCollection<RealEstateProperty> properties = new ObservableCollection<RealEstateProperty>();

    public ObservableCollection<RealEstateProperty> Properties
    {
        get { return properties; }
    }

    public async Task LoadPropertyData()
    {
        var data = new MobileServiceSQLiteStore("inspections.db");
        data.DefineTable<RealEstateProperty>();
        data.DefineTable<Inspection>();

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

        await MobileService.PullLatestAsync<RealEstateProperty>();
        await MobileService.PullLatestAsync<Inspection>();

        var props = await MobileService.GetSyncTable<RealEstateProperty>().ToListAsync();
        foreach (var prop in props)
        {
            Properties.Add(prop);
        }
    }
}

Let’s return to our Windows platform applications and wire up a basic interface. In the MainPage.xaml for the Windows project I’ll add some basic layout – this is just to demonstrate that properties are being synchronised and loaded.

<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>
        <ListView ItemTemplate="{StaticResource PropertyItemTemplate}"
                  ItemsSource="{Binding Properties}"/>
    </Grid>
</Page>

Adding Xamarin.Forms Support

So far I’ve only had support for the new Windows platform clients (ie Windows and Windows Phone) for my Real Estate Inspector sample app. However, to make it successful we need to target more platforms. To this end I’m going to add support for iOS, Android and Windows Phone 8.0 using Xamarin.Forms.

After installing the Xamarin tooling for Visual Studio, I added a new project using the Blank App (Xamrain.Forms Portable) template. This actually look multiple attempts as I hadn’t updated the Xamarin tooling prior to creating the projects. I would highly recommend upgrading the tooling first!

image

This will give you three target projects Driod, iOS and WinPhone, in addition to a PCL which contains the common code and ui for the applications. You should be able to build and run each of these targets – however, you’ll need to either register for a trial or have a Xamarin Business subscription. Since the UI is help in the PCL project, it’ll be the same across all the targets, although there may be platform rendering differences.

Now that we have these three targets, we’re going to have to connect up our Azure Mobile Service, work out how we’re going to deal with navigation and structure our solution to maximise code reuse across our Windows platform projects.

Improving the Differential Synchronisation using Azure Mobile Services

As a mobile developer one of the things I’m critical of is ensuring that network requests are kept to a minimum. Having built several sync solutions in the past I was a little surprised with the current Azure Mobile Service implementation, and here’s why. By now, if you’ve read my previous posts you’ll recall seeing that each table is synchronised by invoking PullAsync to retrieve the latest updates from the Mobile Service, and invoke PushAsync to push local changes up to the Mobile Services. This seems reasonable – however, what I saw when I ran Fiddler is that each time I synchronised a table I would always seem the most recent entry being returned. Not believing what I saw I repeatedly ran the PullAsync method, and sure enough, each time I invoked it, the most recent entity would be returned. This in itself wouldn’t be so bad but the way that the Mobile Service offline support works out if it has retrieved all the entities for a query is to add the $skip query string to each query until no items are returned. The upshot is that not only does the most recent entity get returned, a second unnecessary call is made which returns no items – so there’s a network bandwidth and latency cost!

The reason for this is that there is a difference in time resolution between Entity Framework and SQL Server. Let’s use Visual Studio, or SQL Server Management Studio, or the online Azure SQL management tool, to query the contents of the Inspections table. Here we can see that the most recent update to this table was at 2015-01-09 11:45:40.0194886 +00:00.

image

Now, let’s take a look at the queries that PullAsync generates – the query filter is updatedAt >= 2015-01-09 11:45:40.0190000 +00:00. Firstly, this uses ge which is greater than or equal. Secondly, look at the lack of precision in the timestamp – this means you’re always going to get the most recent entry returned (at least). You’ll see that the second query uses the same filter, except it adds $skip=1 to jump over the entry that was returned by the first query.

https://realestateinspector.azure-mobile.net/tables/Inspection?$filter=(__updatedAt%20ge%20datetimeoffset'2015-01-09T11%3A45%3A40.0190000%2B00%3A00')&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__createdAt%2C__updatedAt%2C__version%2C__deleted
https://realestateinspector.azure-mobile.net/tables/Inspection?$filter=(__updatedAt%20ge%20datetimeoffset'2015-01-09T11%3A45%3A40.0190000%2B00%3A00')&$orderby=__updatedAt&$skip=1&$top=50&__includeDeleted=true&__systemproperties=__createdAt%2C__updatedAt%2C__version%2C__deleted

Now, let’s see if we can improve the query – we’re going to change this:

var inspectionQ = MobileService.GetSyncTable<Inspection>().CreateQuery();
await MobileService.GetSyncTable<Inspection>().PullAsync(typeof(Inspection).Name, inspectionQ);

To this:

await MobileService.PullLatestAsync<Inspection>();

Where PullLatestAsync is an extension method defined as follows:

public static class MobileServiceClientExtensions
{
    public async static Task PullLatestAsync<TTable>(this IMobileServiceClient client) where TTable : BaseEntityData
    {
        // Get the most recent
        var mostRecent = await client.GetLatestAsync<TTable>();

        // Convert the most recent into a query (assuming there is one)
        if (mostRecent != null)
        {
            var maxTimestamp = mostRecent.UpdatedAt.AddMilliseconds(1);
            var q = client.GetSyncTable<TTable>()
                .CreateQuery()
                .Where(x => (x.Id != mostRecent.Id || x.UpdatedAt > maxTimestamp));
            // Do a (filtered) pull from the remote tabl
            await client.GetSyncTable<TTable>().PullAsync(typeof(TTable).Name, q);
        }
        else
        {
            await client.GetSyncTable<TTable>().PullAsync(typeof(TTable).Name, client.GetSyncTable<TTable>().CreateQuery());
        }
    }

    public async static Task<TTable> GetLatestAsync<TTable>(this IMobileServiceClient client) where TTable : BaseEntityData
    {
        return (await client.GetSyncTable<TTable>()
                            .OrderByDescending(x => x.UpdatedAt)
                            .Take(1)
                            .ToListAsync()).SingleOrDefault();
    }
}

This extension method requires us to extend our BaseEntityData to include the UpdatedAt property so that we can use it in our query:

public class BaseEntityData
#if SERVICE
    : EntityData{}
#else
{
    public string Id { get; set; }

    [CreatedAt]
    public DateTimeOffset CreatedAt { get; set; }

    [UpdatedAt]
    public DateTimeOffset UpdatedAt { get; set; }

    [Version]
    public string Version { get; set; }

}
#endif

Running this now, we see that our filter expression has been extended to exclude the most recent entity, unless it has changed.
https://realestateinspector.azure-mobile.net/tables/Inspection?$filter=(((id%20ne%20'6d9f9a19-da8f-4bd6-a4d3-dd888a67e00e')%20or%20(__updatedAt%20gt%20datetimeoffset'2015-01-09T22%3A45%3A40.0200000%2B11%3A00'))%20and%20(__updatedAt%20ge%20datetimeoffset'2015-01-09T11%3A45%3A40.0190000%2B00%3A00'))&$orderby=__updatedAt&$skip=0&$top=50&__includeDeleted=true&__systemproperties=__createdAt%2C__updatedAt%2C__version%2C__deleted

Synchronizing Data for Offline Access with Azure Mobile Service

One of the core requirements of our real estate inspection tool is that it’s available offline. To this end I can leverage the synchronisation component of the Azure Mobile Service SDK to synchronise the entities we referenced in the previous post. In order to make use of this I will need to add another NuGet package which is the WindowsAzure.MobileService.SqliteStore package – you can think of this as the Sqlite storage wrapper which will handle persisting and providing an access layer to the offline data.

image

One thing you may notice that that there is a warning in your References list after adding this NuGet package. In this case you either haven’t got the Windows/Windows Phone Sqlite Extensions installed, or your version doesn’t align with the reference that was added as part of adding the previous package. After making sure you have the most recent version of the Sqlite extensions installed (get them from the Sqlite Download Page), you can then simply delete the reference with the warning, and then add a new reference to the extensions you just installed.

image

Next, you’ll probably still see warnings against the Sqlite and Visual C++ references – this is because neither of these references supports compiling using the AnyCPU option. Switch to building for one of the other platforms (x86 is probably the easiest to use for development since it’s compatible with the Windows Phone emulator, just don’t forget that if you’re testing  on an RT device or on a real Windows Phone, you’ll need to use an ARM build).

image

Now we have our references, let’s add some code to demonstrate synchronising, reading and writing from the offline data store. I already have my MobileService that I setup in a previous post but I’m going to replace my the method call to GetTable<>.ToListAsync() with code that will synchronise data for offline access. I’ll start by defining the offline data file, inspections.db, along with the entity tables that I want to include in the database. I then initialize the SyncContext of the MobileService using this data store

var data = new MobileServiceSQLiteStore("inspections.db");
data.DefineTable<RealEstateProperty>();
data.DefineTable<Inspection>();

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

Invoking a sync is actually a two part process. First you need to pull data down from the Mobile Service. Here we’re passing in a default table query (which will pull down all entities in the table), and using the name of the data type as the synchronisation key. I repeat this for both entity types I want to synchronize.

var propertyQ = MobileService.GetSyncTable<RealEstateProperty>().CreateQuery();
await MobileService.GetSyncTable<RealEstateProperty>().PullAsync(typeof(RealEstateProperty).Name, propertyQ);

var inspectionQ = MobileService.GetSyncTable<Inspection>().CreateQuery();
await MobileService.GetSyncTable<Inspection>().PullAsync(typeof(Inspection).Name, inspectionQ);

You’ll notice that I’m calling GetSyncTable instead of GetTable – this is because I want to reference the local offline table, rather than the Mobile Service endpoint. Now that I’ve pulled data down from the Mobile Service I can call ToListAsync on the sync table to retrieve all the elements that are contained in the offline table.

var props = await MobileService.GetSyncTable<RealEstateProperty>().ToListAsync();

Inserting items is a simple as creating them and inserting them into the sync table

var inspection = new Inspection
{
    InspectedBy = "Joe",
    RealEstatePropertyId = props[0].Id
};
await MobileService.GetSyncTable<Inspection>().InsertAsync(inspection);

Of course this only adds them to the local offline table. To push them up to the Mobile Service I call PushAsync on the synchronization context.
await MobileService.SyncContext.PushAsync();

Lastly to validate that the new items have appeared in the Mobile Service I can query it directly to get all items in the Mobile Service table

var inspections = await MobileService.GetTable<Inspection>().ToListAsync();

If you’re interested in what’s stored in the offline data file it can be found at C:\Users\<username>\AppData\Local\Packages\<package id>\LocalState and you can use a tool like SqliteStudio to view the contents of the database file

image

And you can use the SQL query window to write and execute sql to view the data.

image