Using Microsoft Graph in an Azure Function


The Microsoft Graph is becoming more and more an essential technology inside the Microsoft ecosystem. It was born as a set of APIs specific to integrate apps with the Office 365 suite, but it's expanding more and more to become the single point of access to every data related to a user authenticated with a Microsoft Account or a work / school account.
Many of the Windows and cross-device experiences that have been launched or showcased at BUILD are powered by the Microsoft Graph: Timeline, Project Rome, the Your Phone app, etc.

The integration of the Microsoft Graph in your application always starts from the authentication. In order to access to all the information of the user, we need to make sure it's logged in a secure way. A great way to handle this scenario is using Azure Functions. We can use them to quickly build an ecosystem of REST APIs to interact with the Microsoft Graph and leverage the built-in authentication support.

There's a lot of documentation around this scenario and the Azure team has even built a couple of extensions to make this scenario really easy to implement. However, right now there are a couple of open issues which can drive you crazy, so I've decided to share the right steps to follow to make this scenario working.

Setting up the Azure Function

Azure Functions are a new approach to build APIs based on a serverless paradigm. This means that we don't have to worry about the underlying infrastructure of the API, but we can just focus on what we want to achieve. For example, compared to a traditional WebAPI project, we don't have to handle and maintain the web application, configure the routing, create a Visual Studio project, etc. With Azure Functions we can just write the code of the method we want to expose, using one of the available languages (Java, C#, JavaScript, etc.). The function is associated to a trigger, which is the event that causes its execution. It can be a HTTP trigger (so executed when a client hits the HTTP endpoint associated to the function), it can be a time trigger, it can be an event like a file created in the cloud.
Another big difference compared to the standard WebAPI approach is the consumption: Azure Functions are paid by number of executions and, as such, you're going to be billed only when you're effectively using them. If no clients are invoking your function, you won't be billed. This makes Azure Functions quite cheap: with an Azure subscription you get 1 millions of free executions; every consequential usage is billed at 0.20 $ per million.

In our sample we're going to build an Azure Function, which returns all the basic information about an AAD user using the Microsoft Graph.

Let's start by logging to your Azure Portal. Choose Create a resource and select Serverless Function App.

Now you need to setup your Azure Function, by specifying:

  • The app name, which will be prefixed to the domain .azurewebsites.net and it will become the URL of your web service.
  • Your Azure subscription, in case you have more than one.
  • A resource group: you can create a new one or use an existing one.
  • The OS where you want to host the function. Since the 2.0 version of the runtime (currently in preview) is based on .NET Core, you can also opt-in for a Linux backend. This post is based on a Windows backend.
  • Hosting plan: you can choose the consumption model (the one I've described before) or, if you can prefer, you can host the function inside an App Service and use the same hosting plan.
  • The Azure region.
  • The storage which will be used to store the function's infrastructure. Also in this case you can create a new one or leverage an existing storage.
  • If you want to turn on Application Insights to gather analytics and exception logging during the usage.

Now you just need to wait a while for the portal to finish the creation of the required infrastructure. Once everything is setup, you can open the dashboard on the Azure Function you have just created. As first step, move to the Platform features tab and choose Function app settings. Here you need to change the Runtime version from ~1 to beta (which currently means using the 2.0.xxxxx.0 version). This is required because the Microsoft Graph extensions we're going to leverage are supported only by the newest version of the runtime. We need to do this as first step because we can't change the runtime once we have created one or more functions.

Please note Make sure that the runtime version you're using is at least the 2.0.11888.0 one. Previous versions contain a bug which prevents the usage of the Microsoft Graph extensions we're going to leverage later.

The second important thing we need to do is to setup the Authentication. Since Azure Functions are hosted by an App Service, we can leverage a feature called Easy Auth. This means that we can add support to various authentication providers (like AAD, Microsoft Account, Facebook, etc.) without manually implementing the flow, but just by sharing with Azure the authentication information that the provider has shared with us (typically, an application identifier and a client secret).

In our case we're going use our function to access to the Microsoft Graph and, as such, we're interested in enabling authentication using Azure Active Directory. Move again to the Platform features tab and, this time, choose Authentication / Authorization.
Turn on the App Service Authentication switch to reveal all the different configuration options and the list of supported providers. As first thing, from the dropdown labeled Action to take when request is not authorized choose Login with Azure Active Directory. This means that our function won't allow anonymous calls, but the user will be required to authenticate using AAD when the function is invoked.

Then click on Azure Active Directory, where you will be asked to choose between Express or Advanced configuration. The easiest one is Express, but it will allow only to configure the authentication using the same Active Directory linked to your Azure account. For example, this is what I see when I use this option with my Azure subscription, which is linked to my @microsoft.com account:

The advantage of the Express configuration is that I don't have to manually register my application with my Azure Active Directory tenant. I just need to choose the option Create new AD App, give it a name (by default, it will be the same name of your Azure Function) and then press OK. The Azure portal will do everything for me.
Otherwise, if you want to connect your application to an Active Directory tenant different than the one linked to your Azure subscription, you can choose the Advanced mode, which will require you to register your application inside the Azure Active Directory tenant and then specify the Client ID, Issuer URL and Client Secret returned by the portal, in addition to the callback URL.

Here you can find all the steps to follow in case you need to perform the manual configuration.

Based on the usage you want to make of the Microsoft Graph, you may want to click on the Manage Permissions button and choose which information you want to expose. Keep in mind that some of them require Admin permissions to be enabled, so you will have to be Administrator of the Azure Active Directory tenant you're trying to setup. In my case, I'm just looking to get the user's profile, which is enabled by default.

Once you have completed the setup, press OK and then Save at the top of the Authentication / Authorization blade. Now we'ready to write some code! Move fo the Functions section in the tree on the left and choose New function. Choose HTTP Trigger (we want to build a REST endpoint), then select one of the supported languages (in my case, C#) and then give it a name.
As Authorization level choose Anonymous. By default this value is set to Function, which means that our function will be protected by unauthorized calls and we will be required to add a secret key to its URL if we want to invoke it. In our case, since we already have a level of protection (the AAD authentication), we can safely set this option to Anonymous. This way, we will be able to invoke the function simply by calling the URL associated to it.

The function editor will appear and it will contain the basic skeleton of a function. As you can see, an Azure Function is really just... a function! There's no infrastructure, no web project, no configuration. Just a Run() method, which receives as input the HTTP request that triggered it.

If we want the full power of an IDE, we have the opportunity also to create a function in Visual Studio or in Visual Studio Code. In this case, we will have access to all the Visual Studio features (Intellisense, local debugging, etc.) and the opportunity to include libraries and components using NuGet. However, in this case we will lose the opportunity to edit and configure the function right in the Azure portal.

For the moment, let's ignore the IDE approach since the Microsoft Graph integration still has some hiccups. Let's write some code directly in the web editor. Here is how our function will look like:

using System.Net; 
using System.Net.Http; 
using System.Net.Http.Headers; 
using System.Text;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, string graphToken, TraceWriter log)
{
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", graphToken);
    var me = await client.GetStringAsync("https://graph.microsoft.com/v1.0/me/");

    return new HttpResponseMessage(HttpStatusCode.OK) 
    {
        Content = new StringContent(me, Encoding.UTF8, "application/json")
    };
}

Thanks to the Easy Auth approach supported by the App Service, we don't have to perform the authentication against ADD ourselves. We can just add a parameter to the Run() method, called graphToken in this case, which will contain the authentication token required to perform any operation against the Microsoft Graph.
As such, we can just build a new HttpClient object and add it to the authentication header, by using the DefaultRequestHeaders.Authorization property and the AuthenticationHeaderValue object. This object is just a key-pair value, composed by the the name of the header (Bearer) and its value (the token).

Then, using the GetStringAsync() method exposed by the HttpClient object, we hit the Microsoft Graph endpoint to retrieve the basic information about the logged user. We take the returning JSON and we send it back as a result of the function, by encapsulating it inside a HttpResponseMessage object.
This is just one of the many available endpoints exposed by the Microsoft Graph. If you want to see all of them and understand how they work, you can use the great Graph Explorer tool.

Now that we have created the function, we need to customize the bindings. The bindings are the way the various input and output parameters of the function are translated. As you can see, we have written a function which accepts, as input, a HttpRequestMessage object and a token as a string (plus a TraceWriter object used for logging purposes); as output, instead, it returns a HttpResponseMessage object. However, we haven't specified anywhere where these input and ouput data are coming from. We can do this from the Integrate section that you can find on the left when you expand the function you have created.

By default, you will see a visual editor that will allow you to configure the trigger, the inputs and the outputs.
Let's start from the Triggers.

In most of the cases, all the default options will be good for us. The trigger, by default, supports the GET and POST HTTP methods and, based on the function's code we've written before, the name of the parameter which contains the HTTP request is req.

Now let's move to the Inputs section.

Click on the New Input button and choose Auth token from the list. Let's complete the configuration by filling the other fields:

  • Under Identity choose Use from HTTP request, since the authentication token will be included directly as header of the request.
  • Set as Resource URL the domain we want to use the authentication for. In our case it's https://graph.microsoft.com
  • Set as Auth token parameter name the name of the input parameter which will contain the authentication token. Since we have called the parameter of the Run() method graphToken, change the default value to graphToken.

If you have done everything properly, the box in the middle of the page should display a list of green marks, which will tell you that the authentication configuration is correct.

I will skip the Outputs section, since it doesn't require any special configuration. It just specifies which is the value returned by the function.

All the information we have setup so far are translated into bindings, which are described inside a JSON file called function.json. You can see the content of this file by clicking on the Advanced editor button on the top right of the Integrate section:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "type": "token",
      "name": "graphToken",
      "resource": "https://graph.microsoft.com",
      "identity": "UserFromRequest",
      "direction": "in"
    }
  ]
}

