Working with identity in an Azure Function

Andreas Helland
Contosio Labs
Published in
7 min readApr 21, 2016

--

Did I mention Azure Functions can be quite neat?

While open APIs on the Internet are great, you might want to restrict your HTTP-Triggered Functions ever so slightly. At least when you’re still in the development process. True, it’s not necessarily an easily guessable url. And currently a code needs to be present in the query string that you can’t guess either, but this is just security through obscurity. (I haven’t seen Microsoft claim otherwise either so this isn’t a design flaw.)

Azure Functions is built on top of Azure App Service, so you can actually turn on some features more or less “for free” without writing extra code. Authentication is one of those things. (Off-topic — it can be fun to setup OAuth and OpenID Connect properly too, so you should learn it so you can use it outside Functions.) I’ll take a spin through setting up authentication, and do an API call showing what it can be good for. I’ll assume you already have a C#-based Function App, or know where you can create one in the Azure portal. (You can head over to https://functions.azure.com, and get started if you haven’t been there already.)

Once you have a Function App you need to switch on authentication before it will work. To enforce authentication on your Functions go to “Function app settings”, and then click “Configure Authentication”.

Function App Settings

I’m making the assumption that you spring for Azure Active Directory in the Express variety for this article. (I’m also making the assumption that if you’re using Azure services you’re either using AAD already, or you should be planning to do so.)

App Services Authentication Settings

While Facebook, Google, etc. are acceptable options in their own right, I’m not going through that for now. (The code here is specific to Azure AD.) The express setup configures a single-tenant app, so if you have ideas for offering this to customers across multiple AAD tenants it’s not going to work either. Multi-tenancy is a scenario one can support, but let’s keep it simple for now.

The top of the “developer frame” should have a link you can invoke in your browser.

If you try to open the link you will automatically be redirected to the login page for your Azure AD tenant if you haven’t signed in already in a different tab, and you will directed back following successful login. (Which is a fairly standard login flow.) Depending on the template you chose, and whether you included any extra values in the query string things may or may not work right now, but you should be authenticated if nothing else.

So let’s get started filling in some code — start with a clean slate or plug in the bits you need where you want them.

You need to start with the assemblies needed:

#r “Newtonsoft.Json”
#r “System.Configuration”
#r “Microsoft.IdentityModel.Clients.ActiveDirectory.dll”
using System.Net;
using System.Configuration;
using System.Security.Claims;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Microsoft.IdentityModel.Clients.ActiveDirectory;

#r is used to include/reference external assemblies in a .csx file. The .dll we refer to needs to be included/uploaded to your Function’s disk. To do this you need to open the Kudu debug console (replace functions-xyz with your Functions App name):
https://functions-xyz.scm.azurewebsites.net/DebugConsole/?shell=powershell

Navigate to \home\site\wwwroot\functionname and create a \bin folder that you hop into. Locate the Microsoft.IdentityModel.Clients.ActiveDirectory.dll file, (I had it on disk already since using it in a solution I developed in Visual Studio), and do a drag & drop to the browser window.

Maybe you don’t happen to have it on your hard drive already? The longer version for getting the file is to get the .nupkg from NuGet:
https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/
Rename the file to .zip, and extract the .dll from the \lib\net45 folder.

It’s not an optimal procedure yet, but I’m hoping the tooling for Azure Functions will improve and make the process more streamlined.

While Azure Functions isn’t intended for creating large namespaces and libraries in a single Function you can create extra classes and methods inline. For the sake of simplicity we create a class to be used for deserializing a json response later on:

/// A “reduced” user object containing only a few attributes
public class AADUser
{
public string displayName { get; set; }
public string userPrincipalName { get; set; }
public string mobilePhone { get; set; }
}

A lot of work before writing the actual Function, but it looks more elaborate in writing than it is.

