Wednesday, August 14, 2024

Azure APIM Policy: Maintain CORS Allowed-Origins per Environment using Named Values

In this post let's see how we can maintain  CORS policys' allowed-origins in Azure API Management (APIM) per environment.

In APIM, the CORS policy looks like this:
<cors allow-credentials="true">
  <allowed-origins>
    <origin>https://localhost:4200</origin>
    <origin>https://sub-1.domain.net</origin>
    <origin>https://sub-2.domain.net</origin>
  </allowed-origins>
  <allowed-methods>
    <method>*</method>
  </allowed-methods>
  <allowed-headers>
    <header>*</header>
  </allowed-headers>
  <expose-headers>
    <header>*</header>
  </expose-headers>
</cors>
Most of the time, allowed-origins will be different in each environment. For example,  in a Production environment, we don't want to allow https://localhost:4200

We can manage these using named values.

Let's add a named value of type Plain as follows.
web-allowed-origins named value
Value is basically comma-separated origins.

And now, we can modify the CORS policy as below:
<cors allow-credentials="true">
  <allowed-origins>
    <origin>@{
      string[] allowedOrigins = "{{web-allowed-origins}}"
          .Replace(" ", string.Empty)
          .Split(',');
      string requestOrigin = context.Request.Headers.GetValueOrDefault("Origin", "");
      bool isAllowed = Array.Exists(allowedOrigins, origin => origin == requestOrigin);
      return isAllowed ? requestOrigin : string.Empty;
    }</origin>
  </allowed-origins>
  <allowed-methods>
    <method>*</method>
  </allowed-methods>
  <allowed-headers>
    <header>*</header>
  </allowed-headers>
  <expose-headers>
    <header>*</header>
  </expose-headers>
</cors>
And now the policy doesn't contain any environment-specific values. In different APIM environments, you can have different values for web-allowed-origins named value.

Imagine, you want to allow https://localhost:4200https://*.domain.net. You can further customize the policy by doing something like the following.
<cors allow-credentials="true">
    <allowed-origins>
      <origin>@{
        string[] allowedOrigins = "{{web-allowed-origins}}"
            .Replace(" ", string.Empty)
            .Split(',');
        string requestOrigin = context.Request.Headers.GetValueOrDefault("Origin", "");
        bool isAllowed = Array.Exists(allowedOrigins, origin =>
        {
            if (origin.Trim() == requestOrigin)
            {
                return true;
            }
             if (origin.Contains("*"))
            {
                string[] originParts = origin.Split('.');
                string[] requestOriginParts = requestOrigin.Split('.');
              
                if (originParts.Length != requestOriginParts.Length)
                {
                    return false;
                }
              
                for (int i = 0; i < originParts.Length; i++)
                {
                    if (originParts[i] == "https://*")
                    {
                       continue;
                    }
              
                    if (originParts[i] != requestOriginParts[i])
                    {
                        return false;
                    }
                }
              
                return true;
            }
              
            return false;
        });
          
        return isAllowed ? requestOrigin : string.Empty;
    }</origin>
    </allowed-origins>
    <allowed-methods>
      <method>*</method>
    </allowed-methods>
    <allowed-headers>
      <header>*</header>
    </allowed-headers>
    <expose-headers>
      <header>*</header>
    </expose-headers>
  </cors>
Hope this helps.

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment