Add a proxy for on-cloud services that don’t support security communication

In this article, we describe how to add a proxy for those public APIs that don’t support secure communication and discuss the procedure for setting up the proxy.

Many on-cloud services have open APIs. Developers use open APIs to build mobile applications. We can also build apps for Office using those web-based open APIs. Essentially apps for Office are web-hosted sites with HTML/JavaScript/CSS embedded, as an application, in an Office client or OWA instance.

Applications often consume private data. To prevent unauthorized data access, apps for Office only allow secure communication between a client and a server. This can help raise the bar for public APIs to support secure communication. Unfortunately, not all public APIs support secure web protocols. In this post, we describe a workaround to the secure communication requirement by adding a proxy.

Important: This technique is only recommended if you are certain the information traveling from/to your proxy is not sensitive in nature (e.g. to retrieve public images, or public information like stock quotes). Never send private customer information via unsecure channels.

A proxy as a protocol auto-adaptor between the client and the server

In the section, we describe the traditional secure communication and how to add a proxy for those web services with only HTTP connections.

As shown in figure 1, an app for Office typically uses a secure protocol to communicate with on-cloud services. The app invokes an HTTPs request in JavaScript using, for example, JSONP, to an external service to collect or manipulate data.

Figure 1. An app communicates with service with an HTTPS connection
Figure 1. An app communicates with service with an HTTPS connection

For those web services that only accept HTTP connections, we use a proxy in the middle of an app for Office and the external web service, as shown in figure 2.

Figure 2. An app communicates with service that only accepts HTTP connections
Figure 2. An app communicates with service that only accepts HTTP connections

The HTTP communication dataflow through a proxy is shown in figure 3. First, the proxy takes an HTTPS request from an app for Office and then re-boxes the request to forward it to the remote service in the way that the remote server would like to take. The remote server returns any service or data back to the proxy. The proxy un-boxes the service/data and re-boxes it to the app for Office.

Figure 3. Sequence of a data flow
Figure 3. Sequence of a data flow

Create your own proxy

In this section, we show how to create a proxy using ASP.NET HttpHandler. In this example, to keep it simple and avoid the same-origin policy issue, we deploy it on the same server that hosts the app. The proxy can do the following:

To keep this example simple, cookies and HTTP headers are ignored.

Create an empty ASP.NET web application

First, we create an empty APS.NET web application. In Visual Studio, choose File > New > Project… > Templates > Visual C# > Web. From .NET Framework 4.5, choose ASP.NET Empty Web Application, as shown in figure 4.

Figure 4. Create an empty web application
Figure 4. Create an empty web application

Create a new generic http handler

Now, we are ready to create a generic HTTP handler in the empty web application. In Visual Studio, open the web application we created in the previous subsection. From Solution Explorer, choose the web application and choose Add > Generic Handler. Name the generic handler “ForwordTo”.

Figure 5. Create a generic handler for an empty web application
Figure 5. Create a generic handler for an empty web application

Creating a generic handler creates a code template that includes one important method ProcessRequest we will use later.

 public class ForwardTo : IHttpHandler
{
 public void ProcessRequest(HttpContext context)
 {
 context.Response.ContentType = "text/plain";
 context.Response.Write("Hello World");
}

 public bool IsReusable
 {
 get
 {
 return false;
 }
 }
}

Un-box and re-box the original request

This is an important step that creates a new HTTP request on the server side, on behalf of the client (the app) that will communicate with the original remote server. We create a simple function named reboxRequest. The function feeds with the original HTTPS request, and gives a boxed new HTTP request back, as shown in the following code sample.

 private HttpWebRequest reboxRequest(HttpRequest originalRequest)
{
    UriBuilder forwordUrl = 
        new UriBuilder(originalRequest.QueryString["target"]);
    HttpWebRequest reboxedRequest = null;

    if (originalRequest.HttpMethod == WebRequestMethods.Http.Get)
    {
        string query = forwordUrl.Query;

        reboxedRequest = WebRequest.Create(forwordUrl.Uri) as HttpWebRequest;
        reboxedRequest.Method = WebRequestMethods.Http.Get;
    }
    else if (originalRequest.HttpMethod == WebRequestMethods.Http.Post)
    {
        using (StreamReader requestReader = 
            new StreamReader(originalRequest.InputStream))
        {
            string body = requestReader.ReadToEnd();

            reboxedRequest = 
                WebRequest.Create(forwordUrl.Uri) as HttpWebRequest;
            reboxedRequest.Method = WebRequestMethods.Http.Post;
            reboxedRequest.ContentType = "application/x-www-form-urlencoded";

            using (StreamWriter bodyWriter = 
                new StreamWriter(reboxedRequest.GetRequestStream()))
            {
                bodyWriter.Write(body);
                bodyWriter.Close();
            }

            requestReader.Close();
        }
    }
    else
    {
        // Ignore others
    }

    return reboxedRequest;
}

Forward request and response

Now, change the default behavior of the ProcessRequest method with our own logic. A typical proxy does the following:

  1. Acts as the server endpoint to accept an HTTPS request from the app for Office.
  2. Acts as a client to issue a new HttpRequest to the real target server.
  3. Copies what the real target server returns and writes back to the response of the original request from the app for Office.
 public void ProcessRequest(HttpContext context)
{
    HttpWebRequest reboxedRequest = this.reboxRequest(context.Request);
    HttpWebResponse originalResponse = 
        reboxedRequest.GetResponse() as HttpWebResponse;

byte[] buffer = new byte[1024];
int count = 0;

    while (true)
    {
        count = 
        originalResponse.GetResponseStream().Read(buffer, 0, buffer.Length);

        if (count > 0)
        {
            context.Response.OutputStream.Write(buffer, 0, count);
        }
        else
        {
            break;
        }
    }

    context.Response.ContentType = originalResponse.ContentType;
    context.Response.StatusCode = (int)originalResponse.StatusCode;
    context.Response.TrySkipIisCustomErrors = true;
    context.Response.OutputStream.Flush();
}

Enable the handler

Now that the web application is created, the generic handler is added, and the new logic is implemented. The last step is to add the handler we just created into the Web.config file, and set the matching pattern to “forward”. We can then use https://MyOfficeAppProxy/forward to access our proxy server.

 <system.webServer>
 <handlers>
    <!-- handlers -->
   <add 
    name="MyOfficeAppProxy" 
    verb="*" 
    path="forward" 
    type="MyOfficeAppProxy.forwardTo"/>
 </handlers>
</system.webServer>

Test the proxy

Once we create our own proxy, we can start debugging it in Visual Studio. Start the default browser with the URL like https://localhost:58902/. We can make a quick try from the browser to test the proxy.

  1. Change the URL to https://localhost:58092/forward. The link should follow a parameter “target” telling us the real target we want the proxy to help us issue request to. It could be a RESTful API; here we use a static image as an example. So, the complete URL in the browser should be: https://localhost:58902/forward?target=http%3a%2f%2fi.msdn.microsoft.com%2fAreas%2fEpx%2fThemes%2fOffice%2fContent%2fImages%2fLogo_Office.png
  2. After choosing the “go to” button in the browser, the image is displayed which shows exactly what we want the proxy to be.

Figure 6. The target address
Figure 6. The target address

References

Attribute

This post was written by ecoSystem team SDET Li Jiang and content developer Tony Liu.