Delivering PDF’s through a secure Azure Web API with AngularJS

Scenario

I have a Web API secured by Azure Active Directory that needs to deliver dynamically generated PDF’s to the user. The front end is an Angular SPA that has been secured using Adaljs. I have done this several times from unsecured API’s and have all the existing code to generate the PDF’s, I just needed to deliver them from the API to the Angular SPA and onto the user. How hard could it be.

Turns out, quite a bit harder than doing it from an unsecured API call.

I’m not going to discuss the finer details of creating a solution for creating a secure Azure Active Directory (AAD) API, there are lots of good articles around for doing this. The client side code example also assumes that you have successfully configured Adal to work with your angular app and that both of these are talking to each other nicely.

The first issue is that I needed to pass over the access token in the header so adding a HTML link directly to the API was out of the question. I could have done (and nearly did) create an MVC controller which would have handled the authentication redirects but creating/configuring an MVC app seemed like massive overkill just to deliver a file.

The Adal-angular libraries will automatically attach the access token to the header for $http calls; the problem with this is the data has to be returned to the AJAX call and handled in a way that the file can be returned to the user to save or view.

There are lots of examples around on the t’interweb regarding getting files from API’s and serving these back to the user in angular using $http calls. The examples out there from the Azure side of things seemed to be all for delivering files from Azure blob storage, returning file data from an API isn’t a particularly hard thing to do. There didn’t seem to be anything that would show an end to end solution, especially nothing using a secure API.

Initially I could get the browser to return a PDF but the content was always blank. I knew the process of creating the PDF’s was fine as when the API was unsecure, the PDF would come down and view OK.

The initial breakthrough was when I found this blog post. It described the scenario I was trying to overcome and the key piece of information in the post alluded to the fact the API had to deliver the content base64 encoded. Originally the PDF data was being passed back as a byte array as I had done previously.

Alas, onto the solution.

Configuring the API

Below is the code for the API that will return the content as a base64 encoded string.

HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.NoContent);

byte[] pdfBytes = <WhateverGeneratesYourPDFContentHere>;
HttpContext.Current.Response.Buffer = true;
result.StatusCode = HttpStatusCode.OK;
result.Content = new StringContent(Convert.ToBase64String(pdfBytes));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "filename.pdf";

return result;

Changing the API to deliver the content as a base 64 encoded string worked, the PDF was generated and visible on the screen correctly. Onto the next part…….

Calling the API from Angular

As stated above there are tons of examples for getting a file via an Ajax call and returning the file to the user, I just needed to piece all these together as my original code didn’t work for IE.
I found some code over on stack overflow that pointed the way to a fix for IE.

Below is the Ajax call to get the PDF data and display it to the user. The code assumes you are using the adal and adal-angular libraries to attach the bearer token to the header.

$http.post(url)
.success(function(data) {
    if (window.navigator.msSaveOrOpenBlob) {
        var blob = b64toBlob(data, "application/pdf");
        // IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
        window.navigator.msSaveBlob(blob, filename);
    } else {
        var anchor = angular.element('<a/>');
        anchor.css({display: 'none'});
        angular.element(document.body).append(anchor);
       anchor.attr({
            href: 'data:application/pdf;base64,' + encodeURI(data),
            target: '_blank',
            download: filename
        })[0].click();         anchor.remove();
    }
}

I haven’t managed to test with all browsers yet, and the solution only works with IE >= 10.

I’d be happy to hear of any more elegant solutions, especially from the client side of things

Advertisements
Tagged with: , , , ,
Posted in AngularJS, Azure, Web API

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: