Nick's .NET Travels

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

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.

Pingbacks and trackbacks (1)+

Comments are closed