How can I allow multiple domains in a .Net Web API with OAuth token authentication using CORS?

Viewed 130

We have a .Net Framework Web API, with Token based OAuth authentication, and are trying to make a call to it via an Exchange HTML Add-In. I wish to allow access to several domains, as we may be using several different apps to access it, but we do not wish to allow general (*) access, as it is a proprietary web API, so there is no need for it to be accessed beyond known domains.

I have tried the following in order to satisfy the pre-flight:

  • Add the Access-Control-Allow-Origin headers with multiple domains via <system.webServer> - this returns a "header contains multiple values" CORS error when including multiple domains
  • Adding the Access-Control-Allow-Origin headers with multiple domains via a PreflightRequestsHandler : Delegating Handler - same result

If I set these up with one domain, and used the config.EnableCors with an EnableCorsAttribute with the domains, it would add those on to the headers and give an error with redundant domains.

How can I set up my Web API with OAuth and CORS settings for multiple domains?

2 Answers

You can add the header "Access-Control-Allow-Origin" in the response of authorized sites in Global.asax file

using System.Linq;
        
private readonly string[] authorizedSites = new string[]
{
  "https://site1.com",
  "https://site2.com"
};

private void SetAccessControlAllowOrigin() 
{
  string origin = HttpContext.Current.Request.Headers.Get("Origin");

  if (authorizedSites.Contains(origin)) 
      HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", origin);
}

protected void Application_BeginRequest() 
{
  SetAccessControlAllowOrigin();
}

Found the following from Oscar Garcia (@ozkary) at https://www.ozkary.com/2016/04/web-api-owin-cors-handling-no-access.html, implemented it and it worked perfectly! Added to AppOAuthProvider which Microsoft had set up on project creation:

 /// <summary>
    /// match endpoint is called before Validate Client Authentication. we need
    /// to allow the clients based on domain to enable requests
    /// the header
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override Task MatchEndpoint(OAuthMatchEndpointContext context)
    {
        SetCORSPolicy(context.OwinContext);
        if (context.Request.Method == "OPTIONS")   
        {               
            context.RequestCompleted();
            return Task.FromResult(0);
        }

        return base.MatchEndpoint(context);
    }
   
  
    /// <summary>
    /// add the allow-origin header only if the origin domain is found on the     
    /// allowedOrigin list
    /// </summary>
    /// <param name="context"></param>
    private void SetCORSPolicy(IOwinContext context)
    {
        string allowedUrls = ConfigurationManager.AppSettings["allowedOrigins"];

        if (!String.IsNullOrWhiteSpace(allowedUrls))
        {
            var list = allowedUrls.Split(',');
            if (list.Length > 0)
            {

                string origin = context.Request.Headers.Get("Origin");
                var found = list.Where(item => item == origin).Any();
                if (found){
                    context.Response.Headers.Add("Access-Control-Allow-Origin",
                                                 new string[] { origin });
                }                   
            }
            
        }
        context.Response.Headers.Add("Access-Control-Allow-Headers", 
                               new string[] {"Authorization", "Content-Type" });
        context.Response.Headers.Add("Access-Control-Allow-Methods", 
                               new string[] {"OPTIONS", "POST" });

    }            
Related