When you have an SPA (Single Page App), all your code is being run inside of your browser. This means that, from a network perspective, you’ll be talking to the APIs directly. It’s often (rightfully) said that SPAs are an untrusted client, where a typical server-side app is seen as a trusted client. Why is an SPA seen as untrusted? Because from the publisher side (the one providing the service/app), you do not control the device running the code. So this has a huge effect on the security risks involved and how you should mitigate them.
One of those mitigations is “CORS” ;
Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources (e.g. fonts) on a web page to be requested from another domain outside the domain from which the first resource was served. A web page may freely embed cross-origin images, stylesheets, scripts, iframes, and videos. Certain “cross-domain” requests, notably Ajax requests, are forbidden by default by the same-origin security policy. (Source : Wikipedia)
With CORS, the request will indicate from which domain the calls would originate (and what actions / headers it would like to do). Therefore, the backend can check if the call is warranted or not…
CORS Preflight Check
Ever heard of a CORS preflight check? If you’re keep on reading, I can safely say ; “Probably not”? 😉
When performing certain types of cross-domain Ajax requests, modern browsers that support CORS will insert an extra “preflight” request to determine whether they have permission to perform the action.
What happens in reality? First an HTTP call with the verb/method “OPTIONS” is done.
Here you’ll see that we’ll first indicate what method (POST) we would like to use, what our Origin (http://localhost:8080) is and what headers we would like to send (access-control-allow-origin & ocp-apim-subscription-key). In our response, we’ll see that we allow all origines (*), that the POST method is allowed and the same for both of our headers. As we received the status code “200”, we know we can now safely proceed with our API call!
Test Flow of the Day
What does that mean when we’ve published an API (backed by Azure Functions) via Azure API Management… Then we have two area’s where the CORS check will take place ;
So you’ll have two implementation methods how to tackle this ;
- Configure an “OPTIONS” endpoint on the API Management that links back directly to the Azure Function
- Configure CORS on the API Management to shortcut the flow and handle those calls without sending them to the Azure Function
Option A) Let APIM handle it
In this option, we’ll just say “APIM, please handle this for me”. You can do this by setting a policy on your Inbound flow.
As mentioned, here you can specify the allowed origin/method/headers ;
And once configured, APIM will respond to the “OPTIONS” request for you.
Option B) Pass through by APIM, and let Azure Functions handle it.
The first part is to set up APIM to listen to the OPTIONS method and send it to your function… You can do this like you would configure any other API endpoint.
So now, the OPTIONS request will land at the backend (being your Azure Function). Therefore we’ll need to configure CORS on our Azure Function. Go to the platform overview of your function, and navigate to “CORS Rules” ;
Here you can define the origin ;
In regards to the headers, to my knowledge you can’t specify that… Though the methods are based on the ones you’ve selected for your function(s) ;
Having a flow with multiple components interlinked doesn’t make things like CORS easier. Though be aware that you can tackle this quite easy if you’re familiar with the capabilities of both Azure API Management & Azure Functions. In the example I’ve been very “open” (read: unsecure) in terms of allowed origins. For production usage, be sure to harden this and only allow the origins you trust. Next to that, always open up the developer tools of your favorite browsers and look at the requests that happen. This will surely give you more than a hint about what is happening. 😉
One thought on “When your Single Page App needs CORS and meets Azure API Management with a Function Backend”
Wanted to make a note for others that land here. It’s in the image inside this article, but not really mentioned in the Azure, docs… Be sure that your inbound rules includes the section. Mine looks just like the image…
Was trying all kinds of things before setting that and it worked. Thanks kvaes!