Azure Functions and App Service Authentication

Azure App Service has a handy authentication integration that takes away the work of integrating with various identity providers (currently: Azure Active Directory, Facebook, Google, Twitter and Microsoft Accounts). Azure Functions are built on the same underlying core components as Azure App Service and in this post we will show how to integrate http-based Azure Functions with Azure App Service Authentication (aka EasyAuth).

When working with a Web App, you can easily set up EasyAuth to redirect unauthenticated users to the identity provider of your choice and thanks to the magic of the implicit flow (i.e. various browser redirections) the user ends up returning to your web app as an authenticated user with a cookie from EasyAuth. With this server-directed flow EasyAuth really is particularly easy! For APIs (either hosted as Web Apps, or in Azure Functions) you need to follow a slightly different flow where the initial authentication is driven by the client, and there are some gaps in the documentation right now, so hopefully this blog post can help you through the steps to make this work until the docs are updated :-)

Before we start digging in to how it all works it is worth mentioning that there are various SDKs for Mobile Apps (another part of App Service) which have support for EasyAuth amongst other things. These cover JavaScript, Android, iOS etc but I want to keep this post as general as possible so won’t be using an SDK here. (The sample code does have an example of using the Mobile Apps JavaScript SDK if you’re interested in seeing how that helps!)

The Goal

So, what exactly is my aim? I want to be able to secure an Azure Function using providers such as Facebook, Google etc. The customer that I was working with on this plan to build out a Unity Application, but for ease of putting the sample together (based on my background) I created a simple web client to test the function.

The Code

If you want to skip ahead to the end, the code for this article is up on Github. There is an ARM template with some instructions for how you can deploy it to test it. The template will deploy the Azure Function, set up EasyAuth on the Azure Function, deploy the client, and the set up the configuration that the web app needs. At the time of writing I also have a live demo if you want to just see the result in action.

Creating a Function with EasyAuth

The first thing we need is an Azure Function. I created this in Visual Studio in the same way as is described in these instructions.

Once you have deployed the function, we need to configure EasyAuth. This can be done in the portal by navigating to your function, and then choosing Authentication/Authorization under Platform Features

image

From there you can enable App Service Authentication, i.e. EasyAuth, and configure the providers that you want to:

clip_image001[6]

To configure a provider you will need to enter the relevant details for that provider. When you click on a provider you will get a link to the documentation for setting up that provider and getting the information that you need.

