AnsweredAssumed Answered

Using Secure WFS Layers in AGOL/WAB

Question asked by Caleb1987 on Aug 31, 2017

I don't know if this is the best spot for this question as it has to do with secured WFS services, AGOL, and the esri proxy page, and a custom widget...Anyways, if there is a better spot please feel free to move this post.

 

I am working on an ArcGIS WAB app that needs to reference proprietary lightning strike data which is only provided through the vendor as a secured WFS service. Ideally, this would be a layer inside a WebMap in AGOL that is consumed by WAB. To access the service, I need to provide a Basic Authorization header, which there is no way to specify that in AGOL to my knowledge. So far this is what I have tried:

 

  1. Modified the .NET proxy provided by ESRI to add the needed auth header (see proxy page code at bottom of post)
  2. Tested the proxy to make sure requests were going through with the auth header, this was successful:
  3. Tried to add a "layer from the web" in an AGOL WebMap, with the proxy url in front of the actual WFS url.
  4. This part works, as it will set the request parameter to GetCapabilities, which returns a valid list of layers available in the WFS service. Awesome! 
  5. But here is where things break down...Once I choose a layer from the drop down, AGOL must have some internal validation which strips out my proxy and substitutes it with the default myorg.maps.arcgis.com/sharing/proxy that AGOL uses. Obviously, I get an error here because that proxy doesn't know to add my auth header:

By using Fiddler, I was able to see that ArcGIS Online is no longer using my proxy (the two calls above the highlighted one were used by AGOL to generate the layer list and these calls used the proxy):

 

 

So with all those things failing, because I'm creating a custom widget anyways, I thought the next best thing would be to just create a new WFSLayer() and add my proxy to the esriConfig (wfs layers require a proxy anyways) to add manually from the widget. This also did not work, as for some reason a preflight OPTIONS request is getting sent to the proxy, and it simply does not respond with anything. The weird thing is, if I copy and paste the same request that the JS API makes into the browser window, the request returns back the data (or maybe not so weird since this is a GET request). This is the JS code I'm using:

// set proxy for WFS layer
esriConfig.defaults.io.proxyUrl = 'https://my-domain.com/folder/proxy2/proxy.ashx';

var wfs = new WFSLayer();
wfs.fromJson({
  "url": "https://weather-services.domain.com/basic/wfs_v1/wfs.wsgi",
  "version": "1.0.0",
  "name": "LIGHTNING_STROKES",
  "maxFeatures": 1000
});
this.map.addLayer(wfs);

And here are the modifications I made to the .net proxy.  First, added a custom element to the proxy.config page:

<OGCServer domain="weather-services.domain.com" header="Authorization" headerValue="Basic some_basic_auth_string"/>

Then, in the proxy.ashx I created a new class to handle the OGCResource element in the config:

 

public class OGCResource
{
    string domain;
    string header;
    string headerValue;

    private OGCResource()
    {
    }

    public OGCResource(String domain)
    {
        this.domain = domain;
    }

    [XmlAttribute("domain")]
    public string Domain
    {
        get { return domain; }
        set { domain = value; }
    }

    [XmlAttribute("header")]
    public string Header
    {
        get { return header; }
        set { header = value; }
    }

    [XmlAttribute("headerValue")]
    public string HeaderValue
    {
        get { return headerValue; }
        set { headerValue = value; }
    }
}

 

Then added it to the ProxyConfig object:

 

    // OGC configs
    [XmlElement("OGCServer")]
    public OGCResource OGCServer
    {
        get { return ogcServer; }
        set { ogcServer = value;  }
    }

 

And finally, modified the do HTTPRequest() method to handle the authentication if pointed to the WFS service:

private System.Net.WebResponse doHTTPRequest(string uri, byte[] bytes, string method, string referer, string contentType, System.Net.NetworkCredential credentials = null)
    {
        System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(uri);
        req.ServicePoint.Expect100Continue = false;
        req.Referer = referer;
        req.Method = method;
        ProxyConfig config = ProxyConfig.GetCurrentConfig();

        if (config.ogcServer != null)
        {
            if (uri.Contains(config.ogcServer.Domain)) {
                // add custom auth header for DTN lightning service
                log(TraceLevel.Info, "added custom header to ogc service request");
                req.Headers.Add(config.ogcServer.Header, config.ogcServer.HeaderValue);
            }
        }

        // Use the default system proxy
        req.Proxy = SYSTEM_PROXY;

        if (credentials != null)
            req.Credentials = credentials;

        if (bytes != null && bytes.Length > 0 || method == "POST") {
            req.Method = "POST";
            req.ContentType = string.IsNullOrEmpty(contentType) ? "application/x-www-form-urlencoded" : contentType;
            if (bytes != null && bytes.Length > 0)
                req.ContentLength = bytes.Length;
            using (Stream outputStream = req.GetRequestStream()) {
                outputStream.Write(bytes, 0, bytes.Length);
            }
        }
        return req.GetResponse();
    }

Has anyone ever been able to something like this to work before?  I cannot find any documentation on how to use secured WFS layers in AGOL or the JavaScript API.  The vendor also has the services available in WMS, and are also secured so I have ran into the same issues with that.  Ideally, I just would like to have the layers in a WebMap and somehow tell AGOL to go through my proxy to access the layers.  Is this possible?

Outcomes