Quantcast
Viewing all articles
Browse latest Browse all 53

Add authentication to an Azure Static Web App’s API

At my current client we are writing a Blazor WASM app which is deployed as a Static Web App. The backend is an Azure Function which is deployed as a “Bring your own” function, however I think this still applies if the backend is a Managed Function.

The static web app is hosted in Azure at https://calm-ocean.33.azurestaticapps.net/. All of it’s pages are protected with OAuth on Microsoft Entra ID (formerly Azure AD).

The API is an Azure function with an HTTP endpoint at say https://my-func.azurewebsites.net/api/blogs.

At first this endpoint had no authentication, meaning it can be called directly and return a 200.

I then linked the Static Web App to the Azure function, which adds an “Azure Static Web Apps” Identity provider to the Azure Function, which means only the Static Web app can call the Function.

Image may be NSFW.
Clik here to view.
Azure portal screenshot

After linking, if I try call my function endpoint at https://my-func.azurewebsites.net/api/blogs it now returns a 400 (it should probably return a 401).

The security hole

The Static Web App (https://calm-ocean.33.azurestaticapps.net/) proxies any calls to the function’s endpoints at https://calm-ocean.33.azurestaticapps.net/api.

This means that by default, unauthenticated users can still call the API but via the Static Web App, i.e. https://calm-ocean.33.azurestaticapps.net/api/blogs – even though all other pages are protected by OAuth! Which is a big security hole Image may be NSFW.
Clik here to view.
😱

The fix

The fix is quite simple. Specify that all routes should be locked down in the staticwebapp.config.json file (except our Blazor authentication pages):

{
 "routes": [
    {
      // Our Blazor pages have authentication via the [Authorize] attribute (in _Imports.razor).
      // Blazor's auth routes are at authentication/*, so allow anonymous access to them.
      // FYI, Azure Static Web App's built-in auth is at .auth/
      "route": "authentication/*",
      "allowedRoles": [ "anonymous" ]
    },
    {
      // Our API is an Azure function which is proxied on the "api" route. We don't want to allow anonymous access! We need to specify that calls to api/* are authenticated.
      // Let's lock down the whole site, so that requests to any page will need SWA auth, which is then passed on to our api/* calls.
      "route": "/*",
      "allowedRoles": [ "authenticated" ]
    }
  ],
  "responseOverrides": {
    "401": {
      "statusCode": 302,
      "redirect": "/.auth/login/aad"
    }
  }
}

Once deployed to Azure, if you try call the API directly (i.e. in an incognito browser window), you’ll be redirected to login.
One obvious gotcha is that this won’t work when you’re debugging locally, because your local function will be on a completely different port and isn’t proxied.

Pro tip: if you’re having trouble getting this to work, you can navigate to /.auth/me on your Static Web App to see the information about the currently logged in user. If you don’t see anything then you can sign in at /.auth/login/aad. These .auth routes are built-in to Azure Static Web Apps.

PS. after figuring all this out, I found this page which is a thorough treatment of how to combine Azure Static Web App’s authentication with Blazor WASM. Personally I haven’t needed to go that far myself – I’m so far only using the guides I’ve linked to above to do Blazor authentication.


Viewing all articles
Browse latest Browse all 53

Trending Articles