For Facebook, at this point I have the following in the Facebook portal (I'll come back and add more settings later, once I have the client web app):

clip_image001[8]

And then I enter these settings this in the EasyAuth configuration:

clip_image001[10]

Once you've done this then we have an Azure Function ready for EasyAuth, but we need to call it passing a token…

Creating a Client

For this post let's create a web app - in the sample project I deploy this as an Azure Web App, but you can host it wherever, or create a different type of client app (iOS, Android, Windows, Unity, ….). For the rest of the post I'm going to talk about a web client, but I'll do as much as possible in the browser (rather than the web server) so that it is easy to see how to port to another client type.

For the actual Facebook/Google sign-in, we will use the JavaScript SDKs. Once the Web App is deployed we need to revisit the Facebook/Google developer portals to configure them to accept logins from our Web App

Back in the Facebook app settings, I can now set the App Domain and add a Website platform:

image

In case you're interested, this is what the Google app settings look like:

image

Note the URLs in each case for easyauthtestweb.azurewebsites.net (which was a disposable test site I created for taking screenshots). And yes, I've reset the keys for the apps and will most likely delete them soon!

Because I opted for a web app as the client there is one final tweak to make is to enable CORS on the Function so that our web app can call the Function. Again, under Platform Features for the Function we can select CORS

image

And then enter the URL to our web app (without a trailing slash) clip_image001[12]

Logging In

Now that we have a web app, we need to put some content in it and the sample code can be found here.

In the sample code, I've used ASP.NET Core Razor Pages. These are server-rendered HTML pages that can contain server-side C# code. I've taken advantage of this by injecting the IConfiguration dependency. This is part of the ASP.NET Core configuration system and pulls in configuration values from various sources including environment variables. This is particularly convenient as App Settings in Azure Web Apps are exposed as environment variables, so this gives a clean way to pick up the config.

The sample code also has some @if sections to only include the Facebook/Google scripts if we have the IDs for them set. This allows the same code to work for either or both provider, but you don't have to do that for your client!

The documentation for Facebook and Google both show how to use their SDKs to obtain a token for the user. The key part from there is knowing how to use that to call the Azure Function via EasyAuth, and the steps for that are outlined below.

For Facebook we get an access token, and we need to make an HTTP POST to <functionapp>/.auth/login/facebook with the application/json body { 'access_token' : <accesstoken> }

For Google we get an id token, and we need to make an HTTP POST to <functionapp>/.auth/login/google with the application/json body { 'id_token': <idtoken> }

These calls translate the provider token into an EasyAuth token, which is returned in the authenticationToken property of the returned JSON object.

Finally, once we have the EasyAuth token we can call the Azure Function passing the EasyAuth token in the X-ZUMO-AUTH header on the HTTP request.

These steps are all implemented with vanilla JavaScript in the sample page so that they can easily be translated to other client types.

Working with Authentication in the Function

We have the Function App configured for EasyAuth, and the client sending up the correct token which leaves one final question: how do we access the identity in the Function App? The approach you take here depends on what you want to do.

If you simply want to know whether the request is for an authenticated user then you can test Thread.CurrentPrincipal.Identity.IsAuthenticated. This will be true if a valid token was included on the incoming request.

Alternatively, if you want to get a user identifier that you can use to store information for the authenticated user then you can cast Thread.CurrentPrincipal to a ClaimsPrincipal, and then retrieve the claim of type "stable_sid". The combination of provider (supplied in the "https://schemas.microsoft.com/identity/claims/identityprovider" claim) and the stable_sid gives you a unique user id. Also, while it may be tempting to use other properties (see below) as the user identifier, you need to be careful which properties you pick, for example a user can change the email address associated with their Facebook account. For further reading, see Understanding User Ids.

Finally, if you want to get profile information from the provider then you get retrieve that via EasyAuth. To do this we need to call the .auth/me endpoint passing the X-ZUMO-AUTH header along. I've packaged this in an extension method on HttpRequestMessage called GetAuthInfoAsync for ease of use.

The raw response from .auth/me is of the form:

[{
"provider_name":"<provider>",
"user_id": "<user_id>",
"user_claims":[{"typ": "<claim-type>","val": "<claim-value>"}, ...],
"access_token":"<access_token>",
"access_token_secret":"<access_token_secret>",
"authentication_token":"<authentication_token>",
"expires_on":"<iso8601_datetime>",
"id_token":"<id_token>",
"refresh_token":"<refresh_token>"
}]

The extension method parses this into an instance of the AuthInfo class for easier use.

In the Function project there are a number of Functions that demonstrate accessing the data for each of the scenarios above: https://github.com/stuartleeks/AzureFunctionsEasyAuth/blob/master/src/FunctionWithAuth/AuthFunctions.cs

Putting it All Together

When we put it all together we have an Azure Function that can support logins from a variety of methods via configuration. The beauty of EasyAuth in Azure Functions/AppService is that we don't have to worry about integrating with each provider, we can just call the standard .auth/me endpoint to get the profile information that the provider supplied.

The code for this article is on github here, and there are instructions for deploying this to your Azure subscription (if you don't have one then get a free subscription here. When you deploy the template it will create an Azure Function (with a whole stack of free executions each month), an Azure Web App on the Free Plan and a storage account (for Function logging).

In short, testing this will hardly cost you anything. So what are you waiting for? Give it a try!

Further Reading

If you want to find out more about EasyAuth and Azure Functions then here are some recommended articles: