SPA with msal-browser and AD protected Azure functions

Marc Bastiaansen
3 min readOct 15, 2020

Recently, I built a SPA in React that called a number of APIs running on Azure as functions. I wanted to protect the SPA with an AD login, requiring people to have an account on our AD tenant, and I also wanted the SPA to use a token to access the APIs. It sounded pretty straight forward, but it took many days to get it all up and running. There are many docs and samples out there that are all slightly different and none of them worked for me out of the box. So this blog post is for anyone trying to get this working at this moment in time (towards end of 2020) as probably things will change again at AD/Microsoft end making this blog irrelevant.

I ended up using the sample MSAL.js 2.x Vanilla JavaScript Single-page Application as a kick starter. Once I created an Azure AD app and configured the parameters in the sample code this worked quite easily:

auth: {
clientId: "{appid}",
authority: 'https://login.microsoftonline.com/{tenantid}',
redirectUri: "https://{api-url}.azurewebsites.net/",
},

Main difference for me was that I wanted a single tenant app, which required the authority to be set to contain the {tenantid} instead of the default (multi tenant) https://login.microsoftonline.com/common/

The sample however only access the Graph api, and not any custom built function api. However, it’s quite easy to get Azure to create an AD app for you that is wired up to protect the api, see the official documentation (for example). I created a new app with Express settings. I then switched my sample SPA code to use the new clientid for this new app (instead of the one I manually created which I now deleted).

This new app has already created a “user_impersonation” scope under the “Expose an API” for you. In the SPA, when requesting an access token, I was sure to add this scope to the request:

export const apiAccessTokenRequest = {
scopes: ['https://{api-url}.azurewebsites.net/user_impersonation']
}

So far so “easy”. However, while getting an access token worked fine, calling the api with the Authorization header set (bearer) kept returning 401 Unauthorised. That led to a trip down the rabbit hole of several days trying to figure out why this was not working. Finally, I found the problem (for my scenario).

If you go back to the function app, the Authentication/Authorization tab, and click the Active Directory Authentication, there is also an Advanced tab next to the Express tab (which I had used to create the app). This is filled in by default with the correct Client ID (of the app created) and has the “Allowed Token Audiences” already setup. However, the issuer url by default was set to https://sts.windows.net/{tenantid}.

This seems correct, especially if you take the access token and decode it (using any online decoder) and verify that the token has “iss” field set to the same url. However, for me, I replaced the value with https://login.microsoftonline.com/{tenantid} (to match the authority configured in the SPA). Suddenly it all worked!

To be sure, the official documentation linked above does mention something along these lines in the advanced, manual setup section. However, the express create app option obviously doesn’t set it correctly for this scenario.

Hope it helps you and saves you a bit of time getting this working.

--

--