This describes how to manage Azure App Service authentication and authorization in the case of a multi-tier application, with a single-page app, and an API in a separate app service ; using “easy-auth” (i.e. delegating the auth part to the app service infrastructure).

The Azure documentation does a pretty good job explaining the steps to configure your applications, but it somewhat lacks a more “architectural” view, that I’m hoping to provide here.

Globally, the scenario we want to cover looks like this:

A user gets a single-page app served from an app-service, configured with AAD authentication. The single-page app then interacts with the API in a Function App, also configured with AAD authentication, using user impersonation.

Globally, this scenario can be handled two ways:

  1. Tunnel all communications through the webapp and handle authentication in the webapp
  2. Have the single page app retrieve an access_token from the webapp using an API endpoint, then use the token to invoke the API directly.

There’s very little difference between those two, some may appear based on your own context.

This solution will see both the front-end and the API registered in AAD as separate apps. The front-end needs to have a “delegated permission” added to request an access_token to the API1 to impersonate the user. The beauty of it is that it’s also compatible with Application Roles.

1. Proxy API calls through the web app

The advantage of this solution is that it’s relatively simple, doesn’t require to handle any authentication logic on the single page app side, and doesn’t have access_tokens ex-filtering the backend2.

It looks like this:

The inconvenient is that it adds a layer in the API calls, which may introduce more maintenance, more useless network traffic, and also reduces the capacity to debug calls3. This also means that the invoking of the API is separate from handling the authorization, so effectively you’re creating an API client split in two components. In that, I consider it slightly less elegant, but it’s personal. It’s also limiting potential reuse of a Javascript client.

The solution goes as follows:

  • User requests the single page app
  • App Services get the user authenticated, a cookie is set transparently by the infrastructure
  • Web app calls an API proxy endpoint on the webapp (you have to build it!) just by using a regular AJAX framework (recommending Vanilla.js as usual), auth is handled transparently thanks to the Cookie.
  • API proxy endpoint receives the call from the front-end, the App Service infrastructure adds id_token and access_token, amongst other things. It then takes the access_token, and fits it into the authorization header (don’t forget to prefix it with Bearer, since this is a bearer token), then forwards the API call.
  • Function App authenticates the call through the access_token, and enriches it with id_token and access_token transparently. You have access to all and any claims and roles asked by the API application.

2. API endpoint to retrieve access_token

The advantage of this solution is that it provides a fairly elegant way of handling the API call (to my eyes). The single page app is no different than other applications in its usage of the API: it does require for the client to handle authorization, which also means that you can re-distribute any client you are building. The downside is that it requires handling the temporary storage of the token on the client side, and the refreshing of the access_token every so often.

  • User requests the single page app
  • App Services get the user authenticated, a cookie is set transparently by the infrastructure
  • Web app calls an API endpoint on the webapp that gives it an access_token (you have to build it!) auth to that endpoint is handled transparently thanks to the Cookie. That endpoint in node using Express is:
  app.get("/api/access_token", (req, res) => res.end(req.headers("x-ms-token-aad-access-token")));
  • App stored that token locally, be it in a cookie, local storage, or a global variable if you’re into that.
  • API is invoked directly. The single page app takes the access_token, and fits it into the authorization header.
  • Function App authenticates the call similarly to the other solution.

Notes

  1. Shaun did a great explanation of the difference between an id_token and an access_token. The difference matters a lot

  2. Although the AAD authentication cookie is still ex-filtered, which can then be used to call the API through the webapp, so the security advantage is somewhat equivalent. 

  3. It’s slightly more convenient to use an access_token and call the API directly.