There are three main parts to our little Function:
I: Reading out info from the claims passed by the client’s browser. We need this both for an additional “id check”, and to gather info to be used as parameters in subsequent code.
II: Acquiring a token that the server can use to do lookups. (We are using the client credentials flow for OAuth. Which should only be used in a back-end context; not in a mobile app.)
III: Call the Microsoft Graph to get a basic user object.

There is one more “step 0” though. The Express authentication setup configures the app to support OpenID Connect for signing in and acquiring a token. For our purposes a server-based method for token acquisition is also needed, so we need to navigate to the app properties and configure a client secret. I will do this in the “legacy” Azure portal: https://manage.windowsazure.com

Find your Function App under the Active Directory blade, and click through to the Configure tab. You will need to retrieve a key, and make sure the permissions are checked as seen in the screenshot.

Application Configuration in Azure Active Directory

While you can use the key as any normal varible directly in the Function I would recommend not doing so, and instead defining it as an “App Setting”. (This will keep it out of source control as well.)
Back to new portal, “Function App Settings”, “Go to App Service Settings” & “Application Settings”.
Add “clientSecret” as a key, and the key you copied as the value:

App Settings clientSecret key/value

So, will there ever be any actual code written in this post? I think we’re ready now.

We need some parameters to call into Azure AD. Apart from the clientSecret we just configured these can mostly be read from the claims the user has after signing in. (If you want to learn more about claims I have covered that in a separate place: http://aadguide.azurewebsites.net/claims/)

string resourceId = “https://graph.microsoft.com";
string tenantId = String.Empty;
string authString = String.Empty;
string upn = String.Empty;
string clientId = String.Empty;
string clientSecret = ConfigurationManager.AppSetting [“clientSecret”];
foreach (Claim claim in ClaimsPrincipal.Current.Claims)
{
if (claim.Type == “aud”)
{
clientId = claim.Value;
log.Verbose(“Claim: “ + claim.Type + “ Value: “ + claim.Value);
}
}

I have only extracted one claim here, but you will need two more in the same manner (see complete code snippet). The resourceId variable is the resource we want to access once we have a token. Make sure this lines up with what you’re trying to do so you don’t get any errors. You can get a token issued even if you change the resource, but when you try to access said resource it will throw an error back at you.

Setting up the token acquisition is fairly painless:

var authenticationContext = new AuthenticationContext(authString, false);

// Config for OAuth client credentials
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resourceId,clientCred);
string token = authenticationResult.AccessToken;
log.Verbose(token);

This follows the Client Credential flow for OAuth, and this is a silent flow for the user. Everything happens behind the scenes on the back-end. You should never use this in a client app as the clientSecret is not something you want the end-user to have access to.

Watch out for Catwoman running off with your client secret.

Once we have a token in place we can execute a basic API call to the Microsoft Graph and retrieve some basic info about the user:

var outputName = String.Empty;
var responseString = String.Empty;
var phone = String.Empty;

using (var client = new HttpClient())
{
string requestUrl = $”https://graph.microsoft.com/v1.0/users/{upn}";

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue(“Bearer”, token);
log.Verbose(request.ToString());

HttpResponseMessage response = client.SendAsync(request).Result;
responseString = response.Content.ReadAsStringAsync().Result;
var user = JsonConvert.DeserializeObject<AADUser>(responseString);
phone = user.mobilePhone;
outputName = user.displayName;

log.Verbose(responseString);
}

If we try to open the link in a browser we’ll see an output similar to this:

Oh, quick note, the link needs to be appended with &name=upn (where upn is something on the form of user@contos.io, most likely your login name).

The code will compile when you hit “Save”. That is, since I have only shown extracts of the code so far it will probably fail to compile. The complete code needed to run the Function is right below this paragraph.

https://gist.github.com/ahelland/03c1ec02a2305373d4dee5ee3985ed80

There’s probably more exciting things you can think of than retrieving the phone number of a user, but it goes to show what you can do when you bring identity into the picture.

--

--