As you can see, it's nothing more than a JSON representation of the configuration we just did in a visual way.

Now we're ready to test the function. Go back to the code editor and click on the Get function URL button. Copy the URL associated to the function. It will be something like https://*my-custom-domain*.azurewebsites.net/api/*my-function-name*) and paste it into your browser. I suggest you to use an InPrivate instance of your favorite browser, to avoid being automatically logged in with the wrong account.

The first time you hit the url, you will be asked to login your AAD account and to authorize the web application (in this case, the Azure Function) to access to your profile. Complete the login and observe... your function failing miserably with a server error.

If you go back to the code editor, you will find a section called Logs at the bottom. Open it to get access to the real-time logs and you should see an error similar to the following one:

[Error] System.Private.CoreLib: Exception while executing function: Functions.GetUserProfile. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'graphToken'. Microsoft.Azure.WebJobs.Host: No value was provided for parameter 'graphToken'.

Please note: if, instead, you see an exception like:

'Microsoft.Azure.WebJobs.Extensions.Tokens, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified

it means that you're using a runtime version lower than 2.0.11888.0. In this case, there's nothing you can actively do. You just need to wait for your Azure Function instance to get the required update.

The reason why you're seeing this exception is that the older versions of the Microsoft Graph extensions contained some bugs that prevented the binding to work properly. As such, the graphToken parameter of the Run() method isn't being injected with the authentication token.

In order to fix this, we need to force the update of the extensions to the last version, which contains the required fixes.

In order to do this, let's return to the to our Azure Function dashboard and stop the function. Then move to the Platform Features section and choose Advanced Tools (Kudu). This will open up the advanced diagnostic tools for the App Service.

Choose Debug console from the top menu, then click on CMD. You will open a command prompt on the server's folder which contains the web application that powers your Azure Function. Move to the site --> wwwroot folder.
Now using your favorite editor (in my case, Visual Studio Code) create a new file on your machine called extensions.csproj and add the following content:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.MicrosoftGraph" Version="1.0.0-beta3" />
        <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.AuthTokens" Version="1.0.0-beta3" />
        <PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.0.0-beta2" />
    </ItemGroup>
</Project>

Now drag and drop the file from your computer to the list of files included in wwwroot folder. Once the upload has been completed, move to the command prompt below and type the following command:

dotnet build -o bin extensions.csproj

The process will take approximately 3 minutes. It will care of downloading the last version of the Microsoft Graph packages (defined in the csproj file you have just created) and compiling them inside your Azure Function folder.

Now feel free to close the advanced tools and return to the Azure Portal. Start again the function, then open the logs and hit again the URL. This time... the function will fail again, but with another exception.

Microsoft.IdentityModel.Clients.ActiveDirectory: Response status code does not indicate success: 400 (BadRequest). {"error":"invalid_request","error_description":"AADSTS240002: Input id_token cannot be used as 'urn:ietf:params:oauth:grant-type:jwt-bearer'"}

This problem is caused by a configuration of the Azure Active Directory application. With the default parameters, in fact, the authentication token isn't properly recognized. In order to fix this, we need to change a value in the manifest of the AAD application.

Let's return again to the Platform Features section of our function and open the Authentication / Authorization section. Click on Azure Active Directory under the Authentication Providers section and then press the Manage Application button at the bottom.

Click on Manifest and, inside the JSON editor, look for the attribute oauth2AllowImplicitFlow, which should be set to false. Change it to true and then save.

Now close every instance of your browser or open an InPrivate window. We need to trigger a new authentication flow in order to propagate the change to the client (in our case, the Azure Function). Hit again the HTTP url of your function and... crossing fingers, this time you should see the JSON response with your AAD profile being displayed inside the browser!

Wrapping up

In this post we have seen the power of Azure Functions combined with the Microsoft Graph. Thanks to the built-in authentication support, handling the whole authentication process is a much easier process than having to implement everything on your own. However, right now, you may hit some blockers in building an Azure Function which connects to the Microsoft Graph, due to some bugs included in the function runtime and in the Graph Extension SDKs. Hopefully they will be fixed soon but, in the meantime, this post should give you all the information you need to get up & running.

Happy coding!

Comments (0)

Skip to main content